diff --git a/.gitignore b/.gitignore index fc9333371..3c2f36a79 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ *.class # Default data file -addressbook.txt +Rms.txt # Package Files # *.jar @@ -22,3 +22,4 @@ publish.sh .gradle/ build/ +/config.json diff --git a/.travis.yml b/.travis.yml index 89c29645d..6df75d07f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ matrix: - jdk: oraclejdk9 script: >- - ./gradlew test asciidoctor + ./gradlew clean checkstyleMain checkstyleTest test coverage coveralls asciidoctor deploy: skip_cleanup: true diff --git a/README.adoc b/README.adoc index 5d6ac0c7e..ca1878ca6 100644 --- a/README.adoc +++ b/README.adoc @@ -1,41 +1,29 @@ -= AddressBook (Level 3) ifdef::env-github,env-browser[:relfileprefix: docs/] ifdef::env-github,env-browser[:imagesDir: docs/images] -https://travis-ci.org/se-edu/addressbook-level3[image:https://travis-ci.org/se-edu/addressbook-level3.svg?branch=master[Build Status]] -https://www.codacy.com/app/se-edu/addressbook-level3?utm_source=github.com&utm_medium=referral&utm_content=se-edu/addressbook-level3&utm_campaign=Badge_Grade[image:https://api.codacy.com/project/badge/Grade/d4a0954383444a8db8cb26e5f5b7302c[Codacy Badge]] +https://travis-ci.org/CS2113-AY1819S1-F09-2/main[image:https://travis-ci.org/CS2113-AY1819S1-F09-2/main.svg?branch=master[Build Status]] +https://app.codacy.com/project/CS2113-AY1819S1-F09-2/main/dashboard[image:https://api.codacy.com/project/badge/Grade/e4ce38384d584d61bc91c56dcdacbd70["Codacy code quality", link="https://www.codacy.com/app/CS2113-AY1819S1-F09-2/main?utm_source=github.com&utm_medium=referral&utm_content=CS2113-AY1819S1-F09-2/main&utm_campaign=Badge_Grade"]] +https://coveralls.io/github/CS2113-AY1819S1-F09-2/main?branch=master[image:https://coveralls.io/repos/github/CS2113-AY1819S1-F09-2/main/badge.svg?branch=master[Coverage Status]] -image::Ui.png[] - -* This is a CLI (Command Line Interface) Address Book application *written in OOP fashion*. It has a very basic GUI. -* It is a Java sample application intended for students learning Software Engineering while using Java as -the main programming language. -* It provides a *reasonably well-written* code example that is *significantly bigger* than what students -usually write in data structure modules. - -*What's different from level 2* +image::homepage.png[] -* A simple GUI added to replace the Text UI. -* A `Logic` class added together with a `LogicTest` class. -* Appendices added to <> += https://cs2113-ay1819s1-f09-2.github.io/main/[Welcome to RMS Homepage] +Restaurant Management System (RMS) promises to increase the quality of your service and enhance your work experience. The Command Line Interface makes RMS highly efficient as it reduces the number of steps taken to perform a task to just one (or two). It is your one-stop solution that can be used for serving customers, updating menu as well as storing and tracking important data. In a nutshell, RMS guarantees to be a faster, safer and smarter choice of management for any restaurants and fast food chains. -*Useful Links* +image::Ui.png[] -* <> -* <> -* <> +* This is a Desktop Application for Restaurant Management System. It is making use of Command Line Interface +* Our primary target audience for RMS are *Restaurants* and *Fast Food Chains* +* RMS contains support for Build Automation using Gradle and for Continuous Integration using Travis CI, with coverage checks using Coveralls. -== Contributors +== Who we are -The full list of contributors for se-edu can be found https://se-edu.github.io/Team.html[here]. +* Restaurant Management System was developed by CS2113-AY1819S1-F09-2 team. We are a team based in the School of Computing, National University of Singapore. For further information, visit <> page. == Acknowledgements -Some parts of this sample application was inspired by the excellent +* Restaurant Management System was developed based on the souce code of an +https://github.com/se-edu/addressbook-level3[Address Book] created by +SE-EDU initiative. +* Some parts of this sample application was inspired by the excellent http://code.makery.ch/library/javafx-8-tutorial/[Java FX tutorial] by Marco Jakob - -== Contact Us - -* *Bug reports, Suggestions* : Post in our https://github.com/se-edu/addressbook-level3/issues[issue tracker] -if you noticed bugs or have suggestions on how to improve. -* *Contributing* : We welcome pull requests. Follow the process described https://github.com/oss-generic/process[here] diff --git a/_reposense/config.json b/_reposense/config.json new file mode 100644 index 000000000..e3d2cd960 --- /dev/null +++ b/_reposense/config.json @@ -0,0 +1,30 @@ +{ + "authors": + [ + { + "githubId": "AngWM", + "displayName": "ANG...ING", + "authorNames": ["AngWM", "Wei Ming"] + }, + { + "githubId": "kianhong95", + "displayName": "LIM...ONG", + "authorNames": ["kianhong95", "Lim Kian Hong"] + }, + { + "githubId": "px1099", + "displayName": "PHA...INH", + "authorNames": ["px1099", "Pham Quang Minh"] + }, + { + "githubId": "SalsabilTasnia", + "displayName": "SAL... MD", + "authorNames": ["SalsabilTasnia"] + }, + { + "githubId": "kangmingtay", + "displayName": "TAY...ING", + "authorNames": ["kangmingtay"] + } + ] +} diff --git a/build.gradle b/build.gradle index 0a4290207..10fabeda8 100644 --- a/build.gradle +++ b/build.gradle @@ -6,9 +6,17 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { id 'java' + id 'jacoco' + id 'checkstyle' + id 'com.github.kt3k.coveralls' version '2.4.0' id 'org.asciidoctor.convert' version '1.5.6' + id 'com.github.johnrengelman.shadow' version '2.0.3' + id 'application' } +// Specifies the entry point of the application +mainClassName = 'seedu.addressbook.Main' + sourceCompatibility = JavaVersion.VERSION_1_9 targetCompatibility = JavaVersion.VERSION_1_9 @@ -16,6 +24,18 @@ repositories { mavenCentral() } +checkstyle { + toolVersion = '8.1' +} + +jacocoTestReport { + reports { + xml.enabled false + csv.enabled false + html.destination file("${buildDir}/jacocoHtml") + } +} + sourceSets { main { java { @@ -41,10 +61,41 @@ dependencies { testImplementation group: 'junit', name: 'junit', version: '4.12' } +shadowJar { + archiveName = 'addressbook.jar' + + destinationDir = file("${buildDir}/jar/") +} + task wrapper(type: Wrapper) { gradleVersion = '4.8.1' } +task coverage(type: JacocoReport) { + sourceDirectories = files(sourceSets.main.allSource.srcDirs) + classDirectories = files(sourceSets.main.output) + executionData = files(jacocoTestReport.executionData) + afterEvaluate { + classDirectories = files(classDirectories.files.collect { + fileTree(dir: it, exclude: ['**/*.jar']) + }) + } + reports { + html.enabled = true + xml.enabled = true + } +} + +coveralls { + sourceDirs = sourceSets.main.allSource.srcDirs.absolutePath + jacocoReportPath = "${buildDir}/reports/jacoco/coverage/coverage.xml" +} + +tasks.coveralls { + dependsOn coverage + onlyIf { System.env.'CI' } +} + test { testLogging { events TestLogEvent.FAILED, TestLogEvent.SKIPPED @@ -77,9 +128,8 @@ asciidoctor { idprefix: '', // for compatibility with GitHub preview idseparator: '-', 'site-root': "${sourceDir}", // must be the same as sourceDir, do not modify - 'site-name': 'AddressBook-Level3', - 'site-githuburl': 'https://github.com/se-edu/addressbook-level3', - 'site-seedu': true, // delete this line if your project is not a fork (not a SE-EDU project) + 'site-name': 'Restaurant Management System', + 'site-githuburl': 'https://github.com/CS2113-AY1819S1-F09-2/main', ] options['template_dirs'].each { @@ -96,4 +146,4 @@ task copyStylesheets(type: Copy) { } asciidoctor.dependsOn copyStylesheets -defaultTasks 'clean', 'test', 'asciidoctor' +defaultTasks 'clean', 'test', 'coverage', 'asciidoctor' diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 000000000..a9671a144 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,423 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 000000000..b2e7fdbc3 --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/docs/AboutUs.adoc b/docs/AboutUs.adoc new file mode 100644 index 000000000..93ae56f44 --- /dev/null +++ b/docs/AboutUs.adoc @@ -0,0 +1,57 @@ += About Us +:site-section: AboutUs +:relfileprefix: team/ +:imagesDir: images +:stylesDir: stylesheets + + +Restaurant Management System was developed by the https://github.com/CS2113-AY1819S1-F09-2/main[CS2113-AY1819S1-F09-2] team. + +We are a team based in the http://www.comp.nus.edu.sg[School of Computing, National University of Singapore]. + + +== Project Team + +=== Tay Kang Ming +image::kangmingtay.png[width="150", align="left"] +{empty} [https://github.com/kangmingtay[github]] [<>] + +Role: Team Leader + +Responsibilities: Responsible for overall project coordination. + +''' + +=== Salsabil Tasnia Ali Nikita MD +image::salsabiltasnia.png[width="150", align="left"] +{empty}[https://github.com/SalsabilTasnia[github]] [<>] + +Role: Developer, Documentation Lead + +Responsibilities: Responsible for the menu feature. + +''' + +=== Pham Quang Minh +image::px1099.png[width="150", align="left"] +{empty}[https://github.com/px1099[github]] [<>] + +Role: Developer + +Responsibilities: Responsible for maintaining the code quality & testing. + Order Feature + +''' + +=== Ang Wei Ming +image::angwm.png[width="150", align="left"] +{empty}[https://github.com/AngWM[github]] [<>] + +Role: Developer + +Responsibilities: In charge of the Statistics feature + +''' + +=== Lim Kian Hong +image::kianhong95.png[width="150", align="left"] +{empty}[https://github.com/kianhong95[github]] [<>] + +Role: Developer + +Responsibilities: In charge of Employee Feature + +''' \ No newline at end of file diff --git a/docs/ContactUs.adoc b/docs/ContactUs.adoc new file mode 100644 index 000000000..3c45a83a9 --- /dev/null +++ b/docs/ContactUs.adoc @@ -0,0 +1,8 @@ += Contact Us +:site-section: AboutUs +:imagesDir: images +:stylesDir: stylesheets + +* *Bug reports, Suggestions* : Post in our https://github.com/CS2113-AY1819S1-F09-2/main/issues[issue tracker] +if you noticed bugs or have suggestions on how to improve. +* *Contributing* : We welcome pull requests. Follow the process described https://github.com/oss-generic/process[here] \ No newline at end of file diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index 88ffa2f7a..0159d2e2a 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -1,99 +1,1380 @@ -= Developer Guide += Restaurant Management System - Developer Guide :site-section: DeveloperGuide :toc: :toc-title: +:toc-placement: preamble +:sectnums: :imagesDir: images :stylesDir: stylesheets -:experimental: +:xrefstyle: full +ifdef::env-github[] +:tip-caption: :bulb: +:note-caption: :information_source: +:warning-caption: :warning: +endif::[] +:repoURL: https://github.com/CS2113-AY1819S1-F09-2/main/tree/master + +By: `CS2113-AY1819S1-F09-2` Since: `Aug 2018` Licence: `MIT` + +== Introduction +The Restaurant Management System (RMS) provides restaurants with a more efficient way to manage their operations. It has a wide variety of features which cater to different roles in a restaurant. In line with Singapore's "Smart Nation" movement, the RMS aims to digitise the restaurant business and make day-to-day operations smoother and simpler. Jump to <> to get started with the development. + == Setting up === Prerequisites -* JDK 9 or later -* IntelliJ IDE +. *JDK `9`* or later -=== Importing the project into IntelliJ +. *IntelliJ* IDE + +=== Setting up the project in your computer + +. Fork this repo, and clone the fork to your computer . Open IntelliJ (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project dialog first) -. Set up the correct JDK version +. Set up the correct JDK version for Gradle .. Click `Configure` > `Project Defaults` > `Project Structure` -.. If JDK 9 is listed in the drop down, select it. If it is not, click `New...` and select the directory where you installed JDK 9 -.. Click `OK` +.. Click `New...` and find the directory of the JDK . Click `Import Project` . Locate the `build.gradle` file and select it. Click `OK` . Click `Open as Project` . Click `OK` to accept the default settings -. Run the `seedu.addressbook.Main` class (right-click the `Main` class and click `Run Main.main()`) and try executing a few commands -. Run all the tests (right-click the `test` folder, and click `Run 'All Tests'`) and ensure that they pass -. Open the `StorageFile` file and check for any code errors -.. Due to an ongoing https://youtrack.jetbrains.com/issue/IDEA-189060[issue] with some of the newer versions of IntelliJ, code errors may be detected even if the project can be built and run successfully -.. To resolve this, place your cursor over any of the code section highlighted in red. Press kbd:[ALT + ENTER], and select `Add '--add-modules=java.xml.bind' to module compiler options` +. Open a console and run the command `gradlew processResources` (Mac/Linux: `./gradlew processResources`). It should finish with the `BUILD SUCCESSFUL` message. + +This will generate all resources required by the application and tests. + +=== Verifying the setup + +. Run the `seedu.addressbook.Main` and try a few commands +. <> to ensure they all pass. + +=== Configurations to do before writing code + +==== Configuring the coding style + +This project follows https://github.com/oss-generic/process/blob/master/docs/CodingStandards.adoc[oss-generic coding standards]. IntelliJ's default style is mostly compliant with ours but it uses a different import order from ours. To rectify, + +. Go to `File` > `Settings...` (Windows/Linux), or `IntelliJ IDEA` > `Preferences...` (macOS) +. Select `Editor` > `Code Style` > `Java` +. Click on the `Imports` tab to set the order + +* For `Class count to use import with '\*'` and `Names count to use static import with '*'`: Set to `999` to prevent IntelliJ from contracting the import statements +* For `Import Layout`: The order is `import static all other imports`, `import java.\*`, `import javax.*`, `import org.\*`, `import com.*`, `import all other imports`. Add a `` between each `import` + +Optionally, you can follow the <> document to configure Intellij to check style-compliance as you write code. + +==== Updating documentation to match your fork + +After forking the repo, the documentation will still have the SE-EDU branding and refer to the `se-edu/addressbook-level4` repo. + +If you plan to develop this fork as a separate product (i.e. instead of contributing to `se-edu/addressbook-level4`), you should do the following: + +. Configure the <> in link:{repoURL}/build.gradle[`build.gradle`], such as the `site-name`, to suit your own project. + +. Replace the URL in the attribute `repoURL` in link:{repoURL}/docs/DeveloperGuide.adoc[`DeveloperGuide.adoc`] and link:{repoURL}/docs/UserGuide.adoc[`UserGuide.adoc`] with the URL of your fork. + +==== Setting up CI + +Set up Travis to perform Continuous Integration (CI) for your fork. See <> to learn how to set it up. + +After setting up Travis, you can optionally set up coverage reporting for your team fork (see <>). + +[NOTE] +Coverage reporting could be useful for a team repository that hosts the final version but it is not that useful for your personal fork. + +Optionally, you can set up AppVeyor as a second CI (see <>). +[NOTE] +Having both Travis and AppVeyor ensures your App works on both Unix-based platforms and Windows-based platforms (Travis is Unix-based and AppVeyor is Windows-based) + +==== Getting started with coding + +When you are ready to start coding, + +1. Get some sense of the overall design by reading <>. + + +[[Design-Architecture]] == Design +// tag::architecture[] + +=== Architecture + +.Architecture Diagram +image::Architecture.png[width="600"] + +The *_Architecture Diagram_* given above explains the high-level design of the App. Given below is a quick overview of each component. + +// +//`Main` has only one class called link:{repoURL}/src/main/java/seedu/address/MainApp.java[`MainApp`]. It is responsible for, + +* At app launch: Initializes the components in the correct sequence, and connects them up with each other. +* At shut down: Shuts down the components and invokes cleanup method where necessary. + +<> represents a collection of classes used by multiple other components. Two of those classes play important roles at the architecture level. + +* `Messages` : Used by many classes to write user visible message. +* `Utils` : Contains all the utility methods. +* `Name` : The parent class to the MemberName and EmployeeName classes. +* `Email` The parent class to the MemberEmail and EmployeeEmail classes. + +The rest of the App consists of four key components. + +* <>: The UI of the App. +* <>: The command executor. +* <>: Holds the data of the App in-memory. +* <>: Reads data from, and writes data to, the hard disk. + +// end::architecture[] + +Below contains the description for each of the four key components: + +// tag::UI[] +[[Design-Ui]] +=== UI component + +.Structure of the UI Component +image::UiComponentClassDiagram.png[width="800"] + +The UI consists of `Main` that implements `Stoppable` and extends `Application`. `Main` is also dependent on `Gui` which is dependant on `MainWindow`. +Furthermore, `Gui` and `MainWindow` are both dependent on `Logic`. + +The components use JavaFx UI framework. + +The `Main` component, + +* Executes user commands using the `Gui` component which relies on the `Logic` component. +// end::UI[] + +// tag::logic[] +[[Design-Logic]] +=== Logic component + +[[fig-LogicClassDiagram]] +.Structure of the Logic Component +image::LogicClassDiagram.png[width="800"] + +*API* : +link:{repoURL}/src/seedu/addressbook/logic/Logic.java[`Logic.java`] + +. `Logic` uses the `Parser` class to parse the user command. +. This results in a `Command` object which is executed by `Logic`. +. The command execution can affect the `Data` (e.g. adding a new member) and/or raise events. +. The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`. + +[NOTE] +Each of the `CommandResult` class is used as a specific constructor to correctly handle the class of the returned list +of result. + +e.g. `OrderCommandResult` constructs a `CommandResult` object that only contains a result message and a list of orders +generated from the ordering commands. +// end::logic[] + +// tag::Data[] +[[Design-Data]] +=== Data component +[[fig-DataComponent]] +.Structure of the Data Component +image::DataComponent.png[width="600"] + +*API* : +link:{repoURL}/src/seedu/addressbook/data[`data`] + +. The Data stores the RMS data +. It does not depend on any of the other components + +[NOTE] +As a more OOP model, we can store a Tag list in RMS, which all the relevant features can reference. This would allow RMS to only require one Tag object per unique Tag, instead of each feature needing their own Tag object. +// end::Data[] + +// tag::storage[] + +[[Design-Storage]] +=== Storage component + +.Structure of the Storage Component +image::StorageClassDiagram.png[width="800"] + +*API* : link:{repoURL}/src/seedu/addressbook/storage/StorageFile.java[`StorageFile.java`] + +The `Storage` component, + +* This component saves the RMS data in an XML format and is also able to load the XML back into data. + +// end::storage[] + +// +//[[Design-Commons]] +//=== Common classes +// +//Classes used by multiple components are in the `seedu.addressbook.commons` package. + +// tag::orderimplementation[] +== Implementation + +This section describes some noteworthy details on how certain features are implemented. + +=== Adding a new order + +To avoid writing a long command line when ordering a long list of dishes, a draft order system was implemented. + +* When the ordering draft is initialized or cleared, the member `draftOrder` in `Rms.txt` is loaded with a new object +of class `Order`. It is initialize with an empty list of dish and a pseudo-empty customer field that is not `null`. +* By using the draft order command classes, the ordering draft can be edited (e.g. picking the customer from the member +list if they present their membership card, pick the dish from the menu list one by one before confirming the order). +* Once an order is confirmed, the ordering time is recorded and the total price is calculated to create a new `Order` +object that has the same customer and list of dishes fields as the ordering draft to be added to the order list, the +member points of the customer is updated, and the ordering draft is cleared afterward. + +// tag::draftpointsimplementation[] + +=== Providing the option for members to accumulate and redeem points + +To allow members in the RMS to accumulate and redeem points when an order is made, the command `draftpoints` was implemented. + +* As long as the customer and list of dish fields are not empty, the command `draftpoints` can be used to add a Points object from the Member class into the draft order. A new Points object will be created to store the points that the user wishes to redeem. Once the order is confirmed, the Points object in the Member class that was added by using the `draftcustomer` command will be updated. The member list will then show the newly updated points of that member while the order list will reflect the points that the member has earned from the order. The final price of the order will be calculated based on the price of each dish multiplied by the quantity and also take into account the number of points redeemed. + +// end::draftpointsimplementation[] + +=== Saving and Loading the order list + +The order list is saved by using an adaptive class that convert the existing data in the objects to a jaxb-friendly +class. This means that the order list stores every information of the owner of an order. + +However, the loading the order list cannot be implemented by creating a list of new `Order` objects that contains a new +`Member` object in its customer field based on the data stored in the storage file. This is because the created +customer object are not the same object as the original `Member` object in the member list, so any changes made to the +member list will not get reflected in the order (e.g. not showing the changes of address or phone number if these +fields are to be recorded to the membership database). + +To solve this problem, the order list will retrieve the correct object in the loaded member list at the start of the +program using the method `retrieveMember()` in `AdaptedOrder.java`. + +=== [Proposed] Program State Control + +As there is multiple functionality in the program, it is proposed that a program state control functionality should be +implemented to assist the user navigate through the program. The command keywords can be simplified and the help +message can be specifically wrote in each states to reduce the burdens of remembering very long line of commands. + +This can be done by introducing a status variable in the `Logic` module, creating command to change the state variable +and adjusting the returning command in the `Parser` based on that variable. +// end::orderimplementation[] + +// tag::statisticsimplementation[] + +=== Statistics + +==== Displaying Tables + +As RMS is a CLI (Command Line Interface) based program, we are not able to display any graphical tables to the users. However, the alternative to this is to print out the tables in an Ascii format. Therefore, the `AsciiTable` class is created. This class helps to format any data into a string that is able to display as an AsciiTable. + +In order to display this table correctly, the font being used in the results windows needs to be a monospaced^*^ font. + +_*Monospaced fonts refer to fonts with every characters having equal width size._ + +==== Calculating Menu Statistics + +The way the statistics for menu items are calculated is by iterating through the list of orders and incrementing a counter for each unique menu items every time it is sold. These counters are then sorted in ascending order and it will print the menu item with the most sales and the one with the least sales for the best sellers and the unpopular items respectively. + + +//==== Design Considerations +// +//===== Aspect: How undo & redo executes +// +//* **Alternative 1 (current choice):** Saves the entire address book. +//** Pros: Easy to implement. +//** Cons: May have performance issues in terms of memory usage. +//* **Alternative 2:** Individual command knows how to undo/redo by itself. +//** Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). +//** Cons: We must ensure that the implementation of each individual command are correct. +// +//===== Aspect: Data structure to support the undo/redo commands +// +//* **Alternative 1 (current choice):** Use a list to store the history of address book states. +//** Pros: Easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project. +//** Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both `HistoryManager` and `VersionedAddressBook`. +//* **Alternative 2:** Use `HistoryManager` for undo/redo +//** Pros: We do not need to maintain a separate list, and just reuse what is already in the codebase. +//** Cons: Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as `HistoryManager` now needs to do two different things. +// +//=== [Proposed] Data Encryption +// +//_{Explain here how the data encryption feature will be implemented}_ +// +// +//=== Logging +// +//We are using `java.util.logging` package for logging. The `LogsCenter` class is used to manage the logging levels and logging destinations. +// +//* The logging level can be controlled using the `logLevel` setting in the configuration file (See <>) +//* The `Logger` for a class can be obtained using `LogsCenter.getLogger(Class)` which will log messages according to the specified logging level +//* Currently log messages are output through: `Console` and to a `.log` file. +// +//*Logging Levels* +// +//* `SEVERE` : Critical problem detected which may possibly cause the termination of the application +//* `WARNING` : Can continue, but with caution +//* `INFO` : Information showing the noteworthy actions by the App +//* `FINE` : Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size +// +//[[Implementation-Configuration]] +//=== Configuration +// +//Certain properties of the application can be controlled (e.g App name, logging level) through the configuration file (default: `config.json`). +// +== Documentation + +We use asciidoc for writing documentation. + +//[NOTE] +//We chose asciidoc over Markdown because asciidoc, although a bit more complex than Markdown, provides more flexibility in formatting. +// +//=== Editing Documentation +// +//See <> to learn how to render `.adoc` files locally to preview the end result of your edits. +//Alternatively, you can download the AsciiDoc plugin for IntelliJ, which allows you to preview the changes you have made to your `.adoc` files in real-time. +// +//=== Publishing Documentation +// +//See <> to learn how to deploy GitHub Pages using Travis. +// +//=== Converting Documentation to PDF format +// +//We use https://www.google.com/chrome/browser/desktop/[Google Chrome] for converting documentation to PDF format, as Chrome's PDF engine preserves hyperlinks used in webpages. +// +//Here are the steps to convert the project documentation files to PDF format. +// +//. Follow the instructions in <> to convert the AsciiDoc files in the `docs/` directory to HTML format. +//. Go to your generated HTML files in the `build/docs` folder, right click on them and select `Open with` -> `Google Chrome`. +//. Within Chrome, click on the `Print` option in Chrome's menu. +//. Set the destination to `Save as PDF`, then click `Save` to save a copy of the file in PDF format. For best results, use the settings indicated in the screenshot below. +// +//.Saving documentation as PDF files in Chrome +//image::chrome_save_as_pdf.png[width="300"] +// +//[[Docs-SiteWideDocSettings]] +//=== Site-wide Documentation Settings +// +//The link:{repoURL}/build.gradle[`build.gradle`] file specifies some project-specific https://asciidoctor.org/docs/user-manual/#attributes[asciidoc attributes] which affects how all documentation files within this project are rendered. +// +//[TIP] +//Attributes left unset in the `build.gradle` file will use their *default value*, if any. +// +//[cols="1,2a,1", options="header"] +//.List of site-wide attributes +//|=== +//|Attribute name |Description |Default value +// +//|`site-name` +//|The name of the website. +//If set, the name will be displayed near the top of the page. +//|_not set_ +// +//|`site-githuburl` +//|URL to the site's repository on https://github.com[GitHub]. +//Setting this will add a "View on GitHub" link in the navigation bar. +//|_not set_ +// +//|`site-seedu` +//|Define this attribute if the project is an official SE-EDU project. +//This will render the SE-EDU navigation bar at the top of the page, and add some SE-EDU-specific navigation items. +//|_not set_ +// +//|=== +// +//[[Docs-PerFileDocSettings]] +//=== Per-file Documentation Settings +// +//Each `.adoc` file may also specify some file-specific https://asciidoctor.org/docs/user-manual/#attributes[asciidoc attributes] which affects how the file is rendered. +// +//Asciidoctor's https://asciidoctor.org/docs/user-manual/#builtin-attributes[built-in attributes] may be specified and used as well. +// +//[TIP] +//Attributes left unset in `.adoc` files will use their *default value*, if any. +// +//[cols="1,2a,1", options="header"] +//.List of per-file attributes, excluding Asciidoctor's built-in attributes +//|=== +//|Attribute name |Description |Default value +// +//|`site-section` +//|Site section that the document belongs to. +//This will cause the associated item in the navigation bar to be highlighted. +//One of: `UserGuide`, `DeveloperGuide`, ``LearningOutcomes``{asterisk}, `AboutUs`, `ContactUs` +// +//_{asterisk} Official SE-EDU projects only_ +//|_not set_ +// +//|`no-site-header` +//|Set this attribute to remove the site navigation bar. +//|_not set_ +// +//|=== +// +=== Site Template -image::mainClassDiagram.png[] +The files in link:{repoURL}/docs/stylesheets[`docs/stylesheets`] are the https://developer.mozilla.org/en-US/docs/Web/CSS[CSS stylesheets] of the site. +You can modify them to change some properties of the site's design. +The files in link:{repoURL}/docs/templates[`docs/templates`] controls the rendering of `.adoc` files into HTML5. +These template files are written in a mixture of https://www.ruby-lang.org[Ruby] and http://slim-lang.com[Slim]. + +[WARNING] +==== +Modifying the template files in link:{repoURL}/docs/templates[`docs/templates`] requires some knowledge and experience with Ruby and Asciidoctor's API. +You should only modify them if you need greater control over the site's layout than what stylesheets can provide. +The SE-EDU team does not provide support for modified template files. +==== + +[[Testing]] == Testing -* In IntelliJ, right-click on the `test` folder and choose `Run 'All Tests'` +=== Running Tests + +There are three ways to run tests. + +*Method 1: Using IntelliJ JUnit test runner* + +* To run all tests, right-click on the `src/test/java` folder and choose `Run 'All Tests'` +* To run a subset of tests, you can right-click on a test package, test class, or a test and choose `Run 'ABC'` + +*Method 2: Using Gradle* + +* Open a console and run the command `gradlew clean allTests` (Mac/Linux: `./gradlew clean allTests`) + +*Method 3: Manual testing* + +* Go to https://github.com/CS2113-AY1819S1-F09-2/main/releases[our github release page] and download `Rms.jar` and `Rms.txt` +* Put them into the same folder and run `Rms.jar` and confirm that the program is running with pre-populated data +* `Rms.txt` is populated with existing data, which allows testers to test the program using the data in `Rms.txt` +* Test the program using the user and the developer guide + + +[NOTE] +See <> for more info on how to run tests using Gradle. + + +=== Types of tests + +We have 1 type of test: + +//. *GUI Tests* - These are tests involving the GUI. They include, +//.. _System Tests_ that test the entire App by simulating user actions on the GUI. These are in the `systemtests` package. +//.. _Unit tests_ that test the individual components. These are in `seedu.address.ui` package. +. *Non-GUI Tests* - These are tests not involving the GUI. They include, +.. _Unit tests_ targeting the lowest level methods/classes. + +e.g. `seedu.addressbook.commons.UtilsTest` +.. _Integration tests_ that are checking the integration of multiple code units (those code units are assumed to be working). + +e.g. `seedu.addressbook.storage.StorageFileTest` + + `seedu.addressbook.parser.ParserTest` +.. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together. + +e.g. `seedu.addressbook.logic.LogicTest` + + +== Dev Ops + +=== Build Automation + +See <> to learn how to use Gradle for build automation. + +=== Continuous Integration + +We use https://travis-ci.org/[Travis CI] and https://www.appveyor.com/[AppVeyor] to perform _Continuous Integration_ on our projects. See <> and <> for more details. + +=== Coverage Reporting + +We use https://coveralls.io/[Coveralls] to track the code coverage of our projects. See <> for more details. + +=== Documentation Previews +When a pull request has changes to asciidoc files, you can use https://www.netlify.com/[Netlify] to see a preview of how the HTML version of those asciidoc files will look like when the pull request is merged. See <> for more details. + +=== Making a Release + +Here are the steps to create a new release. + +. Update the version number in link:{repoURL}/src/main/java/seedu/address/MainApp.java[`MainApp.java`]. +. Generate a JAR file <>. +. Tag the repo with the version number. e.g. `v0.1` +. https://help.github.com/articles/creating-releases/[Create a new release using GitHub] and upload the JAR file you created. + +//=== Managing Dependencies +// +//A project often depends on third-party libraries. For example, Address Book depends on the http://wiki.fasterxml.com/JacksonHome[Jackson library] for XML parsing. Managing these _dependencies_ can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than these alternatives. + +//a. Include those libraries in the repo (this bloats the repo size) + +//b. Require developers to download those libraries manually (this creates extra work for developers) +// +//[[GetStartedProgramming]] +//[appendix] +//== Suggested Programming Tasks to Get Started +// +//Suggested path for new programmers: +// +//1. First, add small local-impact (i.e. the impact of the change does not go beyond the component) enhancements to one component at a time. Some suggestions are given in <>. +// +//2. Next, add a feature that touches multiple components to learn how to implement an end-to-end feature across all components. <> explains how to go about adding such a feature. +// +//[[GetStartedProgramming-EachComponent]] +//=== Improving each component +// +//Each individual exercise in this section is component-based (i.e. you would not need to modify the other components to get it to work). +// +//[discrete] +//==== `Logic` component +// +//*Scenario:* You are in charge of `logic`. During dog-fooding, your team realize that it is troublesome for the user to type the whole command in order to execute a command. Your team devise some strategies to help cut down the amount of typing necessary, and one of the suggestions was to implement aliases for the command words. Your job is to implement such aliases. +// +//[TIP] +//Do take a look at <> before attempting to modify the `Logic` component. +// +//. Add a shorthand equivalent alias for each of the individual commands. For example, besides typing `clear`, the user can also type `c` to remove all persons in the list. +//+ +//**** +//* Hints +//** Just like we store each individual command word constant `COMMAND_WORD` inside `*Command.java` (e.g. link:{repoURL}/src/main/java/seedu/address/logic/commands/FindCommand.java[`FindCommand#COMMAND_WORD`], link:{repoURL}/src/main/java/seedu/address/logic/commands/DeleteCommand.java[`DeleteCommand#COMMAND_WORD`]), you need a new constant for aliases as well (e.g. `FindCommand#COMMAND_ALIAS`). +//** link:{repoURL}/src/main/java/seedu/address/logic/parser/AddressBookParser.java[`AddressBookParser`] is responsible for analyzing command words. +//* Solution +//** Modify the switch statement in link:{repoURL}/src/main/java/seedu/address/logic/parser/AddressBookParser.java[`AddressBookParser#parseCommand(String)`] such that both the proper command word and alias can be used to execute the same intended command. +//** Add new tests for each of the aliases that you have added. +//** Update the user guide to document the new aliases. +//** See this https://github.com/se-edu/addressbook-level4/pull/785[PR] for the full solution. +//**** +// +//[discrete] +//==== `Model` component +// +//*Scenario:* You are in charge of `model`. One day, the `logic`-in-charge approaches you for help. He wants to implement a command such that the user is able to remove a particular tag from everyone in the address book, but the model API does not support such a functionality at the moment. Your job is to implement an API method, so that your teammate can use your API to implement his command. +// +//[TIP] +//Do take a look at <> before attempting to modify the `Model` component. +// +//. Add a `removeTag(Tag)` method. The specified tag will be removed from everyone in the address book. +//+ +//**** +//* Hints +//** The link:{repoURL}/src/main/java/seedu/address/model/Model.java[`Model`] and the link:{repoURL}/src/main/java/seedu/address/model/AddressBook.java[`AddressBook`] API need to be updated. +//** Think about how you can use SLAP to design the method. Where should we place the main logic of deleting tags? +//** Find out which of the existing API methods in link:{repoURL}/src/main/java/seedu/address/model/AddressBook.java[`AddressBook`] and link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`] classes can be used to implement the tag removal logic. link:{repoURL}/src/main/java/seedu/address/model/AddressBook.java[`AddressBook`] allows you to update a person, and link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`] allows you to update the tags. +//* Solution +//** Implement a `removeTag(Tag)` method in link:{repoURL}/src/main/java/seedu/address/model/AddressBook.java[`AddressBook`]. Loop through each person, and remove the `tag` from each person. +//** Add a new API method `deleteTag(Tag)` in link:{repoURL}/src/main/java/seedu/address/model/ModelManager.java[`ModelManager`]. Your link:{repoURL}/src/main/java/seedu/address/model/ModelManager.java[`ModelManager`] should call `AddressBook#removeTag(Tag)`. +//** Add new tests for each of the new public methods that you have added. +//** See this https://github.com/se-edu/addressbook-level4/pull/790[PR] for the full solution. +//**** +// +//[discrete] +//==== `Ui` component +// +//*Scenario:* You are in charge of `ui`. During a beta testing session, your team is observing how the users use your address book application. You realize that one of the users occasionally tries to delete non-existent tags from a contact, because the tags all look the same visually, and the user got confused. Another user made a typing mistake in his command, but did not realize he had done so because the error message wasn't prominent enough. A third user keeps scrolling down the list, because he keeps forgetting the index of the last person in the list. Your job is to implement improvements to the UI to solve all these problems. +// +//[TIP] +//Do take a look at <> before attempting to modify the `UI` component. +// +//. Use different colors for different tags inside person cards. For example, `friends` tags can be all in brown, and `colleagues` tags can be all in yellow. +//+ +//**Before** +//+ +//image::getting-started-ui-tag-before.png[width="300"] +//+ +//**After** +//+ +//image::getting-started-ui-tag-after.png[width="300"] +//+ +//**** +//* Hints +//** The tag labels are created inside link:{repoURL}/src/main/java/seedu/address/ui/PersonCard.java[the `PersonCard` constructor] (`new Label(tag.tagName)`). https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/Label.html[JavaFX's `Label` class] allows you to modify the style of each Label, such as changing its color. +//** Use the .css attribute `-fx-background-color` to add a color. +//** You may wish to modify link:{repoURL}/src/main/resources/view/DarkTheme.css[`DarkTheme.css`] to include some pre-defined colors using css, especially if you have experience with web-based css. +//* Solution +//** You can modify the existing test methods for `PersonCard` 's to include testing the tag's color as well. +//** See this https://github.com/se-edu/addressbook-level4/pull/798[PR] for the full solution. +//*** The PR uses the hash code of the tag names to generate a color. This is deliberately designed to ensure consistent colors each time the application runs. You may wish to expand on this design to include additional features, such as allowing users to set their own tag colors, and directly saving the colors to storage, so that tags retain their colors even if the hash code algorithm changes. +//**** +// +//. Modify link:{repoURL}/src/main/java/seedu/address/commons/events/ui/NewResultAvailableEvent.java[`NewResultAvailableEvent`] such that link:{repoURL}/src/main/java/seedu/address/ui/ResultDisplay.java[`ResultDisplay`] can show a different style on error (currently it shows the same regardless of errors). +//+ +//**Before** +//+ +//image::getting-started-ui-result-before.png[width="200"] +//+ +//**After** +//+ +//image::getting-started-ui-result-after.png[width="200"] +//+ +//**** +//* Hints +//** link:{repoURL}/src/main/java/seedu/address/commons/events/ui/NewResultAvailableEvent.java[`NewResultAvailableEvent`] is raised by link:{repoURL}/src/main/java/seedu/address/ui/CommandBox.java[`CommandBox`] which also knows whether the result is a success or failure, and is caught by link:{repoURL}/src/main/java/seedu/address/ui/ResultDisplay.java[`ResultDisplay`] which is where we want to change the style to. +//** Refer to link:{repoURL}/src/main/java/seedu/address/ui/CommandBox.java[`CommandBox`] for an example on how to display an error. +//* Solution +//** Modify link:{repoURL}/src/main/java/seedu/address/commons/events/ui/NewResultAvailableEvent.java[`NewResultAvailableEvent`] 's constructor so that users of the event can indicate whether an error has occurred. +//** Modify link:{repoURL}/src/main/java/seedu/address/ui/ResultDisplay.java[`ResultDisplay#handleNewResultAvailableEvent(NewResultAvailableEvent)`] to react to this event appropriately. +//** You can write two different kinds of tests to ensure that the functionality works: +//*** The unit tests for `ResultDisplay` can be modified to include verification of the color. +//*** The system tests link:{repoURL}/src/test/java/systemtests/AddressBookSystemTest.java[`AddressBookSystemTest#assertCommandBoxShowsDefaultStyle() and AddressBookSystemTest#assertCommandBoxShowsErrorStyle()`] to include verification for `ResultDisplay` as well. +//** See this https://github.com/se-edu/addressbook-level4/pull/799[PR] for the full solution. +//*** Do read the commits one at a time if you feel overwhelmed. +//**** +// +//. Modify the link:{repoURL}/src/main/java/seedu/address/ui/StatusBarFooter.java[`StatusBarFooter`] to show the total number of people in the address book. +//+ +//**Before** +//+ +//image::getting-started-ui-status-before.png[width="500"] +//+ +//**After** +//+ +//image::getting-started-ui-status-after.png[width="500"] +//+ +//**** +//* Hints +//** link:{repoURL}/src/main/resources/view/StatusBarFooter.fxml[`StatusBarFooter.fxml`] will need a new `StatusBar`. Be sure to set the `GridPane.columnIndex` properly for each `StatusBar` to avoid misalignment! +//** link:{repoURL}/src/main/java/seedu/address/ui/StatusBarFooter.java[`StatusBarFooter`] needs to initialize the status bar on application start, and to update it accordingly whenever the address book is updated. +//* Solution +//** Modify the constructor of link:{repoURL}/src/main/java/seedu/address/ui/StatusBarFooter.java[`StatusBarFooter`] to take in the number of persons when the application just started. +//** Use link:{repoURL}/src/main/java/seedu/address/ui/StatusBarFooter.java[`StatusBarFooter#handleAddressBookChangedEvent(AddressBookChangedEvent)`] to update the number of persons whenever there are new changes to the addressbook. +//** For tests, modify link:{repoURL}/src/test/java/guitests/guihandles/StatusBarFooterHandle.java[`StatusBarFooterHandle`] by adding a state-saving functionality for the total number of people status, just like what we did for save location and sync status. +//** For system tests, modify link:{repoURL}/src/test/java/systemtests/AddressBookSystemTest.java[`AddressBookSystemTest`] to also verify the new total number of persons status bar. +//** See this https://github.com/se-edu/addressbook-level4/pull/803[PR] for the full solution. +//**** +// +//[discrete] +//==== `Storage` component +// +//*Scenario:* You are in charge of `storage`. For your next project milestone, your team plans to implement a new feature of saving the address book to the cloud. However, the current implementation of the application constantly saves the address book after the execution of each command, which is not ideal if the user is working on limited internet connection. Your team decided that the application should instead save the changes to a temporary local backup file first, and only upload to the cloud after the user closes the application. Your job is to implement a backup API for the address book storage. +// +//[TIP] +//Do take a look at <> before attempting to modify the `Storage` component. +// +//. Add a new method `backupAddressBook(ReadOnlyAddressBook)`, so that the address book can be saved in a fixed temporary location. +//+ +//**** +//* Hint +//** Add the API method in link:{repoURL}/src/main/java/seedu/address/storage/AddressBookStorage.java[`AddressBookStorage`] interface. +//** Implement the logic in link:{repoURL}/src/main/java/seedu/address/storage/StorageManager.java[`StorageManager`] and link:{repoURL}/src/main/java/seedu/address/storage/XmlAddressBookStorage.java[`XmlAddressBookStorage`] class. +//* Solution +//** See this https://github.com/se-edu/addressbook-level4/pull/594[PR] for the full solution. +//**** +// +//[[GetStartedProgramming-RemarkCommand]] +//=== Creating a new command: `remark` +// +//By creating this command, you will get a chance to learn how to implement a feature end-to-end, touching all major components of the app. +// +//*Scenario:* You are a software maintainer for `addressbook`, as the former developer team has moved on to new projects. The current users of your application have a list of new feature requests that they hope the software will eventually have. The most popular request is to allow adding additional comments/notes about a particular contact, by providing a flexible `remark` field for each contact, rather than relying on tags alone. After designing the specification for the `remark` command, you are convinced that this feature is worth implementing. Your job is to implement the `remark` command. +// +//==== Description +//Edits the remark for a person specified in the `INDEX`. + +//Format: `remark INDEX r/[REMARK]` +// +//Examples: +// +//* `remark 1 r/Likes to drink coffee.` + +//Edits the remark for the first person to `Likes to drink coffee.` +//* `remark 1 r/` + +//Removes the remark for the first person. +// +//==== Step-by-step Instructions +// +//===== [Step 1] Logic: Teach the app to accept 'remark' which does nothing +//Let's start by teaching the application how to parse a `remark` command. We will add the logic of `remark` later. +// +//**Main:** +// +//. Add a `RemarkCommand` that extends link:{repoURL}/src/main/java/seedu/address/logic/commands/Command.java[`Command`]. Upon execution, it should just throw an `Exception`. +//. Modify link:{repoURL}/src/main/java/seedu/address/logic/parser/AddressBookParser.java[`AddressBookParser`] to accept a `RemarkCommand`. +// +//**Tests:** +// +//. Add `RemarkCommandTest` that tests that `execute()` throws an Exception. +//. Add new test method to link:{repoURL}/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java[`AddressBookParserTest`], which tests that typing "remark" returns an instance of `RemarkCommand`. +// +//===== [Step 2] Logic: Teach the app to accept 'remark' arguments +//Let's teach the application to parse arguments that our `remark` command will accept. E.g. `1 r/Likes to drink coffee.` +// +//**Main:** +// +//. Modify `RemarkCommand` to take in an `Index` and `String` and print those two parameters as the error message. +//. Add `RemarkCommandParser` that knows how to parse two arguments, one index and one with prefix 'r/'. +//. Modify link:{repoURL}/src/main/java/seedu/address/logic/parser/AddressBookParser.java[`AddressBookParser`] to use the newly implemented `RemarkCommandParser`. +// +//**Tests:** +// +//. Modify `RemarkCommandTest` to test the `RemarkCommand#equals()` method. +//. Add `RemarkCommandParserTest` that tests different boundary values +//for `RemarkCommandParser`. +//. Modify link:{repoURL}/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java[`AddressBookParserTest`] to test that the correct command is generated according to the user input. +// +//===== [Step 3] Ui: Add a placeholder for remark in `PersonCard` +//Let's add a placeholder on all our link:{repoURL}/src/main/java/seedu/address/ui/PersonCard.java[`PersonCard`] s to display a remark for each person later. +// +//**Main:** +// +//. Add a `Label` with any random text inside link:{repoURL}/src/main/resources/view/PersonListCard.fxml[`PersonListCard.fxml`]. +//. Add FXML annotation in link:{repoURL}/src/main/java/seedu/address/ui/PersonCard.java[`PersonCard`] to tie the variable to the actual label. +// +//**Tests:** +// +//. Modify link:{repoURL}/src/test/java/guitests/guihandles/PersonCardHandle.java[`PersonCardHandle`] so that future tests can read the contents of the remark label. +// +//===== [Step 4] Model: Add `Remark` class +//We have to properly encapsulate the remark in our link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`] class. Instead of just using a `String`, let's follow the conventional class structure that the codebase already uses by adding a `Remark` class. +// +//**Main:** +// +//. Add `Remark` to model component (you can copy from link:{repoURL}/src/main/java/seedu/address/model/person/Address.java[`Address`], remove the regex and change the names accordingly). +//. Modify `RemarkCommand` to now take in a `Remark` instead of a `String`. +// +//**Tests:** +// +//. Add test for `Remark`, to test the `Remark#equals()` method. +// +//===== [Step 5] Model: Modify `Person` to support a `Remark` field +//Now we have the `Remark` class, we need to actually use it inside link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`]. +// +//**Main:** +// +//. Add `getRemark()` in link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`]. +//. You may assume that the user will not be able to use the `add` and `edit` commands to modify the remarks field (i.e. the person will be created without a remark). +//. Modify link:{repoURL}/src/main/java/seedu/address/model/util/SampleDataUtil.java/[`SampleDataUtil`] to add remarks for the sample data (delete your `addressBook.xml` so that the application will load the sample data when you launch it.) +// +//===== [Step 6] Storage: Add `Remark` field to `XmlAdaptedPerson` class +//We now have `Remark` s for `Person` s, but they will be gone when we exit the application. Let's modify link:{repoURL}/src/main/java/seedu/address/storage/XmlAdaptedPerson.java[`XmlAdaptedPerson`] to include a `Remark` field so that it will be saved. +// +//**Main:** +// +//. Add a new Xml field for `Remark`. +// +//**Tests:** +// +//. Fix `invalidAndValidPersonAddressBook.xml`, `typicalPersonsAddressBook.xml`, `validAddressBook.xml` etc., such that the XML tests will not fail due to a missing `` element. +// +//===== [Step 6b] Test: Add withRemark() for `PersonBuilder` +//Since `Person` can now have a `Remark`, we should add a helper method to link:{repoURL}/src/test/java/seedu/address/testutil/PersonBuilder.java[`PersonBuilder`], so that users are able to create remarks when building a link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`]. +// +//**Tests:** +// +//. Add a new method `withRemark()` for link:{repoURL}/src/test/java/seedu/address/testutil/PersonBuilder.java[`PersonBuilder`]. This method will create a new `Remark` for the person that it is currently building. +//. Try and use the method on any sample `Person` in link:{repoURL}/src/test/java/seedu/address/testutil/TypicalPersons.java[`TypicalPersons`]. +// +//===== [Step 7] Ui: Connect `Remark` field to `PersonCard` +//Our remark label in link:{repoURL}/src/main/java/seedu/address/ui/PersonCard.java[`PersonCard`] is still a placeholder. Let's bring it to life by binding it with the actual `remark` field. +// +//**Main:** +// +//. Modify link:{repoURL}/src/main/java/seedu/address/ui/PersonCard.java[`PersonCard`]'s constructor to bind the `Remark` field to the `Person` 's remark. +// +//**Tests:** +// +//. Modify link:{repoURL}/src/test/java/seedu/address/ui/testutil/GuiTestAssert.java[`GuiTestAssert#assertCardDisplaysPerson(...)`] so that it will compare the now-functioning remark label. +// +//===== [Step 8] Logic: Implement `RemarkCommand#execute()` logic +//We now have everything set up... but we still can't modify the remarks. Let's finish it up by adding in actual logic for our `remark` command. +// +//**Main:** +// +//. Replace the logic in `RemarkCommand#execute()` (that currently just throws an `Exception`), with the actual logic to modify the remarks of a person. +// +//**Tests:** +// +//. Update `RemarkCommandTest` to test that the `execute()` logic works. +// +//==== Full Solution +// +//See this https://github.com/se-edu/addressbook-level4/pull/599[PR] for the step-by-step solution. [appendix] -== User Stories +== Product Scope +*Target user profile*: + +* has a need to manage a restaurant with high volume of orders +* prefer desktop apps over other types +* can type fast +* prefers typing over mouse input +* is reasonably comfortable using CLI apps + +*Value proposition*: manage restaurant faster than a typical mouse/GUI driven app + +// tag::userstories[] +[appendix] +== User Stories Priorities: High (must have) - `* * \*`, Medium (nice to have) - `* \*`, Low (unlikely to have) - `*` -[width="100%",cols="22%,<23%,<25%,<30%",options="header",] -|=========================================================================================================================================== +[width="59%",cols="22%,<23%,<25%,<30%",options="header",] +|======================================================================= |Priority |As a ... |I want to ... |So that I can... -|`* * *` |new user |see usage instructions |refer to instructions when I forget how to use the App -|`* * *` |user |add a new person | -|`* * *` |user |delete a person |remove entries that I no longer need -|`* * *` |user |find a person by name |locate details of persons without having to go through the entire list -|`* *` |user |hide <> by default |minimize chance of someone else seeing them by accident -|`*` |user with many persons in the address book |sort persons by name |locate a person easily -|=========================================================================================================================================== +|`* * *` |Cashier |add, edit and delete orders made |collate the customer’s orders and send it to the kitchen + +|`* *` |Cashier |add new members, delete old members and view all members of the restaurant |keep track of which customer has a membership at the restaurant + +|`* * *` |Cashier |view all existing menu items of the restaurant |know which item the customer wants when taking an order + +|`* * *` |Cashier |view the recommended items in the menu |suggest customers on what to order of needed + +|`* * *` |Cashier |search for a menu item |find a particular item easily without viewing the menu lists + +|`* * *` |Manager |keep track of the restaurant business statistics such as daily revenue |understand if the restaurant is making a loss or not + +|`* * *` |Manager |keep track of employees wages | know how much I have to pay or how much I have paid to restaurant employees + +|`* *` |Manager |keep track of which menu item are sold the most | know which food is the best seller + +|`* *` |Manager |add, edit and delete items from the menu |keep my restaurant menu interesting + +|`* *` |Manager |list employees |to view the details of the employees working in the restaurant + +|`* * *` | Manager | add, edit and delete employees from the system |to manage information of the employees working in the restaurant + +|`* * *` |Chef |view all the orders made |know which order I need to prepare next + +|`* * *` |Chef |update the order status |let the customers know that their orders are ready + +|`* *` |Employee |clock in and clock out attendance |account for the time period which I am working +|======================================================================= + +_{More to be added}_ +// end::userstories[] [appendix] == Use Cases -(For all use cases below, the *System* is the `AddressBook` and the *Actor* is the `user`, unless specified otherwise) +(For all use cases below, the *System* is the `Restaurant Management System (RMS)`, unless specified otherwise) -=== Use case: Delete person +[discrete] +=== Use case: Taking a Customer’s Orders +Actor: Manager and Cashier *MSS* -. User requests to list persons -. AddressBook shows a list of persons -. User requests to delete a specific person in the list -. AddressBook deletes the person. +1. User chooses to view the menu list +2. FMS shows the list of items in the menu +3. User informs the menu item and the quantity they wish to order +4. FMS put that item and its quantity in the order +5. Repeat step 1 to 4 until user confirm the order +6. FMS acknowledge the order and add it to the system + Use case ends. *Extensions* -* 2a. The list is empty. +[none] +* 1a. If a customer is in the restaurant's member list, user can record them +in the order so that bonus points can be accumulated when the order is confirmed. + ++ +Return to step 1. + +[none] +* 2a. Menu list too long or does not have what the customer exactly wants + ++ +[none] +** 2a1. User enters the find command and searches for the item customer is looking for ++ +2a2. FMS returns the list of items searched by the command the user entered + + +Return to step 3. + +// tag::menuusecase[] +[discrete] +=== Use case: Show Main Menu +System: Restaurant Management System (RMS) + +Use Case: Show Main Menu + +Actor: Manager, Cashier + +*MSS* + +1. User chooses to view the Main Menu to see the categories of menu item available. +2. RMS shows Main Menu of the restaurant. + Use case ends. -* 3a. The given index is invalid. -** 3a1. AddressBook shows an error message. +[discrete] +=== Use case: List Menu Based On Category +System: Restaurant Management System (RMS) + +Use Case: List employees + +Actor: Manager, Cashier + +Preconditions: User executes the Show Main Menu use case to know the existing menu categories. + +Guarantees: + +* List of menu items of a particular category will be displayed. + +*MSS* + +1. User requests to list menu items of a particular category. +2. RMS displays a list of the menu items of that category. + -Use case resumes at step 2. +Use case ends. -[appendix] -== Non Functional Requirements +*Extensions* + + +* 1a. RMS detects that an invalid type has been entered. +** 1a1. RMS displays an error message stating what the valid item types are. ++ +Use case ends. + +[discrete] +=== Use case: List Menu To View All Menu Items +System: Restaurant Management System (RMS) + +Use Case: List employees + +Actor: Manager, Cashier + + +Guarantees: + +* Displays list of menu items that exists in the menu database. + +*MSS* + +1. User requests to view all the menu items of the menu database, not just based on their categories. +2. RMS displays all the menu items that exist in the restaurant's database. ++ +Use case ends. + + +[discrete] +=== Use case: Add Menu +Actor: Manager +Preconditions: List Menu By Type command should be executed prior to this use case. + +Guarantees: + +* New menu item is added to the RMS. + +*MSS* + +1. User requests to add a new menu item with the required parameters. +2. RMS adds a new menu item to the existing menu list with the parameters that were specified by the User. ++ +Use case ends. + +*Extensions* + + +* 1a. RMS detects that an invalid command format has been entered. +** 1a1. RMS displays an error that says the command format is invalid. +** 1a2. RMS displays information on the proper use of the command along with an example. ++ +Use case ends. + +* 1b. RMS detects that the new menu item to be added has the same name as one of the exisitng menu items. +** 1b1. RMS detects this as an an attempt to add a duplicate menu item. +** 1b2. RMS displays an error that says the employee already exists in Rms. ++ +Use case ends. + +* 1c. RMS detects that the price field entered is invalid. +** 1c1. RMS dispays an error message stating the appropriate price field argument to be entered. ++ +Use case ends. + +* 1d. RMS detects that the type field entered is invalid. +** 1d1. RMS dispays an error message stating what the valid item types are. ++ +Use case ends. + +[discrete] +=== Use case: Delete menu +Actor: Restaurant Manager + +Preconditions: List Menu By Type or Find Menu use case should be executed prior to this use case this use case. + +Guarantees: + +* The specified menu item will be deleted from the RMS. + +*MSS* + +1. User requests to delete menu item by using the index as shown on the list of menu items. +2. RMS deletes a menu item of the specified index. ++ +Use case ends. + +*Extensions* + + +* 1a. RMS detects that an invalid index has been entered. +** 1a1. RMS displays an error that says the index provided is invalid. ++ +Use case ends. + +[discrete] +=== Use case: Find Menu +System: Restaurant Management System (RMS) + +Use Case: Find Menu + +Actor: Cashier + +*MSS* + +1. User chooses to find menu items containing some keywords. +2. RMS shows the menu items that contain those keywords. + +Use case ends. + +[discrete] +=== Use case: Menu Recommendations +System: Restaurant Management System (RMS) + +Use Case: Menu Recommendations + +Actor: Cashier + +*MSS* + +1. User chooses to view the recommended items from the different categories. +2. System displays the best seller items from each category as recommendations. + +Use case ends. +// end::menuusecase[] + +//tag::employeeusecase[] +[discrete] +=== Use case: List employees +System: Restaurant Management System (RMS) + +Use Case: List employees + +Actor: Restaurant Manager + +Preconditions: User has started up the system. + +Guarantees: + +* List of employees will be displayed. + +*MSS* + +1. User requests to list employees. +2. RMS displays a list of the employees. ++ +Use case ends. + +[discrete] +=== Use case: Add employee +Actor: Restaurant Manager + +Preconditions: "List employee" use case was used before this use case. + +Guarantees: + +* New employee is added to the RMS. + +*MSS* + +1. User requests to add a new employee with the required parameters. +2. RMS adds a new employee to the list with the parameters that were specified by the User. ++ +Use case ends. + +*Extensions* + + +* 1a. RMS detects that an invalid command format has been entered. +** 1a1. RMS displays an error that says the command format is invalid. +** 1a2. RMS displays information on the proper use of the command along with an example. ++ +Use case ends. + +* 1b. RMS detects that there is an attempt to add a duplicate employee. +** 1b1. RMS displays an error that says the employee already exists in Rms. ++ +Use case ends. + +[discrete] +=== Use case: Delete employee +Actor: Restaurant Manager + +Preconditions: "List employee" use case was used before this use case. + +Guarantees: + +* The specified employee will be deleted from the RMS. + +*MSS* + +1. User requests to delete an employee by using the index as shown on the list of employees. +2. RMS deletes an employee using the specified index. ++ +Use case ends. + +*Extensions* + + +* 1a. RMS detects that an invalid index has been entered. +** 1a1. RMS displays an error that says the index provided is invalid. ++ +Use case ends. + +[discrete] +=== Use case: Edit employee +Actor: Restaurant Manager + +Preconditions: "List employee" use case was used before this use case. + +Guarantees: + +* Details of an employee is edited. + +*MSS* + +1. User requests to edit an existing employee at the specified index with the optional parameters provided. +2. RMS edits the employee at the specified index with the optional parameters that were provided. ++ +Use case ends. + +*Extensions* + +* 1a. RMS detects that an invalid command format has been entered. +** 1a1. RMS displays an error that says the command format is invalid. +** 1a2. RMS displays information on the proper use of the command along with an example. ++ +Use case ends. + +* 1b. RMS detects that an invalid index has been entered. +** 1b1. RMS displays an error that says the index provided is invalid. ++ +Use case ends. + +[discrete] +=== Use case: Employee clock in +Actor: Employee + +Preconditions: User has started up the system. + +Guarantees: + +* Details of the time that the employee clocks in will be added to the system. + +*MSS* + +1. User requests to record his/her clock in details into the system with his/her name. +2. RMS clocks in the user's attendance based on the current date and time using his/her specified name. ++ +Use case ends. + +*Extensions* + +* 1a. RMS detects that the user did not use the clock out command yet. +** 1a1. RMS displays an error that says the user has to clock out before clocking in. +** 1a2. RMS displays information on the proper use of the command along with an example. ++ +Use case ends. + +[discrete] +=== Use case: Employee clock out +Actor: Employee + +Preconditions: User has started up the system. + +Guarantees: + +* Details of the time that the employee clocks out will be added to the system. + +*MSS* + +1. User requests to record his/her clock out details into the system with his/her name. +2. RMS clocks in the user's attendance based on the current date and time using his/her specified name. ++ +Use case ends. + +*Extensions* + +* 1a. RMS detects that the user did not use the clock in command yet. +** 1a1. RMS displays an error that says the user has to clock in before clocking out. +** 1a2. RMS displays information on the proper use of the command along with an example. ++ +Use case ends. +//end::employeeusecase[] + + +// tag::MemberUseCases[] +[discrete] +=== Use case: List member +System: Restaurant Management System (RMS) + +Use Case: List member + +Actor: Restaurant Manager / Cashier + +Preconditions: User has started up the system. + +Guarantees: + +* List of members will be displayed. + +*MSS* + +1. User chooses to view the entire list of members. +2. RMS displays a list of the employees. ++ +Use case ends. + +[discrete] +=== Use case: Add member +Actor: Restaurant Manager / Cashier + +Preconditions: User has started up the system. + +Guarantees: + +* New member is added to the RMS. -. Should work on any <> as long as it has Java 9 or higher installed. -. Should be able to hold up to 1000 persons. -. Should come with automated unit tests and open source code. -. Should favor DOS style commands over Unix-style commands. +*MSS* + +1. User requests to add a new member with the required parameters. +2. RMS adds a new member to the list with the parameters that were specified by the User. ++ +Use case ends. + +*Extensions* + + +* 1a. RMS detects that an invalid command format has been entered. +** 1a1. RMS displays an error that says the command format is invalid. +** 1a2. RMS displays information on the proper use of the command along with an example. ++ +Use case ends. + +* 1b. RMS detects that there is an attempt to add a duplicate member. +** 1b1. RMS displays an error that says the member already exists in Rms. ++ +Use case ends. + +[discrete] +=== Use case: Delete member +Actor: Restaurant Manager / Cashier + +Preconditions: User has started up the system. + +Guarantees: + +* Existing member is deleted from the RMS. + +*MSS* + +1. User requests to delete an existing member in the list of members. +2. RMS deletes the existing member at the index that was specified by the User. ++ +Use case ends. + +*Extensions* + + +* 1a. RMS detects that an invalid command format has been entered. +** 1a1. RMS displays an error that says the command format is invalid. +** 1a2. RMS displays information on the proper use of the command along with an example. ++ +Use case ends. + +* 1b. RMS detects that there is an attempt to delete a member that does not exist in the RMS. +** 1b1. RMS displays an error that says the member index provided is invalid. ++ +Use case ends. +// end::MemberUseCases[] + + +// tag::statisticsusecase[] +[discrete] + +=== Use case: View employee statistics +Actor: Manager + +*MSS* + +1. User chooses to view the employee statistics +2. RMS shows overview employee statistics + +Use case ends. + +[discrete] +=== Use case: View member statistics +Actor: Manager + +*MSS* + +1. User chooses to view the member statistics +2. RMS shows overview member statistics + +Use case ends. + +[discrete] +=== Use case: View menu statistics +Actor: Manager + +*MSS* + +1. User chooses to view the menu statistics +2. RMS shows overview menu statistics + +Use case ends. + +[discrete] +=== Use case: View order statistics +Actor: Manager + +*MSS* + +1. User chooses to view the order statistics +2. RMS shows overview order statistics + +Use case ends. + +* 1a. RMS detects that there are currently no employees recorded. +** 1a1. RMS shows a message that says there are currently no employees recorded. ++ +Use case ends. + +// end::statisticsusecase[] + +_{More to be added}_ [appendix] -== Glossary +== Non Functional Requirements + +. Quality requirement: The system should be efficient enough for restaurants which want to add a management system for their orders. The orders will be processed in less than 5 clicks. +. Performance requirements: The system should respond within three seconds as speed and efficiency is important for a restaurant. +. Project scope: The product is not developed for the customers of the restaurants to use. -[[mainstream-os]] Mainstream OS:: -Windows, Linux, Unix, OS-X +_{More to be added}_ -[[private-contact-detail]] Private contact detail:: -A contact detail that is not meant to be shared with others. +//[appendix] +//== Glossary +// +//[[mainstream-os]] Mainstream OS:: +//Windows, Linux, Unix, OS-X +// +//[[private-contact-detail]] Private contact detail:: +//A contact detail that is not meant to be shared with others +// +//[appendix] +//== Product Survey +// +//*Product Name* +// +//Author: ... +// +//Pros: +// +//* ... +//* ... +// +//Cons: +// +//* ... +//* ... +// +//[appendix] +//== Instructions for Manual Testing +// +//Given below are instructions to test the app manually. +// +//[NOTE] +//These instructions only provide a starting point for testers to work on; testers are expected to do more _exploratory_ testing. +// +//=== Launch and Shutdown +// +//. Initial launch +// +//.. Download the jar file and copy into an empty folder +//.. Double-click the jar file + +// Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. +// +//. Saving window preferences +// +//.. Resize the window to an optimum size. Move the window to a different location. Close the window. +//.. Re-launch the app by double-clicking the jar file. + +// Expected: The most recent window size and location is retained. +// +//_{ more test cases ... }_ +// +//=== Deleting a person +// +//. Deleting a person while all persons are listed +// +//.. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +//.. Test case: `delete 1` + +// Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. +//.. Test case: `delete 0` + +// Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. +//.. Other incorrect delete commands to try: `delete`, `delete x` (where x is larger than the list size) _{give more}_ + +// Expected: Similar to previous. +// +//_{ more test cases ... }_ +// +//=== Saving data +// +//. Dealing with missing/corrupted data files +// +//.. _{explain how to simulate a missing/corrupted file and the expected behavior}_ +// +//_{ more test cases ... }_ diff --git a/docs/Diagrams.pptx b/docs/Diagrams.pptx index acf8370f1..24617dc2c 100644 Binary files a/docs/Diagrams.pptx and b/docs/Diagrams.pptx differ diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 7e4858342..ae8ca38e3 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -1,150 +1,523 @@ -= User Guide += Restaurant Management System (RMS) - User Guide :site-section: UserGuide +:toc: +:toc-title: +:toc-placement: preamble +:sectnums: :imagesDir: images :stylesDir: stylesheets +:xrefstyle: full +:experimental: ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] +:repoURL: https://github.com/CS2113-AY1819S1-F09-2/main/tree/master -This product is not meant for end-users and therefore there is no user-friendly installer. -Please refer to the <> section to learn how to set up the project. +By: `CS2113-AY1819S1-F09-2` Since: `Aug 2018` Licence: `MIT` -== Starting the program +== Introduction -. Find the project pane (usually located at the left side) -. Open up `src/seedu.addressbook` folder -. Right click on `Main` -. Click `Run Main.main()` -. The GUI should appear in a few seconds +The Restaurant Management System (RMS) provides restaurants with a more efficient way to manage their operations. It has a wide variety of features which cater to different roles in a restaurant. In line with Singapore's "Smart Nation" movement, the RMS aims to digitise the restaurant business and make day-to-day operations smoother and simpler. Jump to <> to get started. + +== Quick Start +. Ensure you have Java version `9` or later installed in your Computer. +. Download the latest `Rms.jar` https://github.com/CS2113-AY1819S1-F09-2/main/releases[here] +. Copy the file to the folder you want to use as the home folder for your Restaurant Management System. +. Double-click the file to start the application. The GUI should appear in a few seconds. + image::Ui.png[] ++ +. Type the command in the command box and press kbd:[Enter] to execute it. +. Some example commands you can try: + +* *`listmenu`* : lists all menu items +* **`addmember`**`John Doe` : adds a member named `John Doe` to the Restaurant Management System. +* *`listorder`* : lists all orders in the order list. +* **`deleteorder`**`3` : deletes the 3rd order shown in the last shown order list +* *`exit`* : exits the app +. Refer to <> for details of each command. -== Viewing help : `help` +== Important Terminology +. _A customer is anyone who wants to place an order._ +. _An employee is a person that works at the restaurant._ +. _A member in the RMS is a registered customer who enjoys more benefits than an unregistered customer._ +. _A dish is a food item on the menu._ -Format: `help` +[[Features]] +== Features -[TIP] ==== -Help is also shown if you enter an incorrect command e.g. `abcd` + +*Command Format* + +* Words in `UPPER_CASE` are the parameters to be supplied by the user e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. +* Items in square brackets are optional e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. ==== -== Adding a person: `add` +// tag::menucommands[] +=== Menu -Adds a person to the address book. + -Format: `add NAME [p]p/PHONE_NUMBER [p]e/EMAIL [p]a/ADDRESS [t/TAG]...` +==== Show Main Menu -**** -Words in `UPPER_CASE` are the parameters, items in `SQUARE_BRACKETS` are optional, -items with `...` after them can have multiple instances. Order of parameters are fixed. +Shows a list of menu categories available. + -Put a `p` before the phone / email / address prefixes to mark it as `private`. `private` details can only -be seen using the `viewall` command. +Format: `showmainmenu` -Persons can have any number of tags (including 0) -**** +==== List Menu -Examples: +Shows a list of all the menu items that exists in the restaurant's menu database. + -* `add John Doe p/98765432 e/johnd@gmail.com a/John street, block 123, #01-01` -* `add Betsy Crowe pp/1234567 e/betsycrowe@gmail.com pa/Newgate Prison t/criminal t/friend` +Each menu item include details of: menu item, price, type and any relevant tags. + -== Listing all persons : `list` +Format: `listmenu` -Shows a list of all persons in the address book. + -Format: `list` +* List the current menu items in the menu -== Finding all persons containing any keyword in their name: `find` +==== List Menu According to Category -Finds persons whose names contain any of the given keywords. + -Format: `find KEYWORD [MORE_KEYWORDS]` +Shows a list of the menu items of a particular category type. The category types are shown in the Main Menu (e.g main, sides). + -[NOTE] -==== -The search is case sensitive, the order of the keywords does not matter, only the name is searched, -and persons matching at least one keyword will be returned (i.e. `OR` search). -==== +Each menu item include details of: menu item, price, type and any relevant tags. + + +Format: `listmenutype TYPE` + +* List the current menu items of a particular category in the menu + +Example: + +`listmenutype main` + +System shows all the main dishes in the menu. + +==== Add Menu + +Add a new item to the menu. + +Format: `addmenu NAME p/PRICE type/TYPE [t/TAG]...` + +* Adds the new menu item into the menu list +* Names must be unique, cannot be longer than 30 alphanumeric characters and spaces. It must contain minimum one character. +* Note that duplicate menu name is still detected even if the user tries to key in the same name with extra spaces at the *end* of the menu name (eg. 'Cheese Burger' and 'Cheese Burger ') +* Price must follow the formal $A.BC or $A where A is a number of 1-3 digits and B and C are 1 digit each. +* Type should only be of the following categories: main, sides, beverage, dessert, others, set meal +* A new food item can have multiple tags (including 0) + +Example: + +`addmenu Cheese Fries p/$3.50 type/sides t/newItem t/seasonalSpecial` + +System adds Cheese Fries to the existing menu. + +==== Delete Menu + +Delete an item on the menu + +Format: `delmenu INDEX` + +Preconditions: The user must perform 'List Menu' , 'List Menu According to Category' or 'Find Menu' use cases first before performing 'Delete Menu' + +* Deletes the menu item in that particular index +* INDEX should be less than or equal to the total number of food items stated at the end of the list + +Example: + +`listmenutype main` + +`delmenu 1` + +System deletes menu item in index 1 from the displayed menu of main dishes. + +`listmenutype main` + +`delmenu 1` + +System deletes the menu item in index 1 from the displayed menu of main dishes. + +`findmenu Cheese Fries` + +`delmenu 3` + +System deletes Cheese Fries in index 3 from the displayed menu of items found with `Cheese Fries` keyword. + +==== Find Menu + +Finds the menu item(s) related to the keywords typed in by the user. + +Format: `findmenu KEYWORD [MORE KEYWORDS]` + +* A list of menu items with names related to the keyword(s) will be displayed. +* The search is case insensitive eg. `cheese` will match `Cheese`. +* Order of keywords do not matter. +* Only full words will be matched. eg `burg` will not match `Burger` +* Menu items matching atleast one key word will be returned. eg '`cheese` will return `Cheese Fries`, `Cheese Burger`. + +Example: + +`findmenu Burger Coke Fries` + +Returns Double Cheese burger, Veggie Burger, Coke, coke zero, Fries, Curly Fries + +==== Menu Recommendations + +Displays the best selling items of each category, if they are sold, as the recommended items of the restaurant. + +Format: `recommendations` + +* For a particular category, if no menu items are sold, items from that category will not be displayed under recommendations. +* Atleast one item from each category must be sold in order for recommendations to reflect items from that category. + +Example: + +`recommendations` + +Returns Main: Double Cheese Burger, Sides: French Fries, Beverage: Sprite + +// end::menucommands[] + +[[MemberCommands]] +// tag::membercommands[] +=== Members + +==== Add Member + +Add a new member to the RMS + +Format: `addmember NAME e/EMAIL` + +* Member name cannot be longer than 30 alphanumeric characters and spaces. +* Member name must have at least 1 character. +* Email should be 2 alphanumeric/period strings that are no longer than 20 characters separated by '@' + +Example: + +`addmember kang ming e/kangming@rms.com` + +Returns: New member added: kang ming | Email: kangming@rms.com | Available Points: 0 | Total Points: 0 | Tier: Bronze | Date: Mon Oct 20 22:12:07 SRET 2018 + +==== List Member + +Shows a list of all the members in the RMS. Displays the NAME, POINTS, DATE JOINED, MEMBERSHIP TIER of each member. + +Format: `listmember` + +Example: + +`listmember` + +==== Delete Member + +Delete membership details of a member in the RMS + +Format: `delmember INDEX` + +* Deletes the member at the specified `INDEX`. +* The index refers to the index number shown in the displayed member list. +* The index must be a positive integer `1, 2, 3 ...` + +Example: + +`listmember` + +`delmember 1` + +Returns: Deleted member: kang ming | Email: kangming@rms.com | Available Points: 0 | Total Points: 0 | Tier: Bronze | Date: Mon Oct 20 22:19:32 SRET 2018 + +// end::membercommands[] + +// tag::employeecommands[] +=== Employees +==== List Employees + +Show a list of all the employees on the RMS. + +Format: `listemp` + +==== Add Employee + +Adds a new employee to the RMS. + +Format: `addemp NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS pos/POSITION` + +* Employees with exact same names are not allowed irregardless of it being in upper or lower case. +* Employee name cannot be longer than 30 alphanumeric characters and spaces. +* Employee name must have at least 1 character. +* Phone numbers must be an 8 digit number. +* Email should be 2 alphanumeric/period strings that are no longer than 20 characters separated by '@' + +Example: + + `addemp John Doe p/91234567 e/Example2018@rms.com a/Clementi Ave 2, Blk 543 #13-12 pos/Cashier` + + Adds a new employee with the specified details. + +==== Edit Employee + +Edit details of an employee in the RMS. + +Format: `editemp INDEX [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [pos/POSITION]` + +* Edits the employee at the specified `INDEX`. +* Parameters in square brackets are optional. +* At least one of the optional parameters must be provided. +* Existing values will be updated to the input values. +* The index must be a positive integer `1, 2, 3 ...` +* Phone numbers must be an 8 digit number. +* Email should be 2 alphanumeric/period strings that are no longer than 20 characters separated by '@' + +Example: + + +* `listemp` + +* `editemp 1 pos/Cashier` + +Edits the position of the 1st employee in the employee list to Cashier. + +==== Delete Employee + +Deletes an employee from the RMS. + +Format: `delemp INDEX` + +* Deletes the employee at the specified `INDEX`. +* The index refers to the index number shown in the displayed employee list. +* The index must be a positive integer `1, 2, 3 ...` + +Example: + + +* `listemp` + +* `delemp 1` + +Deletes the 1st employee in the employee list. + +==== Clock In + +Clocks in attendance for the specified employee based on the current date and time + +Format: `clockin NAME` + +* Employee must either have been newly created or +have used the "Clock Out" command, otherwise the system will request the user to use the "Clock Out" command first. + +Example: + +`clockin John Doe` + +Clocks in for the employee John Doe. + + +==== Clock Out + +Clocks out for the specified employee based on the current date and time. + +Format: `clockout NAME` + +* Employee must have used the "Clock In" command, otherwise the system will request the user to use the "Clock In" command first. + +Example: + +`clockout John Doe` + +Clocks out for the employee John Doe. + +// end::employeecommands[] + +// tag::orderuserguide[] +=== Order + +==== Add Order + +image::AddOrderInstruction.png[] + +To add a new order to the order list, a draft must be completed before adding it to the order list. + + +To do so, dishes must be added into the draft before it can be added into the order list after +the draft is confirmed. + +If the customer is a registered member, the customer's information can be found in the member list and can be added +into the order draft to use and accumulate member points +once the order is confirmed. + +* *Display Draft And Instruction For Adding New Order* + ++ +Displays the current draft, steps to add an order and the list of order draft commands that can be used + ++ +Format: `addorder` + +* *Edit A Dish Item Of The Draft* + ++ +By picking dishes from the last shown menu and state the quantity to be added, +te dish items can be added, deleted or edited in the draft order. + ++ + ** If the quantity is set to 0, then the dish will be removed from the draft. + ** If the quantity is a positive integer, then the quantity of the dish will be set to quantity. ++ +Format: `draftdish INDEX q/QUANTITY [INDEX q/QUANTITY]...` + +** There must be no duplicated index in the input command. +** The indexes refer to the index numbers shown in the last shown menu list. +** The indexes must be non-negative integers. +** The quantities must be non-negative integers of 1-3 digits. + +* *Select A Member As The Customer Of The Draft* + +_This step is only necessary if the customer is a member._ ++ +Select a member from the last shown member list to be the customer of the draft order. + ++ +Format: `draftcustomer INDEX` ++ + ** The index refers to the index number shown in the last shown member list. + +* *Redeems member points in the draft order* + +_This step can only be done after the following conditions have been met:_ ++ +. _A member has been added to the draft order._ ++ +. _At least one dish has been added to the draft order._ ++ +Assign member points to be redeemed as discount for the draft order. ++ +Format: `draftpoints POINTS` ++ + ** The points redeemed must be a non-negative integer. + ** The limit to the redeemable points is set as the smaller value between the amount of points the customer currently + has and the maximum amount of points can be deduced from the order such that the final price is not less than zero. + ** If the assigned redeemed points is more than the limit, then it will be set back to the limit. + +* *Clear Draft* + ++ +Clear all details of the draft order. + ++ +Format: `cleardraft` + +* *ConfirmDraft* + ++ +Confirm the draft and add it to the order list. + ++ +Once it is confirmed, the ordered dish items in the +newly added order can no longer be changed. + ++ +Format: `confirmdraft` + +==== Delete Order + +Delete an order. + + +The deleted order is specified by the index of that order on the last displayed order list. + + +Format: `deleteorder INDEX` + +* The index refers to the index number shown in the last shown order list. + +==== Clear Order + +Clear the entire order list. + + +Format: `clearorder` + +==== List Orders + +Shows a list of all the current orders. + +Each order include details of: customer, ordered time, total price and the list of dish items and quantities ordered + +Format: `listorder`. +// end::orderuserguide[] + + +// tag::statisticscommands[] +=== Statistics + +==== View Employee Statistics + +Displays the employee statistics overview + +Overview consists of the number of employees in the system, +current on duty employees and all the employees' recent 3 activity + +Format: statsemp + +Examples: + +`statsemp` + +==== View Member Statistics + +Displays the member statistics overview + +Overview consists of the number of new member signups on the current day, month and year. +It also includes the number of members in each member tier. + +Format: statsmember + +Examples: + +`statsmember` + +==== View Menu Statistics + +Displays the menu statistics overview without any parameters or with optional parameters `f/` or `t/` to set date range for calculation of statistics + +Overview consists of the number of sales for all the menu items in the system. Including menu items that was deleted but still exists in an order. + +Bestsellers and least popular items are also displayed. +Format: statsmenu [f/MMDDYYY] [t/MMDDYYY] + +Examples: + + +* `statsmenu` +* `statsmenu f/01122017 t/31122017` +* `statsmenu f/01012017` +* `statsmenu f/31122018` + +==== View Order Statistics + +Displays the order statistics overview + +Overview consists of the number of sales as well as the revenue for the current day, month and year + +Past 12 monthly revenue are also displayed here. +Format: statsorder + +Examples: + +`statsorder` + +// end::statisticscommands[] + +=== Saving the data + +Data from the Restaurant Management System are saved in the hard disk automatically after any command that changes the data. + +There is no need to save manually. + +== FAQ + +*Q*: How do I transfer my data to another Computer? + +*A*: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous Restaurant Management System folder. + +== Command Summary + +// tag::menucommandsummary[] + + +=== Menu + +*Show Main Menu Page* : `showmainmenu` + +*Find Menu* : `findmenu KEYWORD [MORE KEYWORDS]` + +*Add Item* : `addmenu NAME p/PRICE type/TYPE [t/TAG]` + +*Delete Item* : `delmenu INDEX` + +*List Menu* : `listmenu` + +*List Menu According to Category* : `listmenubytype TYPE` + +*See Recommended Menu Items* : `recommendations` +// end::menucommandsummary[] -Examples: +=== Employees -* `find John` + -Returns `John Doe` but not `john`. +*List Emplyees* : `listemp` -* `find Betsy Tim John` + -Returns Any person having names `Betsy`, `Tim`, or `John`. +*Add Employee* : `addemp NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS pos/POSITION` -== Deleting a person : `delete` +*Edit Employee* : `editemp INDEX [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [pos/POSITION]` -Deletes the specified person from the address book. Irreversible. + -Format: `delete INDEX` +*Delete Employee* : `delemp INDEX` -**** -Deletes the person at the specified `INDEX`. -The index refers to the index number shown in the most recent listing. -**** +*Clock In* : `clockin NAME` -Examples: +*Clock Out* : `clockout NAME` -* `list` + -`delete 2` + -Deletes the 2nd person in the address book. +=== Members -* `find Betsy` + -`delete 1` + -Deletes the 1st person in the results of the `find` command. +*Add Member* : `addmember NAME e/EMAIL` -== View non-private details of a person : `view` +*List Member* : `listmember` -Displays the non-private details of the specified person. + -Format: `view INDEX` +*Delete Member* : `delmember INDEX` -**** -Views the person at the specified `INDEX`. -The index refers to the index number shown in the most recent listing. -**** -Examples: +// tag::ordercommandsummary[] +=== Order -* `list` + -`view 2` + -Views the 2nd person in the address book. +* *Add Order:* -* `find Betsy` + -`view 1` + -Views the 1st person in the results of the `find` command. +** *Display Add Order Commands*: `addorder` -== View all details of a person : `viewall` +** *Edit Draft Dishes*: `draftdish INDEX q/QUANTITY` + +e.g. `draftdish 4 q/1` -Displays all details (including private details) of the specified person. + -Format: `viewall INDEX` +** *Select Draft Customer*: `draftcustomer INDEX` + +e.g. `draftcustomer 2` -**** -Views all details of the person at the specified `INDEX`. -The index refers to the index number shown in the most recent listing. -**** +** *Redeem Member Points*: `draftpoints POINTS` + +e.g. `draftpoints 100` -Examples: +** *Clear The Draft*: `cleardraft` -* `list` + -`viewall 2` + -Views all details of the 2nd person in the address book. +** *Confirm Draft*: `confirmdraft` -* `find Betsy` + -`viewall 1` + -Views all details of the 1st person in the results of the `find` command. +* *Delete Order* : `deleteorder INDEX` + +e.g. `deleteorder 3` -== Clearing all entries : `clear` +* *Clear Order List* : `clearorder` -Clears all entries from the address book. + -Format: `clear` +* *List All Orders* : `listorder` +// end::ordercommandsummary[] -== Exiting the program : `exit` +=== Statistics -Exits the program. + -Format: `exit` +*View Employee Statistics* : `statsemp` -== Saving the data +*View Member Statistics* : `statsmember` -Address book data are saved in the hard disk automatically after any command that changes the data. +*View Menu Statistics* : `statsmenu [f/ddmmyyyy] [t/ddmmyyyy]` -There is no need to save manually. Address book data are saved in a file called `addressbook.txt` in the project root folder. +*View Order Statistics* : `statsorder` diff --git a/docs/images/AddMember.png b/docs/images/AddMember.png new file mode 100644 index 000000000..9e95020a7 Binary files /dev/null and b/docs/images/AddMember.png differ diff --git a/docs/images/AddOrderInstruction.png b/docs/images/AddOrderInstruction.png new file mode 100644 index 000000000..4c3225282 Binary files /dev/null and b/docs/images/AddOrderInstruction.png differ diff --git a/docs/images/Architecture.png b/docs/images/Architecture.png new file mode 100644 index 000000000..149fb381b Binary files /dev/null and b/docs/images/Architecture.png differ diff --git a/docs/images/DataComponent.png b/docs/images/DataComponent.png new file mode 100644 index 000000000..00feda38b Binary files /dev/null and b/docs/images/DataComponent.png differ diff --git a/docs/images/ListOrder.png b/docs/images/ListOrder.png new file mode 100644 index 000000000..f75b0385b Binary files /dev/null and b/docs/images/ListOrder.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png new file mode 100644 index 000000000..fb890001f Binary files /dev/null and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/OrderAfterRedemption.png b/docs/images/OrderAfterRedemption.png new file mode 100644 index 000000000..58acc1949 Binary files /dev/null and b/docs/images/OrderAfterRedemption.png differ diff --git a/docs/images/OrderBeforeRedemption.png b/docs/images/OrderBeforeRedemption.png new file mode 100644 index 000000000..8761175b9 Binary files /dev/null and b/docs/images/OrderBeforeRedemption.png differ diff --git a/docs/images/PointsEarned.png b/docs/images/PointsEarned.png new file mode 100644 index 000000000..5367d53cf Binary files /dev/null and b/docs/images/PointsEarned.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png new file mode 100644 index 000000000..ac3da0f12 Binary files /dev/null and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5fa3fdad0..bf3e0bde3 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiComponentClassDiagram.png b/docs/images/UiComponentClassDiagram.png new file mode 100644 index 000000000..724cc7028 Binary files /dev/null and b/docs/images/UiComponentClassDiagram.png differ diff --git a/docs/images/angwm.png b/docs/images/angwm.png new file mode 100644 index 000000000..85ca17119 Binary files /dev/null and b/docs/images/angwm.png differ diff --git a/docs/images/homepage.png b/docs/images/homepage.png new file mode 100644 index 000000000..f64ade64f Binary files /dev/null and b/docs/images/homepage.png differ diff --git a/docs/images/kangmingtay.png b/docs/images/kangmingtay.png new file mode 100644 index 000000000..aba0c1a2a Binary files /dev/null and b/docs/images/kangmingtay.png differ diff --git a/docs/images/kianhong95.png b/docs/images/kianhong95.png new file mode 100644 index 000000000..d2f74e0a0 Binary files /dev/null and b/docs/images/kianhong95.png differ diff --git a/docs/images/px1099.png b/docs/images/px1099.png new file mode 100644 index 000000000..4f025e0af Binary files /dev/null and b/docs/images/px1099.png differ diff --git a/docs/images/salsabiltasnia.png b/docs/images/salsabiltasnia.png new file mode 100644 index 000000000..4cf940df0 Binary files /dev/null and b/docs/images/salsabiltasnia.png differ diff --git a/docs/images/statsemp.png b/docs/images/statsemp.png new file mode 100644 index 000000000..ea2b7b1f9 Binary files /dev/null and b/docs/images/statsemp.png differ diff --git a/docs/images/statsmember.png b/docs/images/statsmember.png new file mode 100644 index 000000000..57b199a9b Binary files /dev/null and b/docs/images/statsmember.png differ diff --git a/docs/images/statsmenu.png b/docs/images/statsmenu.png new file mode 100644 index 000000000..9e9171028 Binary files /dev/null and b/docs/images/statsmenu.png differ diff --git a/docs/images/statsorder.png b/docs/images/statsorder.png new file mode 100644 index 000000000..7eb3eba61 Binary files /dev/null and b/docs/images/statsorder.png differ diff --git a/docs/stylesheets/asciidoctor.css b/docs/stylesheets/asciidoctor.css index 36590bf34..5bd5f774c 100644 --- a/docs/stylesheets/asciidoctor.css +++ b/docs/stylesheets/asciidoctor.css @@ -378,7 +378,6 @@ p{margin-bottom:1.25rem} *{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important} a{color:inherit!important;text-decoration:underline!important} a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important} -a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em} abbr[title]:after{content:" (" attr(title) ")"} pre,blockquote,tr,img,object,svg{page-break-inside:avoid} thead{display:table-header-group} diff --git a/docs/stylesheets/gh-pages.css b/docs/stylesheets/gh-pages.css index 121cac388..1db0e18d7 100644 --- a/docs/stylesheets/gh-pages.css +++ b/docs/stylesheets/gh-pages.css @@ -40,7 +40,7 @@ h5, h5 > a.link, h6, h6 > a.link { - color: #e46c0a; + color: #0a9de4; } .subheader, diff --git a/docs/team/angwm.adoc b/docs/team/angwm.adoc new file mode 100644 index 000000000..723e1d2c2 --- /dev/null +++ b/docs/team/angwm.adoc @@ -0,0 +1,139 @@ += Ang Wei Ming - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: Restaurant Management System +image::homepage.png[] + +== Overview + +My name is Ang Wei Ming and I am a Year 2 student majoring in Information Security at National University of Singapore. Over the past 12 weeks I have been working on a project for the twinned module CS2113T/CS2101 with my team. The following is my portfolio for this project that we have completed. + +This portfolio serves to provide an insight on the Restaurant Management System (RMS) project such as the purpose of this project, as well as the various features that our team has developed. Also stated are the different roles of each team members in the project and more importantly the contributions that I have made to the code and documentations. + +=== Project Description + +As frequent customers of restaurants, our team realized that most restaurants around the world are using inefficient systems to manage their restaurants. This includes taking orders on papers, calculating daily revenues at the end of the day, or even having a manual clocking machine for employees starting or ending their shift. Even if a restaurant uses alternative systems to mitigate such issues, they are usually segregated, causing the restaurant manager to maintain different systems all at once. All these can affect the way the restaurant is being run and possibly affect the quality of food and service negatively. + +Therefore, our team decided to develop an All-in-One application called Restaurant Management System (RMS) that is able to handle the different processes within a running restaurant efficiently. + +Restaurant Management System (RMS) is a CLI^*^ based desktop application that can be used within a restaurant to perform various tasks such as listing menu items, order taking, as well as keeping track of members and employees. Restaurant managers will also be able to oversee the different processes more efficiently through a statistics feature. + +_* Command Line Interface. Refers to an user interface where an end user enters commands into the application by typing instead of clicking._ + +=== Features + +image::Ui.png[] + +==== Employee feature + +Manage employees working in the restaurant by adding their information into the application. Employees will have to use the application to clock in when the shift starts and clock out when it ends. + +==== Member feature + +Customers of the restaurant can opt to sign up as a member in the application and will be able to accumulate points that can be used to offset future orders made with the application. + +Members are also sorted into different tiers according to the points accumulated + +==== Menu feature + +Restaurant managers will use this feature to list new items on the menu or make adjustments to the menu such as pricing and this set of menu can then be used when taking orders. + +==== Order feature + +Cashiers can take customers' orders and this order information such as the sales revenue and the items ordered will be stored in the application. Additionally, orders can be tagged to a customer if he/she is a member. + +==== Statistics feature + +Able to display the statistics for the different features. + +* Employee statistics will show the attendance of their clocking and the current on duty personnel. + +image::statsemp.png[] + +* Member statistics shows the number of new members over a period of time and the number of members in each member tier. + +image::statsmember.png[] + +* Menu statistics tells us the sales of each menu items as well as the most and least popular items for each categories in the menu (e.g. mains or beverages). + +image::statsmenu.png[] + +* Order statistics displays the total number of orders and the revenue for day, month and year, along with the revenue for the past 12 months. + +image::statsorder.png[] + + +=== Team roles +* Ang Wei Ming +** In charge of the statistics feature. +* Lim Kian Hong +** In charge of the employee feature. +* Pham Quang Minh +** In charge of the order feature as well as code testing. +* Salsabil Tasnia +** In charge of the menu feature. +* Tay Kang Ming +** In charge of the member feature and the overall project coordination. + +== Summary of contributions + +|=== +|_This section describes in details the various contributions I made to the code base as well as the documentations of the project. Depending on the type of contribution, it is split into different sections._ +|=== + +=== Major Enhancements + +I added the statistics feature for all the different information stored in the application. + + +* `What it does`: As described in the features sections of this portfolio, the statistics feature allows restaurant managers to view the overview statistics of the menus, orders, members and employees +* `Justification`: This feature will be able to let the restaurant managers understand how the restaurant is currently performing and how much it is earning. It also allows the manager to keep track of their members and employees +* `Highlights`: Data displayed under the different statistics commands have to be easy to understand as well as useful to the restaurant managers. +It required thinking from a perspective of a restaurant manager to understand what kind of information he/she would want to see. +This implementation was challenging because apart from the thought process, I had to make use of the limited data the other features were able to provide. + +* `Credits`: https://github.com/nusCS2113-AY1819S1/addressbook-level3 + +* *Code contributed*: https://nuscs2113-ay1819s1.github.io/dashboard/#=undefined&search=angwm[Project Reposense Page] + +=== Other Contributions + +* `Enhancements to existing features`: +** Updated the GUI font: https://github.com/CS2113-AY1819S1-F09-2/main/pull/94[#94] +** Updated the GUI size: https://github.com/CS2113-AY1819S1-F09-2/main/pull/112[#112] +* `Code Integration` +** Helped to integrate my team member's codes during the start of the project when we worked without the Travis CI (Continuous Integration) tool +* `Documentation`: +** Addition of content to User Guide: https://github.com/CS2113-AY1819S1-F09-2/main/pull/100[#100], https://github.com/CS2113-AY1819S1-F09-2/main/pull/122[#122], https://github.com/CS2113-AY1819S1-F09-2/main/pull/213[#213] +** Addition of content to Developer Guide: https://github.com/CS2113-AY1819S1-F09-2/main/pull/8[#8], https://github.com/CS2113-AY1819S1-F09-2/main/pull/67[#67], https://github.com/CS2113-AY1819S1-F09-2/main/pull/114[#114], https://github.com/CS2113-AY1819S1-F09-2/main/pull/213[#213] +* `Community`: +** PRs reviewed (with non-trivial review comments): https://github.com/CS2113-AY1819S1-F09-2/main/pull/107[#107], https://github.com/CS2113-AY1819S1-F09-2/main/pull/113[#113], https://github.com/CS2113-AY1819S1-F09-2/main/pull/117[#117], https://github.com/CS2113-AY1819S1-F09-2/main/pull/120[#120], https://github.com/CS2113-AY1819S1-F09-2/main/pull/138[#138], https://github.com/CS2113-AY1819S1-F09-2/main/pull/202[#202], https://github.com/CS2113-AY1819S1-F09-2/main/pull/203[#203], https://github.com/CS2113-AY1819S1-F09-2/main/pull/205[#205] +//*** Reported bugs and suggestions for other teams in the class (examples: https://github.com[1], https://github.com[2], https://github.com[3]) +//** Tools: +//*** Integrated a third party library (Natty) to the project (https://github.com[#42]) +//*** Integrated a new Github plugin (CircleCI) to the team repo + + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=statisticscommands] + + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=storage] + +include::../DeveloperGuide.adoc[tag=statisticsimplementation] + +include::../DeveloperGuide.adoc[tag=statisticsusecase] diff --git a/docs/team/kangmingtay.adoc b/docs/team/kangmingtay.adoc new file mode 100644 index 000000000..014f9685f --- /dev/null +++ b/docs/team/kangmingtay.adoc @@ -0,0 +1,119 @@ += Kang Ming - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: Restaurant Management System + +--- +== About Myself + +Hello, my name is Kang Ming and I am a Year 2 Computer Engineering student at the National University of Singapore (NUS). The purpose of this page is to showcase and document my contributions to a recent software engineering project that I have been working on. The next section will provide a brief overview of what my project is about. + +== Overview of the Restaurant Management System + + +Restaurant Management System (RMS) is a restaurant management solution that helps restaurants make more money, deliver great customer experience and bring honest and meaningful statistics to the table. The RMS was designed to improve the efficiency of completing mundane processes such as taking orders and counting the cash so that you can focus on the important things that matter - serving your customers with great food. + + +This application is created by my team, which comprises of 5 people including myself, for the NUS module CS2113T titled, Software Engineering & Object-Oriented Programming. The project lasted for around 10 weeks and we were expected to work with an existing sample codebase given by the module. The objective was to innovate and develop the existing codebase into a desktop application for a specific group of users - restaurant owners. + +The workflow of the RMS closely resembles that of a Scrum-Agile framework. At the beginning of the project, we started off with an initial architectural modeling. The project required us to release a working version of our product almost every fortnightly. Within the team, we set weekly milestones and meetings to ensure that everyone is always on track with the development of the RMS. My role in the team was to be the team leader and I was responsible for the overall project coordination. Throughout this development cycle, I have made various contributions to the team, from creating new features, writing tests and documentation and ensuring that each release is functional. + +Below shows a screenshot of the simple command-line-interface of the RMS and some of the main commands to get started: + +image::Ui.png[] + +_If you wish to experience a more hands-on approach to use the RMS, please key in `help` upon running the application to get the full list of commands._ + +_If you are looking for a detailed tutorial on how to use the RMS, please refer to our https://github.com/CS2113-AY1819S1-F09-2/main/tree/master/docs/UserGuide.adoc[User Guide]._ + + +_If you are looking for the detailed architecture and design of the RMS or wish to contribute code to this application, please refer to our https://github.com/CS2113-AY1819S1-F09-2/main/tree/master/docs/DeveloperGuide.adoc[Developer Guide]_ + + +== Summary of contributions + +|=== +|_Given below are various code contributions I have made. They showcase my coding ability as well as my ability to work in a team effectively._ + +|=== + +* *Major enhancement*: Created a membership platform for the RMS +** `What it does`: The membership platform allows the restaurant to keep track of its existing members and accumulate their membership points. It also allows the restaurant manager / cashier to list, add and delete members from the system. +** `Justification`: This feature improves the product significantly because it is an innovative way for restaurants to retain their customers. +** `Highlights`: It is essential that the membership system is well integrated together with the order system. This is to ensure that when an order is made by an existing member, the member's membership points will correspondingly be updated. Also, the commands implemented should be easy to understand and use for a non-tech savvy audience. It is important that the invalid command formats are properly taken care of in a way that it guides the user with examples on how to properly use the platform. +** `Credits`: https://github.com/nusCS2113-AY1819S1/addressbook-level3 +** Below shows a series of images that are meant to aid the understanding of the features that I have developed: + +image::AddMember.png[] + +|=== +Firstly, the Restaurant Manager / Cashier can easily add a new customer as a member of the restaurant to the RMS easily. +|=== + +image::ListOrder.png[] + +|=== + Next, when a member's order is confirmed , the RMS will display the amount of points earned from the order. This allows the Cashier to inform the member how many points he/she has earned. +|=== + +image::OrderBeforeRedemption.png[] + +|=== + When a member places an order, the RMS will display the amount of points that can be redeemed. +|=== + +image::OrderAfterRedemption.png[] + +|=== + The Cashier can then simply enter the amount of points that the member wishes to redeem. The redeemed points will then translate into a discount for the final price that has to be paid. For illustration purposes, a 100 points is equivalent to $1. +|=== + +image::PointsEarned.png[] + +|=== + Regardless of whether the member decides to redeem points, the member will earn points based on the final price of the order. For illustration purposes, $1 is equivalent to 10 points. +|=== + +* *Code contributed*: https://nuscs2113-ay1819s1.github.io/dashboard/#=undefined&search=kangmingtay + +** Project Management: +*** Managed releases v1.1 - v1.3 (3 releases) on GitHub + +** Membership Platform Enhancement: +*** Addition of membership platform: https://github.com/CS2113-AY1819S1-F09-2/main/pull/32[#32], https://github.com/CS2113-AY1819S1-F09-2/main/pull/69[#69], https://github.com/CS2113-AY1819S1-F09-2/main/pull/86[#86], https://github.com/CS2113-AY1819S1-F09-2/main/pull/99[#99], https://github.com/CS2113-AY1819S1-F09-2/main/pull/125[#125], https://github.com/CS2113-AY1819S1-F09-2/main/pull/177[#177] + +** Tests for membership features: +*** Addition of tests for membership feature: https://github.com/CS2113-AY1819S1-F09-2/main/pull/70[#70], https://github.com/CS2113-AY1819S1-F09-2/main/pull/200[#200], https://github.com/CS2113-AY1819S1-F09-2/main/pull/201[#201] + +** Documentation: +*** Addition of content to User Guide: https://github.com/CS2113-AY1819S1-F09-2/main/pull/3[#3], https://github.com/CS2113-AY1819S1-F09-2/main/pull/4[#4], https://github.com/CS2113-AY1819S1-F09-2/main/pull/74[#74], https://github.com/CS2113-AY1819S1-F09-2/main/pull/204[#204] + +*** Addition of content to Developer Guide: https://github.com/CS2113-AY1819S1-F09-2/main/pull/85[#85] + +** Community: +*** PRs reviewed (with non-trivial review comments): https://github.com/CS2113-AY1819S1-F09-2/main/pull/72[#72], https://github.com/CS2113-AY1819S1-F09-2/main/pull/116[#116] + +** Tools: +*** Set up continuous integration using Travis. +*** Set up auto-publishing of documentation using Travis to deploy docs to gh-pages. + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=membercommands] + + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=architecture] +include::../DeveloperGuide.adoc[tag=MemberUseCases] +include::../DeveloperGuide.adoc[tag=draftpointsimplementation] + diff --git a/docs/team/kianhong95.adoc b/docs/team/kianhong95.adoc new file mode 100644 index 000000000..571481ebd --- /dev/null +++ b/docs/team/kianhong95.adoc @@ -0,0 +1,78 @@ += Kian Hong - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: Restaurant Management System + +--- + +== Introduction + +The purpose of this portfolio is to detail the contributions that I have made throughout the time that me and my team +have spent on this project. + +Our team was formed through our CS2113T module which we were taking together. The project that we have done is an idea +that we have come up with as a team, where we aim to align our project to meet Singapore's goal of cultivating a smart +nation. Not only that, the project has aspects of sustainability that would allow future developers that would like to +adopt our project, to morph existing features or add new features to meet the ever changing needs of users, be it from +the food industry or other industries. + +Restaurant Management System is an application that can be run on a desktop, +which helps restaurants run more smoothly and efficiently by turning everyday processes and tasks that needs to be +carried out by restaurants into a paperless and digital format. + +The main features of the Restaurant Management System are: + +* Menu management +* Order management +* Employee management +* Member management +* Statistics + +== Summary of contributions +|=== +|_Given below are the contributions that I have made for the project._ +|=== + +* *Major enhancement*: Added employee management features. +** *What it does*: Allows restaurant managers to manage their employees through the system. +The system stores details of the employees +where the managers are able to view, add, edit and delete the details of the employees that are available in the system. + Additionally, employees of the restaurant can clock in and out their attendance using the system, + where details of their attendance would be stored in the system. +** Justification: This feature improves the product significantly as restaurant managers using the system +would be able to manage his/her employees more efficiently. +** Highlights: Needed to ensure that the commands implemented would be easy to understand and use for a non-tech savvy audience. +The commands had to properly take care of formatting errors of commands that could be entered by such users and +display examples on how to properly use the command that they are trying to use that can guide them when such +formatting errors are encountered. +** Credits: https://github.com/nusCS2113-AY1819S1/addressbook-level3 + +* *Code contributed*: https://nuscs2113-ay1819s1.github.io/dashboard/#=undefined&search=kianhong95&sort=displayName&since=2018-09-12&until=2018-11-04&timeframe=day&reverse=false&repoSort=true[Link to code contributed] + +** Documentation: +*** Addition of content to User Guide: https://github.com/CS2113-AY1819S1-F09-2/main/pull/9[#9], https://github.com/CS2113-AY1819S1-F09-2/main/pull/82[#82], https://github.com/CS2113-AY1819S1-F09-2/main/pull/118[#118] +*** Addition of content to Developer Guide: https://github.com/CS2113-AY1819S1-F09-2/main/pull/91[#91], https://github.com/CS2113-AY1819S1-F09-2/main/pull/120[#120] +** Community: +*** PRs reviewed (with non-trivial review comments): https://github.com/CS2113-AY1819S1-F09-2/main/pull/88[#88], https://github.com/CS2113-AY1819S1-F09-2/main/pull/99[#99], https://github.com/CS2113-AY1819S1-F09-2/main/pull/100[#100] + +== Contributions to the User Guide + + +|=== +|_ Given below are the additions that I have made to the User Guide for the project. They demonstrate my capability to craft documentation that is targeted at end-users._ +|=== + +include::../UserGuide.adoc[tag=employeecommands] + + +== Contributions to the Developer Guide + +|=== +|_Given below are sections that I have added to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=UI] + +include::../DeveloperGuide.adoc[tag=employeeusecase] diff --git a/docs/team/px1099.adoc b/docs/team/px1099.adoc new file mode 100644 index 000000000..15b46f655 --- /dev/null +++ b/docs/team/px1099.adoc @@ -0,0 +1,165 @@ += Pham Quang Minh - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: Restaurant Manager System + +--- + +== Overview + +This portfolio is written to show my contributions to the RMS project. + +Restaurant Management System (RMS) is a desktop application written in Java to help users manage a restaurant. + +It has five main features: + +* Menu Management +* Order Management +* Customer Membership Management +* Employee Management +* Statistics + +The interactions of this application with the users are implemented with a Command Line Interface (CLI) which prints out the results based on the command the user type in. + +This project is morphed from the +https://github.com/se-edu/addressbook-level3[Address Book Level 3] +created by +https://github.com/se-edu[SE-EDU initiative]. + +The application is developed by team F09-2 for the module CS2113T in the National University of Singapore (NUS). + +The team consists of five people, with each people managing one of the five functionality of the program. + +== Summary of contributions + +=== Implemented contributions + +==== Major contribution + +==== + +Added the functionality to manage orders + +* What it does: Allows the user to add, delete, clear and list orders in the restaurant system. +* Justification: This feature is required for a restaurant to keep track of all orders made in the past and present. +It allows conveniences in taking new orders and storing important data for future references. +* Highlights: Not only does this feature requires implementation of an order list management system, it also requires a +new order drafting system to make sure that the flow of adding new order feels natural and to avoid typing very long +command line to add a new order. As a result, many commands were developed for this functionality. +* Credits: The implementation of the order list is based on the code written for the person list in the Address Book +Level 3 made by SE-EDU initiative. + +==== + +==== Minor contributions + +==== + +Display the entered input as part of the returning message + +* Makes it possible for users to check what is the recently entered input. +* Helps user figure out any formatting error of the entered input. +* Allows users to quickly copy the previous entered command. +* Assist bug reporting by making both input and output appear at the same time. + +Make the input command word cases insensitive + +* Prevent error caused by case insensitive commands. + +==== + +=== Proposed Idea For Future Implementations + +==== Implement a state control system + +==== + +* What it does: Allows the user to access a specific functionality in the RMS. +* Justification: As there are many functionality in the RMS, having a state control system will make the flow of using +the RMS feels more natural and less confusing. + +==== + +==== Create short command keywords + +==== + +* What it does: Allows quicker input using shorter command keywords. +* Justification: Some of the command keywords are quite long, making it easy to type it wrongly. + +==== + +=== Code contributed + +* https://nuscs2113-ay1819s1.github.io/dashboard/#=undefined&search=px1099&sort=displayName&since=2018-09-12&until=2018-11-04&timeframe=day&reverse=false&repoSort=true[Code Dashboard] + +=== Other contributions + +* Project management: +** Managed release `v1.2.1`, `v1.3`, `v1.4` on https://github.com/CS2113-AY1819S1-F09-2/main/releases[GitHub] +** Add shadowJar to the project according to the announcement made by the CS2113T teaching team: +https://github.com/CS2113-AY1819S1-F09-2/main/pull/110[#110] +** Assisted teammates to debug problems + +* Enhancements to existing features: +** Clean the checkStyle errors in the project: +https://github.com/CS2113-AY1819S1-F09-2/main/pull/116[#116] +** Divide the command classes into multiple subfolder: +https://github.com/CS2113-AY1819S1-F09-2/main/pull/48[#48] +** Improve the quality of the code from B (19% code issues) to A (5% code issues): +https://github.com/CS2113-AY1819S1-F09-2/main/pull/211[#211] + +* Documentation: +** Contributed the ordering functionality contents of the User Guide and Developer Guide: +https://github.com[#14] +** Add the Logic Design and the Implementation in the Developer Guide: +https://github.com/CS2113-AY1819S1-F09-2/main/pull/109[#109] + +* Community: +** Made a proposal on how to each member can morph the code without creating too many merge conflicts. +** PRs reviewed (with non-trivial review comments): +https://github.com/CS2113-AY1819S1-F09-2/main/pull/87[#87], +https://github.com/CS2113-AY1819S1-F09-2/main/pull/88[#88], +https://github.com/CS2113-AY1819S1-F09-2/main/pull/99[#99], +https://github.com/CS2113-AY1819S1-F09-2/main/pull/144[#144], +https://github.com/CS2113-AY1819S1-F09-2/main/pull/176[#176], +https://github.com/CS2113-AY1819S1-F09-2/main/pull/185[#185] +** Shared the problems our project had to the CS2113T forum discussions: +https://github.com/nusCS2113-AY1819S1/forum/issues/75[1], +https://github.com/nusCS2113-AY1819S1/forum/issues/78[2], +https://github.com/nusCS2113-AY1819S1/forum/issues/85[3], +https://github.com/nusCS2113-AY1819S1/forum/issues/87[4] +** Reported bugs and suggestions to the other team members: +https://github.com/CS2113-AY1819S1-F09-2/main/issues/79[#79], +https://github.com/CS2113-AY1819S1-F09-2/main/issues/104[#104] +** Reported bugs and suggestions for other teams in the class: +https://github.com/CS2113-AY1819S1-T13-4/main/issues/116[1], +https://github.com/CS2113-AY1819S1-T13-4/main/issues/120[2], +https://github.com/CS2113-AY1819S1-T13-4/main/issues/126[3], +https://github.com/CS2113-AY1819S1-T13-4/main/issues/113[4] + +* Tools: +** Integrated CheckStyle and Coveralls into the project: +https://github.com/CS2113-AY1819S1-F09-2/main/pull/116[#116] +** Changed the Codacy badge in the README page: +https://github.com/CS2113-AY1819S1-F09-2/main/pull/129[#129] + +== Contributions to the User Guide + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=orderuserguide,leveloffset=+1]] + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=logic,leveloffset=+1] + +include::../DeveloperGuide.adoc[tag=orderimplementation,leveloffset=+1] diff --git a/docs/team/salsabiltasnia.adoc b/docs/team/salsabiltasnia.adoc new file mode 100644 index 000000000..3ac18d268 --- /dev/null +++ b/docs/team/salsabiltasnia.adoc @@ -0,0 +1,96 @@ += Salsabil Tasnia - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: Restaurant Management System + +--- +image::homepage.png[width="800"] + +== About Myself + +My name is Salsabil Tasnia Ali Nikita MD, I am a Year 2 Computer Engineering student in National University of Singapore (NUS). The purpose of this portfolio is to document my contributions to the Restaurant Management System project. + +== Overview + +Restaurant Management System (RMS) is a desktop management App targeted to Fast Food Chains. It is an integrated system that caters to both front end and back end operations in a restaurant. This application is developed to be used by managers, cashiers and other employees in a restaurant. + +For this system, I have developed the Menu system. A system that is vital for day-to-day operations of any restaurant, this feature is primarily catered to cashiers and restaurant managers. It consists of a database that stores all the menu items of a restaurant and separates them into different categories: Main, Sides, Beverage, Others, Set Meals. The menu data base is accessed for multiple operations of a restaurant, such as taking orders and collating statistics. The menu is accessed through a set of commands that I have implemented to support different operations. Some of these commands accesses the menu database, while others modifies the existing database. + +RMS was created using Java and has a user interface that uses CLI. The project contains approximately 10k LoC and is thoroughly tested through multiple forms of testing. Through this project, I have gained experience in Java programming, learned how to effectively use Git Hub to collaborate with other programmers for a project and conduct thorough testing and documentation necessary when developing a product. + + +== Summary of contributions + +|=== +|_This sections provides a summary of the various contributions I have made to the RMS project. These contributions include Major enhancement, Code contributions and Other contributions._ +|=== + +* *Major Enhancement*: Added the *Menu Feature* + +** Usage: Menu feature is mainly used by cashiers and managers for conducting separate set of operations + + Cashiers: + Menu allows cashiers to view the menu items as a list (according to their categories) when taking an order. Using a search option, cashiers are able to find a particular menu item if they wish not to scroll through the different item categories to find one item. Finally, cashiers are also able to suggest items to customers by viewing the recommended items. + + Managers: + Menu allows cashiers to add and delete items, individually, from the menu data base. It also allows them to clear all content of a menu if needed. Managers are also able to conduct the same operations as cashiers if needed. + +** Why the feature is important: The hassle of having separate systems for front-end and back-end operations is reduced with the RMS menu feature as it caters to both sides. With our menu feature, ordering is made easier and faster. Categorizing allows cashiers to navigate through the menu items much easily when taking an order. Furthermore, search option allows them to find a particular item in just one step, which increases efficiency when taking an order. Recommendations is useful when catering to first-time customers and customer service of a restaurant. Additionally, the feature caters to back-end operations, usually conducted by managers, such as updating the existing menu and clearing it if needed. + +** Highlights: This enhancement required me to understand the needs of a fast food chain and add, modify and improve existing commands to optimize day-to-day operations. The implementation was challenging as it required making major and minor changes to existing to morph into a menu system, while adding new commands to the feature. + +** Credits: https://github.com/nusCS2113-AY1819S1/addressbook-level3 + +* *Code Contributed*: https://nuscs2113-ay1819s1.github.io/dashboard/#=undefined&search=salsabiltasnia&sort=displayName&since=2018-09-12&until=2018-11-04&timeframe=day&reverse=false&repoSort=true[Salsabil Tasnia Ali Nikita MD] + +* *Other Contributions* + +** Project Management +*** Was in charge of creating the Readme.adoc file, which served as the homepage of our product. +*** Managed the Git Hub Issue tracker by raising new issues. + +* *Enhancement to existing feature* +*** Created test cases to test for the existing features to ensure every command functions as expected +*** Wrote additional methods for team mate who required accessing the menu feature. + +* *Documentation*: + +*** Addition of content to User Guide: https://github.com/CS2113-AY1819S1-F09-2/main/pull/73[#73] +https://github.com/CS2113-AY1819S1-F09-2/main/pull/135[#135] +https://github.com/CS2113-AY1819S1-F09-2/main/pull/144[#144] +https://github.com/CS2113-AY1819S1-F09-2/main/pull/202[#202] +https://github.com/CS2113-AY1819S1-F09-2/main/pull/214[#214] +*** Addition of content to Developer Guide: https://github.com/CS2113-AY1819S1-F09-2/main/pull/73[#73] +https://github.com/CS2113-AY1819S1-F09-2/main/pull/135[#135] +https://github.com/CS2113-AY1819S1-F09-2/main/pull/202[#202] +https://github.com/CS2113-AY1819S1-F09-2/main/pull/214[#214] +* *Community*: +*** PRs reviewed (with non-trivial review comments): https://github.com/CS2113-AY1819S1-F09-2/main/pull/109[#109], https://github.com/CS2113-AY1819S1-F09-2/main/pull/37[#37], https://github.com/CS2113-AY1819S1-F09-2/main/pull/36[36], + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. This section provides explicit instructions on how to use the Menu Feature and explains the outcome of each of the menu commands. A summary of the different commands used to operate the Menu is also provided. Overall, the User Guide serves as a manual for first time users of RMS and taken them step-by-step through each features._ +|=== + +include::../UserGuide.adoc[tag=menucommands] +include::../UserGuide.adoc[tag=menucommandsummary] + + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. This sections provides various scenarios when different aspects of the Menu feature ought be used and who should use them. It also shows the Data diagram that I have created for the entire project. This section allows new Developers of the product to understand each feature easily provides the architecture of the entire system._ +|=== + +include::../DeveloperGuide.adoc[tag=menuusecase] + +include::../DeveloperGuide.adoc[tag=Data] + + + + + diff --git a/docs/templates/_header.html.slim b/docs/templates/_header.html.slim index e2a19efaa..c596ecea6 100644 --- a/docs/templates/_header.html.slim +++ b/docs/templates/_header.html.slim @@ -1,26 +1,4 @@ / NOTE: You must restart the gradle daemon after modifying any template file for the changes to take effect. -- if !(attr? 'no-site-header') && (attr? 'site-seedu') - #seedu-header - nav.navbar.navbar-lg.navbar-light.bg-lighter - .container - a.navbar-brand href='https://se-edu.github.io/' - img src=(site_url 'images/SeEduLogo.png') alt='SE-EDU' - ul.navbar-nav - li.nav-item - a.nav-link href='https://se-edu.github.io/addressbook-level1' AB-1 - li.nav-item - a.nav-link href='https://se-edu.github.io/addressbook-level2' AB-2 - li.nav-item - a.nav-link.active href=(site_url 'index.html') AB-3 - li.nav-item - a.nav-link href='https://se-edu.github.io/addressbook-level4' AB-4 - li.nav-item - a.nav-link href='https://se-edu.github.io/collate' Collate - li.nav-item - a.nav-link href='https://se-edu.github.io/se-book' Book - li.nav-item - a.nav-link href='https://se-edu.github.io/learningresources' Resources - - if !(attr? 'no-site-header') #site-header nav.navbar.navbar-light.bg-light @@ -32,9 +10,10 @@ =nav_link('UserGuide', 'UserGuide.html', 'User Guide') li.nav-item =nav_link('DeveloperGuide', 'DeveloperGuide.html', 'Developer Guide') - - if attr? 'site-seedu' - li.nav-item - =nav_link('LearningOutcomes', 'LearningOutcomes.html', 'LOs') + li.nav-item + =nav_link('AboutUs', 'AboutUs.html', 'About Us') + li.nav-item + =nav_link('ContactUs', 'ContactUs.html', 'Contact Us') - if attr? 'site-githuburl' li.navitem a.nav-link href=(attr 'site-githuburl') diff --git a/docs/templates/document.html.slim b/docs/templates/document.html.slim index 3e1961d4a..67c939449 100644 --- a/docs/templates/document.html.slim +++ b/docs/templates/document.html.slim @@ -18,7 +18,7 @@ html lang=(attr :lang, 'en' unless attr? :nolang) =docinfo_content body( id=id - class=[(attr :doctype), ("#{attr 'toc-class'} toc-#{attr 'toc-position', 'left'}" if (attr? 'toc-class') && (attr? :toc) && (attr? 'toc-placement', 'auto'))] + class=[(attr :doctype), ("#{attr 'toc-class'} toc-#{attr 'toc-value', 'left'}" if (attr? 'toc-class') && (attr? :toc) && (attr? 'toc-placement', 'auto'))] style=style_value(max_width: (attr 'max-width'))) - unless noheader include _header.html diff --git a/src/seedu/addressbook/Main.java b/src/seedu/addressbook/Main.java index fca4d5d1d..70c7cd356 100644 --- a/src/seedu/addressbook/Main.java +++ b/src/seedu/addressbook/Main.java @@ -1,39 +1,39 @@ -package seedu.addressbook; - -import javafx.application.Application; -import javafx.application.Platform; - -import javafx.stage.Stage; -import seedu.addressbook.logic.Logic; -import seedu.addressbook.ui.Gui; -import seedu.addressbook.ui.Stoppable; - -/** - * Main entry point to the application. - */ -public class Main extends Application implements Stoppable{ - - /** Version info of the program. */ - public static final String VERSION = "AddressBook Level 3 - Version 1.0"; - - private Gui gui; - - @Override - public void start(Stage primaryStage) throws Exception{ - gui = new Gui(new Logic(), VERSION); - gui.start(primaryStage, this); - } - - @Override - public void stop() throws Exception { - super.stop(); - Platform.exit(); - System.exit(0); - } - - public static void main(String[] args) { - launch(args); - } -} - - +package seedu.addressbook; + +import javafx.application.Application; +import javafx.application.Platform; + +import javafx.stage.Stage; +import seedu.addressbook.logic.Logic; +import seedu.addressbook.ui.Gui; +import seedu.addressbook.ui.Stoppable; + +/** + * Main entry point to the application. + */ +public class Main extends Application implements Stoppable { + + /** Version info of the program. */ + public static final String VERSION = "Restaurant Management System - Version 1.4.0"; + + private Gui gui; + + @Override + public void start(Stage primaryStage) throws Exception { + gui = new Gui(new Logic(), VERSION); + gui.start(primaryStage, this); + } + + @Override + public void stop() throws Exception { + super.stop(); + Platform.exit(); + System.exit(0); + } + + public static void main(String[] args) { + launch(args); + } +} + + diff --git a/src/seedu/addressbook/commands/AddCommand.java b/src/seedu/addressbook/commands/AddCommand.java deleted file mode 100644 index 78ad0da21..000000000 --- a/src/seedu/addressbook/commands/AddCommand.java +++ /dev/null @@ -1,69 +0,0 @@ -package seedu.addressbook.commands; - -import seedu.addressbook.data.exception.IllegalValueException; -import seedu.addressbook.data.person.*; -import seedu.addressbook.data.tag.Tag; - -import java.util.HashSet; -import java.util.Set; - -/** - * Adds a person to the address book. - */ -public class AddCommand extends Command { - - public static final String COMMAND_WORD = "add"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Adds a person to the address book. " - + "Contact details can be marked private by prepending 'p' to the prefix.\n\t" - + "Parameters: NAME [p]p/PHONE [p]e/EMAIL [p]a/ADDRESS [t/TAG]...\n\t" - + "Example: " + COMMAND_WORD - + " John Doe p/98765432 e/johnd@gmail.com a/311, Clementi Ave 2, #02-25 t/friends t/owesMoney"; - - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; - - private final Person toAdd; - - /** - * Convenience constructor using raw values. - * - * @throws IllegalValueException if any of the raw values are invalid - */ - public AddCommand(String name, - String phone, boolean isPhonePrivate, - String email, boolean isEmailPrivate, - String address, boolean isAddressPrivate, - Set tags) throws IllegalValueException { - final Set tagSet = new HashSet<>(); - for (String tagName : tags) { - tagSet.add(new Tag(tagName)); - } - this.toAdd = new Person( - new Name(name), - new Phone(phone, isPhonePrivate), - new Email(email, isEmailPrivate), - new Address(address, isAddressPrivate), - tagSet - ); - } - - public AddCommand(Person toAdd) { - this.toAdd = toAdd; - } - - public ReadOnlyPerson getPerson() { - return toAdd; - } - - @Override - public CommandResult execute() { - try { - addressBook.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); - } catch (UniquePersonList.DuplicatePersonException dpe) { - return new CommandResult(MESSAGE_DUPLICATE_PERSON); - } - } - -} diff --git a/src/seedu/addressbook/commands/ClearCommand.java b/src/seedu/addressbook/commands/ClearCommand.java deleted file mode 100644 index 330146aaa..000000000 --- a/src/seedu/addressbook/commands/ClearCommand.java +++ /dev/null @@ -1,19 +0,0 @@ -package seedu.addressbook.commands; - -/** - * Clears the address book. - */ -public class ClearCommand extends Command { - - public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Clears address book permanently.\n\t" - + "Example: " + COMMAND_WORD; - - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; - - @Override - public CommandResult execute() { - addressBook.clear(); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/seedu/addressbook/commands/Command.java b/src/seedu/addressbook/commands/Command.java index a54cbcb5b..8008c69bc 100644 --- a/src/seedu/addressbook/commands/Command.java +++ b/src/seedu/addressbook/commands/Command.java @@ -1,23 +1,37 @@ package seedu.addressbook.commands; -import seedu.addressbook.common.Messages; -import seedu.addressbook.data.AddressBook; -import seedu.addressbook.data.person.ReadOnlyPerson; +import static seedu.addressbook.ui.Gui.DISPLAYED_INDEX_OFFSET; import java.util.List; -import static seedu.addressbook.ui.Gui.DISPLAYED_INDEX_OFFSET; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.Rms; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.order.ReadOnlyOrder; /** * Represents an executable command. */ public abstract class Command { - protected AddressBook addressBook; - protected List relevantPersons; + + //@@author AngWM + protected Rms rms; + //@@author SalsabilTasnia + protected List relevantMenus; + //@@author kangmingtay + protected List relevantMembers; + //@@author px1099 + protected List relevantOrders; + //@@author kianhong95 + protected List relevantEmployees; + //@@author + private int targetIndex = -1; /** - * @param targetIndex last visible listing index of the target person + * @param targetIndex last visible listing index of the target object */ public Command(int targetIndex) { this.setTargetIndex(targetIndex); @@ -26,43 +40,129 @@ public Command(int targetIndex) { protected Command() { } + //@@author kianhong95 /** - * Constructs a feedback message to summarise an operation that displayed a listing of persons. + * Constructs a feedback message to summarise an operation that displayed a listing of employees. * - * @param personsDisplayed used to generate summary - * @return summary message for persons displayed + * @param employeesDisplayed used to generate summary + * @return summary message for employees displayed */ - public static String getMessageForPersonListShownSummary(List personsDisplayed) { - return String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, personsDisplayed.size()); + public static String getMessageForEmployeeListShownSummary(List employeesDisplayed) { + if (employeesDisplayed.size() == 0) { + return Messages.MESSAGE_NO_EMPLOYEES_IN_SYSTEM; + } + return String.format(Messages.MESSAGE_EMPLOYEES_LISTED_OVERVIEW, employeesDisplayed.size()); } + //@@author SalsabilTasnia /** - * Executes the command and returns the result. + * Constructs a feedback message to summarise an operation that displayed a listing of menu. + * + * @param menusDisplayed used to generate summary + * @return summary message for menus displayed + */ + public static String getMessageForMenuListShownSummary(List menusDisplayed) { + return String.format(Messages.MESSAGE_MENUS_LISTED_OVERVIEW, menusDisplayed.size()); + } + + //@@author kangmingtay + /** + * Constructs a feedback message to summarise an operation that displayed a listing of members. + * + * @param membersDisplayed used to generate summary + * @return summary message for members displayed + */ + public static String getMessageForMemberListShownSummary(List membersDisplayed) { + return String.format(Messages.MESSAGE_MEMBERS_LISTED_OVERVIEW, membersDisplayed.size()); + } + + //@@author px1099 + /** + * Constructs a feedback message to summarise an operation that displayed a listing of orders. + * + * @param ordersDisplayed used to generate summary + * @return summary message for orders displayed + */ + public static String getMessageForOrderListShownSummary(List ordersDisplayed) { + return String.format(Messages.MESSAGE_ORDERS_LISTED_OVERVIEW, ordersDisplayed.size()); + } + + /** + * Constructs a string from the current status of the draft order. + * + * @return draft order details */ - public CommandResult execute(){ - throw new UnsupportedOperationException("This method should be implement in child classes"); + protected String getDraftOrderAsString() { + final String draftOrder = rms.getDraftOrderAsText(); + return String.format(Messages.MESSAGE_DRAFT_ORDER_DETAILS, draftOrder); } + //@@ author + /** + * Executes the command and returns the result. + */ + public abstract CommandResult execute(); + //Note: it is better to make the execute() method abstract, by replacing the above method with the line below: //public abstract CommandResult execute(); + //@@author AngWM /** * Supplies the data the command will operate on. */ - public void setData(AddressBook addressBook, List relevantPersons) { - this.addressBook = addressBook; - this.relevantPersons = relevantPersons; + public void setData(Rms rms, + List relevantMenus, + List relevantOrders, + List relevantMembers, + List relevantEmployees) { + this.rms = rms; + this.relevantMenus = relevantMenus; + this.relevantOrders = relevantOrders; + this.relevantMembers = relevantMembers; + this.relevantEmployees = relevantEmployees; + } + + //@@author SalsabilTasnia + /** + * Extracts the the target menu item in the last shown menu list from the given arguments. + * + * @throws IndexOutOfBoundsException if the target index is out of bounds of the last viewed listing + */ + protected ReadOnlyMenus getTargetMenu() throws IndexOutOfBoundsException { + return relevantMenus.get(getTargetIndex() - DISPLAYED_INDEX_OFFSET); + } + + //@@author kangmingtay + /** + * Extracts the the target member in the last shown list from the given arguments. + * + * @throws IndexOutOfBoundsException if the target index is out of bounds of the last viewed listing + */ + protected ReadOnlyMember getTargetMember() throws IndexOutOfBoundsException { + return relevantMembers.get(getTargetIndex() - DISPLAYED_INDEX_OFFSET); + } + + //@@author px1099 + /** + * Extracts the the target order in the last shown order list from the given arguments. + * + * @throws IndexOutOfBoundsException if the target index is out of bounds of the last viewed listing + */ + protected ReadOnlyOrder getTargetOrder() throws IndexOutOfBoundsException { + return relevantOrders.get(getTargetIndex() - DISPLAYED_INDEX_OFFSET); } + //@@author kianhong95 /** - * Extracts the the target person in the last shown list from the given arguments. + * Extracts the target employee in the last shown employee list from the given arguments. * * @throws IndexOutOfBoundsException if the target index is out of bounds of the last viewed listing */ - protected ReadOnlyPerson getTargetPerson() throws IndexOutOfBoundsException { - return relevantPersons.get(getTargetIndex() - DISPLAYED_INDEX_OFFSET); + protected ReadOnlyEmployee getTargetEmployee() throws IndexOutOfBoundsException { + return relevantEmployees.get(getTargetIndex() - DISPLAYED_INDEX_OFFSET); } + //@@author public int getTargetIndex() { return targetIndex; } diff --git a/src/seedu/addressbook/commands/CommandResult.java b/src/seedu/addressbook/commands/CommandResult.java index cf4e72585..9cf4e7b9d 100644 --- a/src/seedu/addressbook/commands/CommandResult.java +++ b/src/seedu/addressbook/commands/CommandResult.java @@ -1,10 +1,14 @@ package seedu.addressbook.commands; -import seedu.addressbook.data.person.ReadOnlyPerson; - import java.util.List; import java.util.Optional; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.order.ReadOnlyOrder; + /** * Represents the result of a command execution. */ @@ -13,24 +17,88 @@ 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 persons that was produced by the command */ - private final List relevantPersons; + //@@author kianhong95 + /** The list of employees that was produced by the command */ + private final List relevantEmployees; + + /** The list of attendances that was produced by the command */ + private final List relevantAttendances; + + //@@author SalsabilTasnia + /** The menu list produced by the menu command*/ + private final List relevantMenus; + + //@@author kangmingtay + /** The list of members that was produced by the command */ + private final List relevantMembers; + + //@@author px1099 + /** The list of orders that was produced by the order command */ + private final List relevantOrders; + /** Constructor for result which do not return any list*/ public CommandResult(String feedbackToUser) { this.feedbackToUser = feedbackToUser; - relevantPersons = null; + relevantMenus = null; + relevantOrders = null; + relevantMembers = null; + relevantEmployees = null; + relevantAttendances = null; } - public CommandResult(String feedbackToUser, List relevantPersons) { + /** Command result constructor used by child classes for Rms commands*/ + public CommandResult(String feedbackToUser, + List relevantMenus, + List relevantOrders, + List relevantMembers, + List relevantEmployees, + List relevantAttendances) { this.feedbackToUser = feedbackToUser; - this.relevantPersons = relevantPersons; + this.relevantMenus = relevantMenus; + this.relevantOrders = relevantOrders; + this.relevantMembers = relevantMembers; + this.relevantEmployees = relevantEmployees; + this.relevantAttendances = relevantAttendances; + } + + //@@author SalsabilTasnia + /** + * Returns list of menu items relevant to the command result, if any. + */ + public Optional> getRelevantMenus() { + return Optional.ofNullable(relevantMenus); + } + + //@@author px1099 + /** + * Returns list of orders relevant to the command result, if any. + */ + public Optional> getRelevantOrders() { + return Optional.ofNullable(relevantOrders); + } + + //@@author kangmingtay + /** + * Returns list of members relevant to the command result, if any. + */ + public Optional> getRelevantMember() { + return Optional.ofNullable(relevantMembers); + } + + //@@author kianhong95 + /** + * Returns list of employees relevant to the command result, if any. + */ + public Optional> getRelevantEmployee() { + return Optional.ofNullable(relevantEmployees); } /** - * Returns list of persons relevant to the command command result, if any. + * Returns list of attendances relevant to the command result, if any. */ - public Optional> getRelevantPersons() { - return Optional.ofNullable(relevantPersons); + public Optional> getRelevantAttendance() { + return Optional.ofNullable(relevantAttendances); } + //@@author } diff --git a/src/seedu/addressbook/commands/DeleteCommand.java b/src/seedu/addressbook/commands/DeleteCommand.java deleted file mode 100644 index 1dd78f85e..000000000 --- a/src/seedu/addressbook/commands/DeleteCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -package seedu.addressbook.commands; - -import seedu.addressbook.common.Messages; -import seedu.addressbook.data.person.ReadOnlyPerson; -import seedu.addressbook.data.person.UniquePersonList.PersonNotFoundException; - - -/** - * Deletes a person identified using it's last displayed index from the address book. - */ -public class DeleteCommand extends Command { - - public static final String COMMAND_WORD = "delete"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" - + "Deletes the person identified by the index number used in the last person listing.\n\t" - + "Parameters: INDEX\n\t" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; - - - public DeleteCommand(int targetVisibleIndex) { - super(targetVisibleIndex); - } - - - @Override - public CommandResult execute() { - try { - final ReadOnlyPerson target = getTargetPerson(); - addressBook.removePerson(target); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, target)); - - } catch (IndexOutOfBoundsException ie) { - return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } catch (PersonNotFoundException pnfe) { - return new CommandResult(Messages.MESSAGE_PERSON_NOT_IN_ADDRESSBOOK); - } - } - -} diff --git a/src/seedu/addressbook/commands/ExitCommand.java b/src/seedu/addressbook/commands/ExitCommand.java index 0585451f1..549e7f1e1 100644 --- a/src/seedu/addressbook/commands/ExitCommand.java +++ b/src/seedu/addressbook/commands/ExitCommand.java @@ -9,7 +9,7 @@ public class ExitCommand extends Command { public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Exits the program.\n\t" + "Example: " + COMMAND_WORD; - public static final String MESSAGE_EXIT_ACKNOWEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWEDGEMENT = "Exiting Rms as requested ..."; @Override public CommandResult execute() { diff --git a/src/seedu/addressbook/commands/FindCommand.java b/src/seedu/addressbook/commands/FindCommand.java deleted file mode 100644 index c8e9a380f..000000000 --- a/src/seedu/addressbook/commands/FindCommand.java +++ /dev/null @@ -1,56 +0,0 @@ -package seedu.addressbook.commands; - -import seedu.addressbook.data.person.ReadOnlyPerson; - -import java.util.*; - -/** - * Finds and lists all persons in address book whose name contains any of the argument keywords. - * Keyword matching is case sensitive. - */ -public class FindCommand extends Command { - - public static final String COMMAND_WORD = "find"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Finds all persons whose names contain any of " - + "the specified keywords (case-sensitive) and displays them as a list with index numbers.\n\t" - + "Parameters: KEYWORD [MORE_KEYWORDS]...\n\t" - + "Example: " + COMMAND_WORD + " alice bob charlie"; - - private final Set keywords; - - public FindCommand(Set keywords) { - this.keywords = keywords; - } - - /** - * Returns copy of keywords in this command. - */ - public Set getKeywords() { - return new HashSet<>(keywords); - } - - @Override - public CommandResult execute() { - final List personsFound = getPersonsWithNameContainingAnyKeyword(keywords); - return new CommandResult(getMessageForPersonListShownSummary(personsFound), personsFound); - } - - /** - * Retrieve all persons in the address book whose names contain some of the specified keywords. - * - * @param keywords for searching - * @return list of persons found - */ - private List getPersonsWithNameContainingAnyKeyword(Set keywords) { - final List matchedPersons = new ArrayList<>(); - for (ReadOnlyPerson person : addressBook.getAllPersons()) { - final Set wordsInName = new HashSet<>(person.getName().getWordsInName()); - if (!Collections.disjoint(wordsInName, keywords)) { - matchedPersons.add(person); - } - } - return matchedPersons; - } - -} diff --git a/src/seedu/addressbook/commands/HelpCommand.java b/src/seedu/addressbook/commands/HelpCommand.java index ef2ed7d0e..fef35763d 100644 --- a/src/seedu/addressbook/commands/HelpCommand.java +++ b/src/seedu/addressbook/commands/HelpCommand.java @@ -1,5 +1,32 @@ package seedu.addressbook.commands; +import seedu.addressbook.commands.employee.EmployeeAddCommand; +import seedu.addressbook.commands.employee.EmployeeListCommand; +import seedu.addressbook.commands.member.MemberAddCommand; +import seedu.addressbook.commands.member.MemberListCommand; +import seedu.addressbook.commands.menu.MenuAddCommand; +import seedu.addressbook.commands.menu.MenuClearCommand; +import seedu.addressbook.commands.menu.MenuDeleteCommand; +import seedu.addressbook.commands.menu.MenuFindCommand; +import seedu.addressbook.commands.menu.MenuListByTypeCommand; +import seedu.addressbook.commands.menu.MenuListCommand; +import seedu.addressbook.commands.menu.MenuRecommendationCommand; +import seedu.addressbook.commands.menu.MenuShowMainMenuCommand; +import seedu.addressbook.commands.order.DraftOrderClearCommand; +import seedu.addressbook.commands.order.DraftOrderConfirmCommand; +import seedu.addressbook.commands.order.DraftOrderEditCustomerCommand; +import seedu.addressbook.commands.order.DraftOrderEditDishCommand; +import seedu.addressbook.commands.order.DraftOrderEditPointsCommand; +import seedu.addressbook.commands.order.OrderAddCommand; +import seedu.addressbook.commands.order.OrderClearCommand; +import seedu.addressbook.commands.order.OrderDeleteCommand; +import seedu.addressbook.commands.order.OrderListCommand; +import seedu.addressbook.commands.statistics.StatsEmployeeCommand; +import seedu.addressbook.commands.statistics.StatsHelpCommand; +import seedu.addressbook.commands.statistics.StatsMemberCommand; +import seedu.addressbook.commands.statistics.StatsMenuCommand; +import seedu.addressbook.commands.statistics.StatsOrderCommand; + /** * Shows help instructions. @@ -8,18 +35,44 @@ public class HelpCommand extends Command { public static final String COMMAND_WORD = "help"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" +"Shows program usage instructions.\n\t" + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Shows program usage instructions.\n\t" + "Example: " + COMMAND_WORD; - public static final String MESSAGE_ALL_USAGES = AddCommand.MESSAGE_USAGE - + "\n" + DeleteCommand.MESSAGE_USAGE - + "\n" + ClearCommand.MESSAGE_USAGE - + "\n" + FindCommand.MESSAGE_USAGE - + "\n" + ListCommand.MESSAGE_USAGE - + "\n" + ViewCommand.MESSAGE_USAGE - + "\n" + ViewAllCommand.MESSAGE_USAGE - + "\n" + HelpCommand.MESSAGE_USAGE - + "\n" + ExitCommand.MESSAGE_USAGE; + public static final String MESSAGE_ALL_USAGES = "1. " + HelpCommand.MESSAGE_USAGE + + + "\n\n2. " + EmployeeListCommand.MESSAGE_USAGE + + "\n\n3. " + EmployeeAddCommand.MESSAGE_USAGE + + + "\n\n4. " + MemberListCommand.MESSAGE_USAGE + + "\n\n5. " + MemberAddCommand.MESSAGE_USAGE + + + "\n\n6. " + MenuAddCommand.MESSAGE_USAGE + + "\n\n7. " + MenuDeleteCommand.MESSAGE_USAGE + + "\n\n8. " + MenuFindCommand.MESSAGE_USAGE + + "\n\n9. " + MenuListCommand.MESSAGE_USAGE + + "\n\n10. " + MenuShowMainMenuCommand.MESSAGE_USAGE + + "\n\n11. " + MenuListByTypeCommand.MESSAGE_USAGE + + "\n\n12. " + MenuRecommendationCommand.MESSAGE_USAGE + + "\n\n13. " + MenuClearCommand.MESSAGE_USAGE + + + "\n\n14. " + OrderAddCommand.MESSAGE_USAGE + + "\n\n15. " + OrderDeleteCommand.MESSAGE_USAGE + + "\n\n16. " + OrderClearCommand.MESSAGE_USAGE + + "\n\n17. " + OrderListCommand.MESSAGE_USAGE + + "\n\n18. " + DraftOrderEditCustomerCommand.MESSAGE_USAGE + + "\n\n19. " + DraftOrderEditDishCommand.MESSAGE_USAGE + + "\n\n20. " + DraftOrderEditPointsCommand.MESSAGE_USAGE + + "\n\n21. " + DraftOrderClearCommand.MESSAGE_USAGE + + "\n\n22. " + DraftOrderConfirmCommand.MESSAGE_USAGE + + + "\n\n23. " + StatsHelpCommand.MESSAGE_USAGE + + "\n\n24. " + StatsEmployeeCommand.MESSAGE_USAGE + + "\n\n25. " + StatsMenuCommand.MESSAGE_USAGE + + "\n\n26. " + StatsMemberCommand.MESSAGE_USAGE + + "\n\n27. " + StatsOrderCommand.MESSAGE_USAGE + + + "\n\n28. " + ExitCommand.MESSAGE_USAGE; @Override public CommandResult execute() { diff --git a/src/seedu/addressbook/commands/IncorrectCommand.java b/src/seedu/addressbook/commands/IncorrectCommand.java index 81abba7a1..46777aa61 100644 --- a/src/seedu/addressbook/commands/IncorrectCommand.java +++ b/src/seedu/addressbook/commands/IncorrectCommand.java @@ -4,11 +4,11 @@ /** * Represents an incorrect command. Upon execution, produces some feedback to the user. */ -public class IncorrectCommand extends Command{ +public class IncorrectCommand extends Command { public final String feedbackToUser; - public IncorrectCommand(String feedbackToUser){ + public IncorrectCommand(String feedbackToUser) { this.feedbackToUser = feedbackToUser; } diff --git a/src/seedu/addressbook/commands/ListCommand.java b/src/seedu/addressbook/commands/ListCommand.java deleted file mode 100644 index cb604a8e9..000000000 --- a/src/seedu/addressbook/commands/ListCommand.java +++ /dev/null @@ -1,25 +0,0 @@ -package seedu.addressbook.commands; - -import seedu.addressbook.data.person.ReadOnlyPerson; - -import java.util.List; - - -/** - * Lists all persons in the address book to the user. - */ -public class ListCommand extends Command { - - public static final String COMMAND_WORD = "list"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" - + "Displays all persons in the address book as a list with index numbers.\n\t" - + "Example: " + COMMAND_WORD; - - - @Override - public CommandResult execute() { - List allPersons = addressBook.getAllPersons().immutableListView(); - return new CommandResult(getMessageForPersonListShownSummary(allPersons), allPersons); - } -} diff --git a/src/seedu/addressbook/commands/ViewAllCommand.java b/src/seedu/addressbook/commands/ViewAllCommand.java deleted file mode 100644 index ed2c16e83..000000000 --- a/src/seedu/addressbook/commands/ViewAllCommand.java +++ /dev/null @@ -1,40 +0,0 @@ -package seedu.addressbook.commands; - -import seedu.addressbook.common.Messages; -import seedu.addressbook.data.person.ReadOnlyPerson; - - -/** - * Shows all details of the person identified using the last displayed index. - * Private contact details are shown. - */ -public class ViewAllCommand extends Command { - - public static final String COMMAND_WORD = "viewall"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Shows all details of the person " - + "identified by the index number in the last shown person listing.\n\t" - + "Parameters: INDEX\n\t" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_VIEW_PERSON_DETAILS = "Viewing person: %1$s"; - - - public ViewAllCommand(int targetVisibleIndex) { - super(targetVisibleIndex); - } - - - @Override - public CommandResult execute() { - try { - final ReadOnlyPerson target = getTargetPerson(); - if (!addressBook.containsPerson(target)) { - return new CommandResult(Messages.MESSAGE_PERSON_NOT_IN_ADDRESSBOOK); - } - return new CommandResult(String.format(MESSAGE_VIEW_PERSON_DETAILS, target.getAsTextShowAll())); - } catch (IndexOutOfBoundsException ie) { - return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - } -} diff --git a/src/seedu/addressbook/commands/ViewCommand.java b/src/seedu/addressbook/commands/ViewCommand.java deleted file mode 100644 index 1058c4b52..000000000 --- a/src/seedu/addressbook/commands/ViewCommand.java +++ /dev/null @@ -1,41 +0,0 @@ -package seedu.addressbook.commands; - -import seedu.addressbook.common.Messages; -import seedu.addressbook.data.person.ReadOnlyPerson; - - -/** - * Shows details of the person identified using the last displayed index. - * Private contact details are not shown. - */ -public class ViewCommand extends Command { - - public static final String COMMAND_WORD = "view"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Shows the non-private details of the person " - + "identified by the index number in the last shown person listing.\n\t" - + "Parameters: INDEX\n\t" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_VIEW_PERSON_DETAILS = "Viewing person: %1$s"; - - - public ViewCommand(int targetVisibleIndex) { - super(targetVisibleIndex); - } - - - @Override - public CommandResult execute() { - try { - final ReadOnlyPerson target = getTargetPerson(); - if (!addressBook.containsPerson(target)) { - return new CommandResult(Messages.MESSAGE_PERSON_NOT_IN_ADDRESSBOOK); - } - return new CommandResult(String.format(MESSAGE_VIEW_PERSON_DETAILS, target.getAsTextHidePrivate())); - } catch (IndexOutOfBoundsException ie) { - return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - } - -} diff --git a/src/seedu/addressbook/commands/employee/EmployeeAddCommand.java b/src/seedu/addressbook/commands/employee/EmployeeAddCommand.java new file mode 100644 index 000000000..e64d855b7 --- /dev/null +++ b/src/seedu/addressbook/commands/employee/EmployeeAddCommand.java @@ -0,0 +1,75 @@ +package seedu.addressbook.commands.employee; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.Employee; +import seedu.addressbook.data.employee.EmployeeAddress; +import seedu.addressbook.data.employee.EmployeeEmail; +import seedu.addressbook.data.employee.EmployeeName; +import seedu.addressbook.data.employee.EmployeePhone; +import seedu.addressbook.data.employee.EmployeePosition; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.employee.UniqueEmployeeList; +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kianhong95 +/** + * Adds a new employee to the Rms. + */ +public class EmployeeAddCommand extends Command { + + public static final String COMMAND_WORD = "addemp"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Adds an employee to the Rms.\n\n" + + "Parameters: NAME p/PHONE e/EMAIL a/ADDRESS pos/POSITION\n" + + "Example: " + COMMAND_WORD + " " + + EmployeeName.EXAMPLE + + " p/" + EmployeePhone.EXAMPLE + + " e/" + EmployeeEmail.EXAMPLE + + " a/" + EmployeeAddress.EXAMPLE + + " pos/" + EmployeePosition.EXAMPLE; + + public static final String MESSAGE_SUCCESS = "New employee added: %1$s"; + public static final String MESSAGE_DUPLICATE_EMPLOYEE = "This employee already exists in the Rms."; + + private final Employee toAdd; + private final Attendance newAttendance; + + /** + * Convenience constructor using raw values. + * + * @throws IllegalValueException if any of the raw values are invalid + */ + public EmployeeAddCommand(String name, + String phone, + String email, + String address, + String position) throws IllegalValueException { + this.toAdd = new Employee( + new EmployeeName(name), + new EmployeePhone(phone), + new EmployeeEmail(email), + new EmployeeAddress(address), + new EmployeePosition(position) + ); + + this.newAttendance = new Attendance(name); + } + + public ReadOnlyEmployee getEmployee() { + return toAdd; + } + + @Override + public CommandResult execute() { + try { + rms.addEmployee(toAdd); + rms.addAttendance(newAttendance); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } catch (UniqueEmployeeList.DuplicateEmployeeException dee) { + return new CommandResult(MESSAGE_DUPLICATE_EMPLOYEE); + } + } +} diff --git a/src/seedu/addressbook/commands/employee/EmployeeClockInCommand.java b/src/seedu/addressbook/commands/employee/EmployeeClockInCommand.java new file mode 100644 index 000000000..72600aa91 --- /dev/null +++ b/src/seedu/addressbook/commands/employee/EmployeeClockInCommand.java @@ -0,0 +1,76 @@ +package seedu.addressbook.commands.employee; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Set; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.EmployeeName; +import seedu.addressbook.data.employee.Timing; + +//@@author kianhong95 +/** + * Clocks in for the specified employee based on the current time. + */ +public class EmployeeClockInCommand extends Command { + + public static final String COMMAND_WORD = "clockin"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Clocks in with the current time for the specified employee.\n\n" + + "Parameters: NAME\n" + + "Example: " + COMMAND_WORD + " " + + EmployeeName.EXAMPLE; + + public static final String MESSAGE_SUCCESS = "%1$s clocked in on %2$s at %3$s."; + public static final String MESSAGE_NOT_YET_CLOCKED_OUT = "%1$s needs to clock out first in order to clock in."; + + private final String name; + + private SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy"); + private SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm"); + private Date date = new Date(); + private final String currentTime = timeFormatter.format(date); + private final String currentDate = dateFormatter.format(date); + + public EmployeeClockInCommand(String name) { + this.name = name; + } + + /** + * Creates and returns an {@code Attendance} with the details of the current time to clock in. + */ + private Attendance createNewAttendance(Attendance oldAttendance) { + String name = oldAttendance.getName(); + + Set updatedTimings = oldAttendance.getTimings(); + + Timing currentTiming = new Timing(this.currentTime, this.currentDate, true); + updatedTimings.add(currentTiming); + + return new Attendance(name, true, updatedTimings); + } + + @Override + public CommandResult execute() { + try { + int index = rms.findAttendanceIndex(name); + + Attendance oldAttendance = rms.findAttendance(index); + boolean isClockedIn = oldAttendance.getClockedIn(); + if (isClockedIn) { + return new CommandResult(String.format(MESSAGE_NOT_YET_CLOCKED_OUT, name)); + } + + Attendance newAttendance = createNewAttendance(oldAttendance); + + rms.updateAttendance(oldAttendance, newAttendance); + return new CommandResult(String.format(MESSAGE_SUCCESS, name, this.currentDate, this.currentTime)); + } catch (IndexOutOfBoundsException ie) { + return new CommandResult(Messages.MESSAGE_EMPLOYEE_NOT_IN_RMS); + } + } +} diff --git a/src/seedu/addressbook/commands/employee/EmployeeClockOutCommand.java b/src/seedu/addressbook/commands/employee/EmployeeClockOutCommand.java new file mode 100644 index 000000000..5ee576df4 --- /dev/null +++ b/src/seedu/addressbook/commands/employee/EmployeeClockOutCommand.java @@ -0,0 +1,74 @@ +package seedu.addressbook.commands.employee; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Set; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.EmployeeName; +import seedu.addressbook.data.employee.Timing; + +//@@author kianhong95 +/** + * Clocks out for the specified employee based on the current time. + */ +public class EmployeeClockOutCommand extends Command { + public static final String COMMAND_WORD = "clockout"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Clocks out with the current time for the specified employee.\n\n" + + "Parameters: NAME\n" + + "Example: " + COMMAND_WORD + " " + + EmployeeName.EXAMPLE; + + public static final String MESSAGE_SUCCESS = "%1$s clocked out on %2$s at %3$s."; + public static final String MESSAGE_NOT_YET_CLOCKED_IN = "%1$s needs to clock in first in order to clock out."; + + private final String name; + + private SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy"); + private SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm"); + private Date date = new Date(); + private final String currentTime = timeFormatter.format(date); + private final String currentDate = dateFormatter.format(date); + + public EmployeeClockOutCommand(String name) { + this.name = name; + } + + /** + * Creates and returns an {@code Attendance} with the details of the current time to clock out. + */ + private Attendance createNewAttendance(Attendance oldAttendance) { + String name = oldAttendance.getName(); + Set updatedTimings = oldAttendance.getTimings(); + + Timing currentTiming = new Timing(this.currentTime, this.currentDate, false); + updatedTimings.add(currentTiming); + + return new Attendance(name, false, updatedTimings); + } + + @Override + public CommandResult execute() { + try { + int index = rms.findAttendanceIndex(name); + + Attendance oldAttendance = rms.findAttendance(index); + boolean isClockedIn = oldAttendance.getClockedIn(); + if (!isClockedIn) { + return new CommandResult(String.format(MESSAGE_NOT_YET_CLOCKED_IN, name)); + } + + Attendance newAttendance = createNewAttendance(oldAttendance); + + rms.updateAttendance(oldAttendance, newAttendance); + return new CommandResult(String.format(MESSAGE_SUCCESS, name, this.currentDate, this.currentTime)); + } catch (IndexOutOfBoundsException ie) { + return new CommandResult(Messages.MESSAGE_EMPLOYEE_NOT_IN_RMS); + } + } +} diff --git a/src/seedu/addressbook/commands/employee/EmployeeCommandResult.java b/src/seedu/addressbook/commands/employee/EmployeeCommandResult.java new file mode 100644 index 000000000..5b9879b5d --- /dev/null +++ b/src/seedu/addressbook/commands/employee/EmployeeCommandResult.java @@ -0,0 +1,17 @@ +package seedu.addressbook.commands.employee; + +import java.util.List; + +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.employee.ReadOnlyEmployee; + +//@@author kianhong95 +/** + * Represents the result of an employee command execution. + */ +public class EmployeeCommandResult extends CommandResult { + + public EmployeeCommandResult(String feedbackToUser, List relevantEmployees) { + super(feedbackToUser, null, null, null, relevantEmployees, null); + } +} diff --git a/src/seedu/addressbook/commands/employee/EmployeeDeleteCommand.java b/src/seedu/addressbook/commands/employee/EmployeeDeleteCommand.java new file mode 100644 index 000000000..1871ef81e --- /dev/null +++ b/src/seedu/addressbook/commands/employee/EmployeeDeleteCommand.java @@ -0,0 +1,52 @@ +package seedu.addressbook.commands.employee; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.employee.UniqueEmployeeList.EmployeeNotFoundException; + +//@@author kianhong95 +/** + * Deletes an employee identified using it's last displayed index from the Rms. + */ +public class EmployeeDeleteCommand extends Command { + + public static final String COMMAND_WORD = "delemp"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Deletes the employee identified by the index number used in the last employee listing.\n" + + "(listemp must be used before this command to retrieve index for employee to be deleted)\n\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_EMPLOYEE_SUCCESS = "Deleted Employee: %1$s"; + + + public EmployeeDeleteCommand(int targetVisibleIndex) { + super(targetVisibleIndex); + } + + + @Override + public CommandResult execute() { + try { + final ReadOnlyEmployee target = getTargetEmployee(); + rms.removeEmployee(target); + + String name = target.getName().fullName; + int index = rms.findAttendanceIndex(name); + Attendance toRemove = rms.findAttendance(index); + rms.removeAttendance(toRemove); + + return new CommandResult(String.format(MESSAGE_DELETE_EMPLOYEE_SUCCESS, target)); + + } catch (IndexOutOfBoundsException ie) { + return new CommandResult(Messages.MESSAGE_INVALID_EMPLOYEE_DISPLAYED_INDEX); + } catch (EmployeeNotFoundException enfe) { + return new CommandResult(Messages.MESSAGE_EMPLOYEE_NOT_IN_RMS); + } + } + +} diff --git a/src/seedu/addressbook/commands/employee/EmployeeEditCommand.java b/src/seedu/addressbook/commands/employee/EmployeeEditCommand.java new file mode 100644 index 000000000..7e0dfe5d6 --- /dev/null +++ b/src/seedu/addressbook/commands/employee/EmployeeEditCommand.java @@ -0,0 +1,128 @@ +package seedu.addressbook.commands.employee; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.employee.EditEmployeeDescriptor; +import seedu.addressbook.data.employee.Employee; +import seedu.addressbook.data.employee.EmployeeAddress; +import seedu.addressbook.data.employee.EmployeeEmail; +import seedu.addressbook.data.employee.EmployeeName; +import seedu.addressbook.data.employee.EmployeePhone; +import seedu.addressbook.data.employee.EmployeePosition; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.employee.UniqueEmployeeList.EmployeeNotFoundException; +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kianhong95 +/** + * Edits the details of an existing employee in the Rms. + */ +public class EmployeeEditCommand extends Command { + public static final String COMMAND_WORD = "editemp"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Edits the details of the employee identified by the index number used in the displayed person list.\n" + + "Existing values will be overwritten by the input values.\n" + + "(listemp must be used before this command to retrieve index for employee to be deleted)\n\n" + + "Parameters: INDEX (must be a positive integer) " + + "[p/PHONE] " + + "[e/EMAIL] " + + "[a/ADDRESS] " + + "[pos/POSITION]\n" + + "Example: " + COMMAND_WORD + " 1 " + + "p/" + EmployeePhone.EXAMPLE; + + public static final String MESSAGE_EDIT_EMPLOYEE_SUCCESS = "Edited Employee: %1$s"; + public static final String MESSAGE_NOARGS = "At least one field to edit must be provided.\n%1$s"; + + private final EditEmployeeDescriptor editEmployeeDescriptor; + + /** + * @param targetVisibleIndex of the person in the filtered person list to edit + * @value editEmployeeDescriptor details to edit the employee with + */ + public EmployeeEditCommand(int targetVisibleIndex, + String phone, + String email, + String address, + String position) throws IllegalValueException { + super(targetVisibleIndex); + this.editEmployeeDescriptor = new EditEmployeeDescriptor(phone, email, address, position); + } + + @Override + public CommandResult execute() { + try { + final ReadOnlyEmployee employeeToEdit = getTargetEmployee(); + Employee editedEmployee = createEditedEmployee(employeeToEdit, editEmployeeDescriptor); + + rms.editEmployee(employeeToEdit, editedEmployee); + + return new CommandResult(String.format(MESSAGE_EDIT_EMPLOYEE_SUCCESS, editedEmployee)); + + } catch (IndexOutOfBoundsException ie) { + return new CommandResult(Messages.MESSAGE_INVALID_EMPLOYEE_DISPLAYED_INDEX); + } catch (EmployeeNotFoundException enfe) { + return new CommandResult(Messages.MESSAGE_EMPLOYEE_NOT_IN_RMS); + } + + } + + /** + * Creates and returns an {@code Employee} with the details of {@code employeeToEdit} + * edited with {@code editEmployeeDescriptor}. + */ + private static Employee createEditedEmployee(ReadOnlyEmployee employeeToEdit, + EditEmployeeDescriptor editEmployeeDescriptor) { + + EmployeeName updatedName = employeeToEdit.getName(); + EmployeePhone updatedPhone = checkPhone(editEmployeeDescriptor.getPhone(), employeeToEdit.getPhone()); + EmployeeEmail updatedEmail = checkEmail(editEmployeeDescriptor.getEmail(), employeeToEdit.getEmail()); + EmployeeAddress updatedAddress = checkAddress(editEmployeeDescriptor.getAddress(), employeeToEdit.getAddress()); + EmployeePosition updatedPosition = checkPosition(editEmployeeDescriptor.getPosition(), + employeeToEdit.getPosition()); + + return new Employee(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedPosition); + } + + /** + * Check for new phone value. + */ + private static EmployeePhone checkPhone(EmployeePhone newEdit, EmployeePhone oldInfo) { + if (newEdit.value.isEmpty()) { + return oldInfo; + } + return newEdit; + } + + /** + * Check for new email value. + */ + private static EmployeeEmail checkEmail(EmployeeEmail newEdit, EmployeeEmail oldInfo) { + if (newEdit.value.isEmpty()) { + return oldInfo; + } + return newEdit; + } + + /** + * Check for new address value. + */ + private static EmployeeAddress checkAddress(EmployeeAddress newEdit, EmployeeAddress oldInfo) { + if (newEdit.value.isEmpty()) { + return oldInfo; + } + return newEdit; + } + + /** + * Check for new position value. + */ + private static EmployeePosition checkPosition(EmployeePosition newEdit, EmployeePosition oldInfo) { + if (newEdit.value.isEmpty()) { + return oldInfo; + } + return newEdit; + } +} diff --git a/src/seedu/addressbook/commands/employee/EmployeeListCommand.java b/src/seedu/addressbook/commands/employee/EmployeeListCommand.java new file mode 100644 index 000000000..1474b1242 --- /dev/null +++ b/src/seedu/addressbook/commands/employee/EmployeeListCommand.java @@ -0,0 +1,27 @@ +package seedu.addressbook.commands.employee; + +import java.util.List; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.employee.ReadOnlyEmployee; + +//@@author kianhong95 +/** + * List all employees. + */ +public class EmployeeListCommand extends Command { + + public static final String COMMAND_WORD = "listemp"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "List all employees. \n\n" + + "Parameters: NIL\n" + + "Example: " + COMMAND_WORD; + + @Override + public CommandResult execute() { + List allEmployees = rms.getAllEmployees().immutableListView(); + return new EmployeeCommandResult(getMessageForEmployeeListShownSummary(allEmployees), allEmployees); + } +} diff --git a/src/seedu/addressbook/commands/member/MemberAddCommand.java b/src/seedu/addressbook/commands/member/MemberAddCommand.java new file mode 100644 index 000000000..27cbee661 --- /dev/null +++ b/src/seedu/addressbook/commands/member/MemberAddCommand.java @@ -0,0 +1,57 @@ +package seedu.addressbook.commands.member; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.exception.IllegalValueException; +import seedu.addressbook.data.member.Member; +import seedu.addressbook.data.member.MemberEmail; +import seedu.addressbook.data.member.MemberName; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.member.UniqueMemberList; + +//@@author kangmingtay +/** + * Adds a member to the member list. + */ +public class MemberAddCommand extends Command { + + public static final String COMMAND_WORD = "addmember"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Adds a member to the Member list. " + + "Contact details can be marked private by prepending 'p' to the prefix.\n\n" + + "Parameters: NAME e/EMAIL \n" + + "Example: " + COMMAND_WORD + + " John Doe e/Example123@gmail.com"; + + public static final String MESSAGE_SUCCESS = "New member added: %1$s"; + public static final String MESSAGE_DUPLICATE_MEMBER = "This member already exists in the member list"; + + private final Member toAdd; + + /** + * Convenience constructor using raw values. + * + * @throws IllegalValueException if any of the raw values are invalid + */ + public MemberAddCommand(String name, String email) throws IllegalValueException { + this.toAdd = new Member( + new MemberName(name), + new MemberEmail(email) + ); + } + + public ReadOnlyMember getMember() { + return toAdd; + } + + @Override + public CommandResult execute() { + try { + rms.addMember(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } catch (UniqueMemberList.DuplicateMemberException dpe) { + return new CommandResult(MESSAGE_DUPLICATE_MEMBER); + } + } + +} diff --git a/src/seedu/addressbook/commands/member/MemberCommandResult.java b/src/seedu/addressbook/commands/member/MemberCommandResult.java new file mode 100644 index 000000000..9297b03e5 --- /dev/null +++ b/src/seedu/addressbook/commands/member/MemberCommandResult.java @@ -0,0 +1,17 @@ +package seedu.addressbook.commands.member; + +import java.util.List; + +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.member.ReadOnlyMember; + +//@@author kangmingtay +/** + * Represents the result of an order command execution. + */ +public class MemberCommandResult extends CommandResult { + + public MemberCommandResult(String feedbackToUser, List relevantMembers) { + super(feedbackToUser, null, null, relevantMembers, null, null); + } +} diff --git a/src/seedu/addressbook/commands/member/MemberDeleteCommand.java b/src/seedu/addressbook/commands/member/MemberDeleteCommand.java new file mode 100644 index 000000000..8eaa0c1ff --- /dev/null +++ b/src/seedu/addressbook/commands/member/MemberDeleteCommand.java @@ -0,0 +1,44 @@ +package seedu.addressbook.commands.member; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.member.UniqueMemberList.MemberNotFoundException; + +//@@author kangmingtay +/** + * Deletes a member identified using it's last displayed index from the member list. + */ +public class MemberDeleteCommand extends Command { + + public static final String COMMAND_WORD = "delmember"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Deletes the member identified by the index number used in the last member listing.\n\t" + + "Parameters: INDEX\n\t" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_MEMBER_SUCCESS = "Deleted member: %1$s"; + + + public MemberDeleteCommand(int targetVisibleIndex) { + super(targetVisibleIndex); + } + + + @Override + public CommandResult execute() { + try { + final ReadOnlyMember target = getTargetMember(); + rms.removeMember(target); + return new CommandResult(String.format(MESSAGE_DELETE_MEMBER_SUCCESS, target)); + + } catch (IndexOutOfBoundsException ie) { + return new CommandResult(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX); + } catch (MemberNotFoundException enfe) { + return new CommandResult(Messages.MESSAGE_MEMBER_NOT_IN_RMS); + } + } + +} diff --git a/src/seedu/addressbook/commands/member/MemberListCommand.java b/src/seedu/addressbook/commands/member/MemberListCommand.java new file mode 100644 index 000000000..f2a05afb1 --- /dev/null +++ b/src/seedu/addressbook/commands/member/MemberListCommand.java @@ -0,0 +1,26 @@ +package seedu.addressbook.commands.member; + +import java.util.List; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.member.ReadOnlyMember; + +//@@author kangmingtay +/** + * Lists all members in the member list to the user. + */ +public class MemberListCommand extends Command { + public static final String COMMAND_WORD = "listmember"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays all members in the member list as a list with index numbers.\n\t" + + "Example: " + COMMAND_WORD; + + + @Override + public CommandResult execute() { + List allMembers = rms.getAllMembers().immutableListView(); + return new MemberCommandResult(getMessageForMemberListShownSummary(allMembers), allMembers); + } +} diff --git a/src/seedu/addressbook/commands/menu/MenuAddCommand.java b/src/seedu/addressbook/commands/menu/MenuAddCommand.java new file mode 100644 index 000000000..222c0e8fb --- /dev/null +++ b/src/seedu/addressbook/commands/menu/MenuAddCommand.java @@ -0,0 +1,80 @@ +package seedu.addressbook.commands.menu; + +import java.util.HashSet; +import java.util.Set; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.exception.IllegalValueException; +import seedu.addressbook.data.menu.Menu; +import seedu.addressbook.data.menu.MenuName; +import seedu.addressbook.data.menu.Price; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.menu.Type; +import seedu.addressbook.data.menu.UniqueMenuList; +import seedu.addressbook.data.tag.Tag; + +//@@author SalsabilTasnia +/** + * Adds a menu item to the Rms. + */ +public class MenuAddCommand extends Command { + + public static final String COMMAND_WORD = "addmenu"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Adds a food item to the Rms.\n\n" + + "Parameters: NAME p/PRICE type/TYPE [t/TAG]...\n" + + "Example: " + COMMAND_WORD + + " Cheese Burger p/$5.00 type/main t/newAddition t/hotSeller\n" + + "Additional Notes:" + + "\ni. PRICE must start with a $ sign and must be integer or float in value of 2 decimal places" + + "\nii. TYPE should only be one of the following categories:" + + "\n" + " - main" + + "\n" + " - sides" + + "\n" + " - beverage" + + "\n" + " - dessert" + + "\n" + " - others" + + "\n" + " - set meal"; + + public static final String MESSAGE_SUCCESS = "New food item added: %1$s"; + public static final String MESSAGE_DUPLICATE_MENU_ITEM = "This food item already exists in the Rms"; + + private final Menu toAddFoodItem; + + /** + * Convenience constructor using raw values. + * + * @throws IllegalValueException if any of the raw values are invalid + */ + public MenuAddCommand(String name, + String price, /*boolean isPricePrivate,*/ + String type, + Set tags) throws IllegalValueException { + final Set tagSet = new HashSet<>(); + for (String tagName : tags) { + tagSet.add(new Tag(tagName)); + } + this.toAddFoodItem = new Menu( + new MenuName(name), + new Price(price), + new Type(type), + tagSet + ); + } + + public ReadOnlyMenus getMenu() { + return toAddFoodItem; + } + + @Override + public CommandResult execute() { + try { + rms.addMenu(toAddFoodItem); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAddFoodItem)); + } catch (UniqueMenuList.DuplicateMenuException dpe) { + return new CommandResult(MESSAGE_DUPLICATE_MENU_ITEM); + } + } + +} + diff --git a/src/seedu/addressbook/commands/menu/MenuClearCommand.java b/src/seedu/addressbook/commands/menu/MenuClearCommand.java new file mode 100644 index 000000000..8e6219aba --- /dev/null +++ b/src/seedu/addressbook/commands/menu/MenuClearCommand.java @@ -0,0 +1,23 @@ +package seedu.addressbook.commands.menu; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; + +//@@author SalsabilTasnia +/** + * Clears the menu list. + */ +public class MenuClearCommand extends Command { + + public static final String COMMAND_WORD = "clearmenu"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Clears menu items permanently.\n\t" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Menu has been cleared!"; + + @Override + public CommandResult execute() { + rms.clearMenu(); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/seedu/addressbook/commands/menu/MenuCommandResult.java b/src/seedu/addressbook/commands/menu/MenuCommandResult.java new file mode 100644 index 000000000..b81772865 --- /dev/null +++ b/src/seedu/addressbook/commands/menu/MenuCommandResult.java @@ -0,0 +1,29 @@ +package seedu.addressbook.commands.menu; + +import java.util.List; + +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.menu.ReadOnlyMenus; + +//@@author SalsabilTasnia +/** + * Represents the result of a command execution. + */ +public class MenuCommandResult extends CommandResult { + + + /** The feedback message to be shown to the user. Contains a description of the execution result */ + //public final String feedbackToUser; + + public MenuCommandResult(String feedbackToUser) { + super(feedbackToUser); + } + + /** + * Returns list of menu items relevant to the command command result_menu, if any. + */ + + public MenuCommandResult(String feedbackToUser, List relevantMenus) { + super(feedbackToUser, relevantMenus, null, null, null, null); + } +} diff --git a/src/seedu/addressbook/commands/menu/MenuDeleteCommand.java b/src/seedu/addressbook/commands/menu/MenuDeleteCommand.java new file mode 100644 index 000000000..1cf8e718b --- /dev/null +++ b/src/seedu/addressbook/commands/menu/MenuDeleteCommand.java @@ -0,0 +1,44 @@ +package seedu.addressbook.commands.menu; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.menu.UniqueMenuList.MenuNotFoundException; + +//@@author SalsabilTasnia +/** + * Deletes a menu item identified using it's last displayed index from the Rms. + */ +public class MenuDeleteCommand extends Command { + + public static final String COMMAND_WORD = "delmenu"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Deletes the menu item identified by the index number used in the last menu listing.\n\t" + + "Parameters: INDEX\n\t" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_MENU_ITEM_SUCCESS = "Deleted Menu Item: %1$s"; + + + public MenuDeleteCommand(int targetVisibleIndex) { + super(targetVisibleIndex); + } + + + @Override + public CommandResult execute() { + try { + final ReadOnlyMenus menutarget = getTargetMenu(); + rms.removeMenuItem(menutarget); + return new CommandResult(String.format(MESSAGE_DELETE_MENU_ITEM_SUCCESS, menutarget)); + + } catch (IndexOutOfBoundsException ie) { + return new CommandResult(Messages.MESSAGE_INVALID_MENU_ITEM_DISPLAYED_INDEX); + } catch (MenuNotFoundException pnfe) { + return new CommandResult(Messages.MESSAGE_MENU_ITEM_NOT_IN_ADDRESSBOOK); + } + } + +} diff --git a/src/seedu/addressbook/commands/menu/MenuFindCommand.java b/src/seedu/addressbook/commands/menu/MenuFindCommand.java new file mode 100644 index 000000000..d193e1a12 --- /dev/null +++ b/src/seedu/addressbook/commands/menu/MenuFindCommand.java @@ -0,0 +1,63 @@ +package seedu.addressbook.commands.menu; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.menu.ReadOnlyMenus; + +//@@author SalsabilTasnia +/** + * Finds and lists all menu items which name contains any of the argument keywords. + * Keyword matching is case sensitive. + */ +public class MenuFindCommand extends Command { + + public static final String COMMAND_WORD = "findmenu"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Finds all menu items whose names contain any of " + + "the specified keywords (case-sensitive) and displays them as a list with index numbers.\n\t" + + "Parameters: KEYWORD [MORE_KEYWORDS]...\n\t" + + "Example: " + COMMAND_WORD + " coke cheese burger"; + + private final Set keywords; + + public MenuFindCommand(Set keywords) { + this.keywords = keywords; + } + + /** + * Returns copy of keywords in this command. + */ + public Set getKeywords() { + return new HashSet<>(keywords); + } + + @Override + public CommandResult execute() { + final List menusFound = getMenuItemsWithNameContainingAnyKeyword(keywords); + return new MenuCommandResult(getMessageForMenuListShownSummary(menusFound), menusFound); + } + + /** + * Retrieve all menu items in the Rms which names contain some of the specified keywords. + * + * @param keywords for searching + * @return list of menu items found + */ + private List getMenuItemsWithNameContainingAnyKeyword(Set keywords) { + final List matchedMenuItems = new ArrayList<>(); + for (ReadOnlyMenus menu : rms.getAllMenus()) { + final Set wordsInName = new HashSet<>(menu.getName().getWordsInName()); + if (!Collections.disjoint(wordsInName, keywords)) { + matchedMenuItems.add(menu); + } + } + return matchedMenuItems; + } + +} diff --git a/src/seedu/addressbook/commands/menu/MenuListByTypeCommand.java b/src/seedu/addressbook/commands/menu/MenuListByTypeCommand.java new file mode 100644 index 000000000..7b81f73cb --- /dev/null +++ b/src/seedu/addressbook/commands/menu/MenuListByTypeCommand.java @@ -0,0 +1,62 @@ +package seedu.addressbook.commands.menu; + +import java.util.ArrayList; +import java.util.List; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.menu.Type; + +//@@author SalsabilTasnia +/** + * Lists all food items of a certain type (or category) in the menu list to the user. + */ +public class MenuListByTypeCommand extends Command { + + public static final String COMMAND_WORD = "listmenutype"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays all food item of a specific category in the Rms system as a list with index numbers.\n\t" + + "Example: " + COMMAND_WORD + " main"; + + public static final String MESSAGE_ERROR = "Invalid menu type searched! " + "\n" + + "Only the following types are available: main, sides, beverage, dessert, others, set meal." + "\n" + + "Only one type search allowed at a time!"; + + private final String itemword; + private final List matchedFoodItems = new ArrayList<>(); + public MenuListByTypeCommand(String itemword) { + this.itemword = itemword; + } + public String getItemword() { + return itemword; + } + + + /** + * Retrieve all menu items in the menu that are of the same type as itemType + * + * @param itemType type of menu items the user wishes to view + * @return a list of menu items of type, 'itemType' + */ + private List getFoodItems(String itemType) { + for (ReadOnlyMenus menuItem : rms.getAllMenus()) { + final String wordsInItemName = menuItem.getType().value; + if (wordsInItemName.equals(itemword)) { + matchedFoodItems.add(menuItem); + } + } + return matchedFoodItems; + } + + @Override + public CommandResult execute() { + final List itemsFound = getFoodItems(itemword); + if (!Type.isValidTypeName(itemword)) { + return new MenuCommandResult(MESSAGE_ERROR); + } + + return new MenuCommandResult(getMessageForMenuListShownSummary(itemsFound), itemsFound); + } +} diff --git a/src/seedu/addressbook/commands/menu/MenuListCommand.java b/src/seedu/addressbook/commands/menu/MenuListCommand.java new file mode 100644 index 000000000..6a6075799 --- /dev/null +++ b/src/seedu/addressbook/commands/menu/MenuListCommand.java @@ -0,0 +1,26 @@ +package seedu.addressbook.commands.menu; + +import java.util.List; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.menu.ReadOnlyMenus; + +//@@author SalsabilTasnia +/** + * Lists all food items in the Rms to the user. + */ +public class MenuListCommand extends Command { + + public static final String COMMAND_WORD = "listmenu"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays all menu items in the Rms system as a list with index numbers.\n\t" + + "Example: " + COMMAND_WORD; + + @Override + public CommandResult execute() { + List allMenus = rms.getAllMenus().immutableListView(); + return new MenuCommandResult(getMessageForMenuListShownSummary(allMenus), allMenus); + } +} diff --git a/src/seedu/addressbook/commands/menu/MenuRecommendationCommand.java b/src/seedu/addressbook/commands/menu/MenuRecommendationCommand.java new file mode 100644 index 000000000..6d4da6965 --- /dev/null +++ b/src/seedu/addressbook/commands/menu/MenuRecommendationCommand.java @@ -0,0 +1,53 @@ +package seedu.addressbook.commands.menu; + +import java.util.List; +import java.util.Map; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.commands.statistics.StatsMenuCommand; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.order.ReadOnlyOrder; + +//@@author SalsabilTasnia +/** + * Lists recommended food items in the menu list to the user. + */ +public class MenuRecommendationCommand extends Command { + + public static final String COMMAND_WORD = "recommendations"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays the best seller items as the recommendations of the month.\n\t" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_NO_RECOMMENDATION = "There are no recommendation yet."; + + /** + * Displays all the best selling items of each category in the menu, if the items in those categories are sold + * The best selling items are obtained from the statistics that determine the best and worst selling items of + * the month + * + * @return the best selling items of each category + */ + private String displayRecommendedItems() { + List allMenus = rms.getAllMenus().immutableListView(); + List allOrders = rms.getAllOrders().immutableListView(); + Map map = StatsMenuCommand.getBs(allOrders, allMenus); + if (map == null) { + return MESSAGE_NO_RECOMMENDATION; + } else { + final StringBuilder builder = new StringBuilder(); + builder.append("Recommendations of the month are:\n\n"); + for (Map.Entry m: map.entrySet()) { + builder.append(m.getKey()).append(" : \n\t").append(m.getValue()).append("\n\n"); + } + return builder.toString(); + } + } + + @Override + public CommandResult execute() { + return new MenuCommandResult(displayRecommendedItems()); + } +} diff --git a/src/seedu/addressbook/commands/menu/MenuShowMainMenuCommand.java b/src/seedu/addressbook/commands/menu/MenuShowMainMenuCommand.java new file mode 100644 index 000000000..ce5914599 --- /dev/null +++ b/src/seedu/addressbook/commands/menu/MenuShowMainMenuCommand.java @@ -0,0 +1,31 @@ +package seedu.addressbook.commands.menu; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; + +//@@author SalsabilTasnia +/** + * Lists all food items in the menu list to the user. + */ +public class MenuShowMainMenuCommand extends Command { + + public static final String COMMAND_WORD = "showmainmenu"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays all the categories of menu items in the Rms system.\n\t" + + "Example: " + COMMAND_WORD; + public static final String MAIN_MENU_DISPLAY = "RMS Main Menu" + "\n" + + "===================================================================================" + + "\n\n" + "Item Category:" + + "\n\n" + " -Main : key in 'listmenutype main' to view all Main items" + + "\n" + " -Sides : key in 'listmenutype sides' to view all Sides" + + "\n" + " -Beverages : key in 'listmenutype beverage' to view all Beverage" + + "\n" + " -Dessert : key in 'listmenutype dessert' to view all Dessert" + + "\n" + " -Others : key in 'listmenutype others' to view all Others" + + "\n" + " -Set Meals : key in 'listmenutype set meal' to view all Set Meal"; + + @Override + public CommandResult execute() { + return new MenuCommandResult(MAIN_MENU_DISPLAY); + } +} diff --git a/src/seedu/addressbook/commands/order/DraftOrderClearCommand.java b/src/seedu/addressbook/commands/order/DraftOrderClearCommand.java new file mode 100644 index 000000000..079708487 --- /dev/null +++ b/src/seedu/addressbook/commands/order/DraftOrderClearCommand.java @@ -0,0 +1,26 @@ +package seedu.addressbook.commands.order; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; + +//@@author px1099 +/** + * Delete all the fields of the draft order. + */ +public class DraftOrderClearCommand extends Command { + + public static final String COMMAND_WORD = "cleardraft"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Delete all the fields of the draft order.\n\t" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "The draft order is cleared.\n%1$s"; + + @Override + public CommandResult execute() { + rms.clearDraftOrder(); + String message = String.format(MESSAGE_SUCCESS, getDraftOrderAsString()); + return new CommandResult(message); + } +} diff --git a/src/seedu/addressbook/commands/order/DraftOrderConfirmCommand.java b/src/seedu/addressbook/commands/order/DraftOrderConfirmCommand.java new file mode 100644 index 000000000..56b20cf1c --- /dev/null +++ b/src/seedu/addressbook/commands/order/DraftOrderConfirmCommand.java @@ -0,0 +1,49 @@ +package seedu.addressbook.commands.order; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.order.Order; +import seedu.addressbook.data.order.ReadOnlyOrder; +import seedu.addressbook.data.order.UniqueOrderList; + +//@@author px1099 +/** + * Confirm the order and put it into the order list. Clear the draft order afterward. + */ +public class DraftOrderConfirmCommand extends Command { + + public static final String COMMAND_WORD = "confirmdraft"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Confirm the order and put it into the order list. Clear the draft order afterward.\n\t" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "The order has been added:\n%1$s"; + public static final String MESSAGE_DRAFT_INCOMPLETE = "The draft needs to be completed before confirming.\n%1$s"; + public static final String MESSAGE_DUPLICATE_ORDER = "This order already exists in the order list"; + + @Override + public CommandResult execute() { + try { + final ReadOnlyOrder draftOrder = rms.getDraftOrder(); + String message; + if (draftOrder.hasDishItems()) { + final Order toAdd = new Order( + draftOrder.getCustomer(), + draftOrder.getDishItems(), + draftOrder.getPoints()); + rms.addOrder(toAdd); + if (draftOrder.hasCustomerField()) { + rms.updatePointsOfCustomer(toAdd.getCustomer(), toAdd.getPrice(), toAdd.getPoints()); + } + rms.clearDraftOrder(); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd.getAsTextAfterAdd())); + } else { + message = String.format(MESSAGE_DRAFT_INCOMPLETE, getDraftOrderAsString()); + return new CommandResult(message); + } + } catch (UniqueOrderList.DuplicateOrderException doe) { + return new CommandResult(MESSAGE_DUPLICATE_ORDER); + } + } +} diff --git a/src/seedu/addressbook/commands/order/DraftOrderEditCustomerCommand.java b/src/seedu/addressbook/commands/order/DraftOrderEditCustomerCommand.java new file mode 100644 index 000000000..b563caf7f --- /dev/null +++ b/src/seedu/addressbook/commands/order/DraftOrderEditCustomerCommand.java @@ -0,0 +1,46 @@ +package seedu.addressbook.commands.order; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.member.ReadOnlyMember; + +//@@author px1099 +/** + * Edit the customer field of the draft order. + * The customer is retrieved with the index of last displayed member list. + */ +public class DraftOrderEditCustomerCommand extends Command { + + public static final String COMMAND_WORD = "draftcustomer"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Add a customer to the draft order. " + + "The customer is identified using the index from the last shown menu list. \n\t" + + "Parameters: INDEX\n\t" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_SUCCESS = "Customer is edited in the draft order.\n%1$s"; + + public DraftOrderEditCustomerCommand(int targetVisibleIndex) { + super(targetVisibleIndex); + } + + + @Override + public CommandResult execute() { + try { + final ReadOnlyMember target = getTargetMember(); + if (!rms.containsMember(target)) { + return new CommandResult(Messages.MESSAGE_MEMBER_NOT_IN_RMS); + } + rms.editDraftOrderPoints(0); + rms.editDraftOrderCustomer(target); + String message = String.format(MESSAGE_SUCCESS, getDraftOrderAsString()); + return new CommandResult(message); + } catch (IndexOutOfBoundsException ie) { + return new CommandResult(Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX); + } + } + +} diff --git a/src/seedu/addressbook/commands/order/DraftOrderEditDishCommand.java b/src/seedu/addressbook/commands/order/DraftOrderEditDishCommand.java new file mode 100644 index 000000000..3124c7860 --- /dev/null +++ b/src/seedu/addressbook/commands/order/DraftOrderEditDishCommand.java @@ -0,0 +1,71 @@ +package seedu.addressbook.commands.order; + +import java.util.HashMap; +import java.util.Map; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.menu.ReadOnlyMenus; + +//@@author px1099 +/** + * Edit the quantity of a dish item of the draft order. + * The dish item is retrieved with the index of last displayed menu. + */ +public class DraftOrderEditDishCommand extends Command { + + public static final String COMMAND_WORD = "draftdish"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Add dishes to the draft order. " + + "The dishes are identified using the indexes from the last shown menu list. \n\t" + + "Parameters: INDEX q/QUANTITY [INDEX q/QUANTITY]...\n\t" + + "Example: " + COMMAND_WORD + " 1 q/4 3 q/2"; + + public static final String MESSAGE_SUCCESS = "The dishes are edited in the draft order.\n%1$s"; + + public static final String MESSAGE_INVALID_FORMAT = "The entered command does not follow the format\n" + + "INDEX must be a non-negative integer\n" + + "QUANTITY must be a non-negative integer of 1-3 digits\n" + + MESSAGE_USAGE; + + public static final String MESSAGE_DUPLICATE_INDEX = "There are duplicate index in the input command"; + + private Map indexQuantityPairs = new HashMap<>(); + + public DraftOrderEditDishCommand(Map indexQuantityPairs) { + super(); + this.indexQuantityPairs.putAll(indexQuantityPairs); + } + + + @Override + public CommandResult execute() { + try { + for (Map.Entry entry: indexQuantityPairs.entrySet()) { + int index = entry.getKey(); + setTargetIndex(index); + ReadOnlyMenus target = getTargetMenu(); + if (!rms.containsMenus(target)) { + return new CommandResult(Messages.MESSAGE_MENU_ITEM_NOT_IN_ADDRESSBOOK); + } + } + for (Map.Entry entry: indexQuantityPairs.entrySet()) { + int index = entry.getKey(); + int quantity = entry.getValue(); + setTargetIndex(index); + ReadOnlyMenus target = getTargetMenu(); + rms.editDraftOrderDishItem(target, quantity); + } + String message = String.format(MESSAGE_SUCCESS, getDraftOrderAsString()); + return new CommandResult(message); + } catch (IndexOutOfBoundsException ie) { + return new CommandResult(Messages.MESSAGE_INVALID_MENU_ITEM_DISPLAYED_INDEX); + } + } + + public Map getIndexQuantityPairs() { + return indexQuantityPairs; + } +} diff --git a/src/seedu/addressbook/commands/order/DraftOrderEditPointsCommand.java b/src/seedu/addressbook/commands/order/DraftOrderEditPointsCommand.java new file mode 100644 index 000000000..42ff3399f --- /dev/null +++ b/src/seedu/addressbook/commands/order/DraftOrderEditPointsCommand.java @@ -0,0 +1,68 @@ +package seedu.addressbook.commands.order; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.exception.IllegalValueException; +import seedu.addressbook.data.member.Points; +import seedu.addressbook.data.order.ReadOnlyOrder; + +//@@author kangmingtay +/** + * Edit the amount of points to redeem from the customer of the draft order. + * The points to be redeemed will be keyed in and retrieved. + */ +public class DraftOrderEditPointsCommand extends Command { + + public static final String COMMAND_WORD = "draftpoints"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Add the amount of member points to be redeemed.\n\t" + + "Parameters: POINTS\n\t" + + "Example: " + COMMAND_WORD + " 50"; + + public static final String MESSAGE_SUCCESS = "Points to be redeemed has been assigned into the draft\n%1$s"; + + public static final String MESSAGE_EMPTY_CUSTOMER_FIELD = "Member needs to be added first!\n%1$s"; + + public static final String MESSAGE_EMPTY_DISH_FIELD = "At least one dish needs to be added first!\n%1$s"; + + public static final String MESSAGE_NO_REDEEMABLE_POINTS = "Member does not have any points to redeem!\n%1$s"; + + public static final String MESSAGE_NEGATIVE_POINTS = "Points to be redeemed must not be a negative value!\n%1$s"; + + private final Points toRedeem; + + public DraftOrderEditPointsCommand(int points) { + this.toRedeem = new Points(points); + } + + + @Override + public CommandResult execute() { + try { + final ReadOnlyOrder draftOrder = rms.getDraftOrder(); + int points = toRedeem.getCurrentPoints(); + if (!draftOrder.hasCustomerField()) { + throw new IllegalValueException(MESSAGE_EMPTY_CUSTOMER_FIELD); + } else if (!draftOrder.hasDishItems()) { + throw new IllegalValueException(MESSAGE_EMPTY_DISH_FIELD); + } else if (!draftOrder.hasPoints()) { + throw new IllegalValueException(MESSAGE_NO_REDEEMABLE_POINTS); + } else if (points < 0) { + throw new IllegalValueException(MESSAGE_NEGATIVE_POINTS); + } else { + final int maxPointsRedeemable = draftOrder.getMaxPointsRedeemable(); + if (points > maxPointsRedeemable) { + points = maxPointsRedeemable; + } + rms.editDraftOrderPoints(points); + String message = String.format(MESSAGE_SUCCESS, getDraftOrderAsString()); + return new CommandResult(message); + } + } catch (IllegalValueException e) { + String message = String.format(e.getMessage(), getDraftOrderAsString()); + return new CommandResult(message); + } + } + +} diff --git a/src/seedu/addressbook/commands/order/OrderAddCommand.java b/src/seedu/addressbook/commands/order/OrderAddCommand.java new file mode 100644 index 000000000..c1f686ac3 --- /dev/null +++ b/src/seedu/addressbook/commands/order/OrderAddCommand.java @@ -0,0 +1,66 @@ +package seedu.addressbook.commands.order; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.commands.member.MemberListCommand; +import seedu.addressbook.commands.menu.MenuFindCommand; +import seedu.addressbook.commands.menu.MenuListByTypeCommand; +import seedu.addressbook.commands.menu.MenuListCommand; + +//@@author px1099 +/** + * Display the current draft and the list of order draft commands used for adding a new order + */ +public class OrderAddCommand extends Command { + + public static final String COMMAND_WORD = "addorder"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Shows the details of the current draft order and the new order drafting instructions.\n\t" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_ALL_ORDER_DRAFT_COMMANDS = "List of commands used for drafting a new order:"; + + public static final String MESSAGE_ALL_ORDER_DRAFT_COMMANDS_USAGES = "1. " + OrderAddCommand.MESSAGE_USAGE + + "\n" + "2. " + DraftOrderEditCustomerCommand.MESSAGE_USAGE + + "\n" + "3. " + DraftOrderEditDishCommand.MESSAGE_USAGE + + "\n" + "4. " + DraftOrderEditPointsCommand.MESSAGE_USAGE + + "\n" + "5. " + DraftOrderClearCommand.MESSAGE_USAGE + + "\n" + "6. " + DraftOrderConfirmCommand.MESSAGE_USAGE + + "\n" + "7. " + MemberListCommand.MESSAGE_USAGE + + "\n" + "8. " + MenuListCommand.MESSAGE_USAGE + + "\n" + "9. " + MenuFindCommand.MESSAGE_USAGE + + "\n" + "10." + MenuListByTypeCommand.MESSAGE_USAGE; + + public static final String MESSAGE_ADD_ORDER_INSTRUCTION = "Adding new order instructions:" + + + "\n\t" + "Step 1: " + "Pick the food to order" + + "\n\t\t" + "* " + "View menu list: " + + MenuListCommand.COMMAND_WORD + "/" + + MenuFindCommand.COMMAND_WORD + "/" + + MenuListByTypeCommand.COMMAND_WORD + + "\n\t\t" + "* " + "Pick a dish item from the list and the quantity of it: " + + DraftOrderEditDishCommand.COMMAND_WORD + + "\n\t\t" + "* " + "Repeat step 1 until the order are completed" + + + "\n\t" + "Step 2: " + "(Optional) Pick a member as the customer and redeem points for discount" + + "\n\t\t" + "* " + "View the member list: " + + MemberListCommand.COMMAND_WORD + + "\n\t\t" + "* " + "Pick a member from the list: " + + DraftOrderEditCustomerCommand.COMMAND_WORD + + "\n\t\t" + "* " + "Assign member points to be used for discount: " + + DraftOrderEditPointsCommand.COMMAND_WORD + + + "\n\t" + "Step 3: " + "Confirm and add the order to the order list: " + + DraftOrderConfirmCommand.COMMAND_WORD; + + @Override + public CommandResult execute() { + String message = getDraftOrderAsString() + + "\n\n" + MESSAGE_ADD_ORDER_INSTRUCTION + + "\n\n" + MESSAGE_ALL_ORDER_DRAFT_COMMANDS + + "\n\n" + MESSAGE_ALL_ORDER_DRAFT_COMMANDS_USAGES; + return new CommandResult(message); + } + +} diff --git a/src/seedu/addressbook/commands/order/OrderClearCommand.java b/src/seedu/addressbook/commands/order/OrderClearCommand.java new file mode 100644 index 000000000..21c066170 --- /dev/null +++ b/src/seedu/addressbook/commands/order/OrderClearCommand.java @@ -0,0 +1,23 @@ +package seedu.addressbook.commands.order; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; + +//@@author px1099 +/** + * Clears the order list. + */ +public class OrderClearCommand extends Command { + + public static final String COMMAND_WORD = "clearorder"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Clears order list permanently.\n\t" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Order list has been cleared!"; + + @Override + public CommandResult execute() { + rms.clearOrderList(); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/seedu/addressbook/commands/order/OrderCommandResult.java b/src/seedu/addressbook/commands/order/OrderCommandResult.java new file mode 100644 index 000000000..46c4efe77 --- /dev/null +++ b/src/seedu/addressbook/commands/order/OrderCommandResult.java @@ -0,0 +1,17 @@ +package seedu.addressbook.commands.order; + +import java.util.List; + +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.order.ReadOnlyOrder; + +//@@author px1099 +/** + * Represents the result of an order command execution. + */ +public class OrderCommandResult extends CommandResult { + + public OrderCommandResult(String feedbackToUser, List relevantOrders) { + super(feedbackToUser, null, relevantOrders, null, null, null); + } +} diff --git a/src/seedu/addressbook/commands/order/OrderDeleteCommand.java b/src/seedu/addressbook/commands/order/OrderDeleteCommand.java new file mode 100644 index 000000000..2332757f2 --- /dev/null +++ b/src/seedu/addressbook/commands/order/OrderDeleteCommand.java @@ -0,0 +1,45 @@ +package seedu.addressbook.commands.order; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.order.ReadOnlyOrder; +import seedu.addressbook.data.order.UniqueOrderList.OrderNotFoundException; + +//@@author px1099 +/** + * Deletes an order identified using it's last displayed index from the order list. + */ +public class OrderDeleteCommand extends Command { + + public static final String COMMAND_WORD = "deleteorder"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Deletes the order identified by the index number used in the last order listing.\n\t" + + "Parameters: INDEX\n\t" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_ORDER_SUCCESS = "Deleted Order: %1$s"; + + + public OrderDeleteCommand(int targetVisibleIndex) { + super(targetVisibleIndex); + } + + + @Override + public CommandResult execute() { + try { + final ReadOnlyOrder target = getTargetOrder(); + rms.removeOrder(target); + String message = String.format(MESSAGE_DELETE_ORDER_SUCCESS, target); + return new CommandResult(message); + + } catch (IndexOutOfBoundsException ie) { + return new CommandResult(Messages.MESSAGE_INVALID_ORDER_DISPLAYED_INDEX); + } catch (OrderNotFoundException pnfe) { + return new CommandResult(Messages.MESSAGE_ORDER_NOT_IN_ORDER_LIST); + } + } + +} diff --git a/src/seedu/addressbook/commands/order/OrderListCommand.java b/src/seedu/addressbook/commands/order/OrderListCommand.java new file mode 100644 index 000000000..f054450f7 --- /dev/null +++ b/src/seedu/addressbook/commands/order/OrderListCommand.java @@ -0,0 +1,27 @@ +package seedu.addressbook.commands.order; + +import java.util.List; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.order.ReadOnlyOrder; + +//@@author px1099 +/** + * Lists all orders in the order list to the user. + */ +public class OrderListCommand extends Command { + + public static final String COMMAND_WORD = "listorder"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays all orders in the order list as a list with index numbers.\n\t" + + "Example: " + COMMAND_WORD; + + + @Override + public CommandResult execute() { + List allOrders = rms.getAllOrders().immutableListView(); + return new OrderCommandResult(getMessageForOrderListShownSummary(allOrders), allOrders); + } +} diff --git a/src/seedu/addressbook/commands/statistics/StatsCommandResult.java b/src/seedu/addressbook/commands/statistics/StatsCommandResult.java new file mode 100644 index 000000000..135654afd --- /dev/null +++ b/src/seedu/addressbook/commands/statistics/StatsCommandResult.java @@ -0,0 +1,29 @@ +package seedu.addressbook.commands.statistics; + +import seedu.addressbook.commands.CommandResult; + +//@@author AngWM +/** + * Represents the result of a command execution. + */ +public class StatsCommandResult extends CommandResult { + + /** The feedback message to be shown to the user. Contains a description of the execution result */ + //public final String feedbackToUser; + + + public StatsCommandResult(String feedbackToUser) { + super(feedbackToUser); + } + + /*public MenuCommandResult(String feedbackToUser, List relevantMenus) { + this.feedbackToUser = feedbackToUser; + this.relevantMenus = relevantMenus; + }*/ + /** + * Returns list of menu items relevant to the command command result_menu, if any. + */ +// public StatsCommandResult(String feedbackToUser, List relevantMenus){ +// super(feedbackToUser, null, relevantMenus, null, null, null); +// } +} diff --git a/src/seedu/addressbook/commands/statistics/StatsEmployeeCommand.java b/src/seedu/addressbook/commands/statistics/StatsEmployeeCommand.java new file mode 100644 index 000000000..2b74d39d8 --- /dev/null +++ b/src/seedu/addressbook/commands/statistics/StatsEmployeeCommand.java @@ -0,0 +1,110 @@ +package seedu.addressbook.commands.statistics; + +import java.util.List; +import java.util.Set; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.employee.Timing; +import seedu.addressbook.data.employee.UniqueAttendanceList; +import seedu.addressbook.data.statistics.AsciiTable; + +//@@author AngWM +/** + * Lists all employee statistics to the user. + */ +public class StatsEmployeeCommand extends Command { + + public static final String COMMAND_WORD = "statsemp"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays statistics information for employees.\n\t" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_NO_EMPLOYEE = "There are no employees in the system."; + + @Override + public CommandResult execute() { + return new StatsCommandResult(getEmployeeOverviewStats()); + } + + private String getEmployeeOverviewStats() { + StringBuilder res = new StringBuilder(); + List allEmployees = rms.getAllEmployees().immutableListView(); + UniqueAttendanceList allAttendance = rms.getAllAttendance(); + if (allEmployees.isEmpty()) { + return MESSAGE_NO_EMPLOYEE; + } + res.append("Number of employees: ").append(allEmployees.size()).append("\n\n"); + res.append("Currently on duty employees: "); + AsciiTable onDuty = createOnDutyTable(); + AsciiTable recentAttendance = createRecentAttendanceTable(); + + int count = 0; + for (ReadOnlyEmployee emp : allEmployees) { + String name = getEmpName(emp); + Attendance attendance = getAttendance(allAttendance, name); + Set timings = getAttendanceTiming(attendance); + Object[] timingArray = timings.toArray(); + int offset = 0; + if (attendance.getClockedIn()) { + offset = 1; + String[] data; + data = new String[]{name, emp.getPosition().value, ((Timing) timingArray[timingArray.length - 1]).time}; + onDuty.addRow(data); + count++; + } + + int j = 0; + for (int i = timingArray.length - 1 - offset; i >= 1 && j < 3; i -= 2) { + String[] data = new String[]{" ", " ", " "}; + + if (j == 0) { + data[0] = emp.getName().fullName; + data[1] = emp.getPosition().value; + } + Timing outTiming = (Timing) timingArray[i]; + Timing inTiming = (Timing) timingArray[i - 1]; + data[2] = inTiming.date + " " + inTiming.time + " - " + outTiming.date + " " + outTiming.time; + j++; + recentAttendance.addRow(data); + } + + } + res.append(count).append("\n"); + if (count != 0) { + res.append(onDuty.toString()); + } + res.append("\n\n"); + res.append("All employees recent attendance\n"); + res.append(recentAttendance.toString()); + res.append("\n\n"); + + return res.toString(); + } + + private String getEmpName(ReadOnlyEmployee emp) { + return emp.getName().fullName; + } + + private Attendance getAttendance(UniqueAttendanceList allAttendance, String name) { + int index = allAttendance.getAttendanceIndex(name); + return allAttendance.getAttendance(index); + } + + private Set getAttendanceTiming(Attendance attendance) { + return attendance.getTimings(); + } + + private AsciiTable createOnDutyTable() { + String[] headings = new String[]{"Name", "Position", "Clocked in"}; + return new AsciiTable(headings); + } + + private AsciiTable createRecentAttendanceTable() { + String[] headings = new String[]{"Name", "Position", "Activity"}; + return new AsciiTable(headings); + } +} diff --git a/src/seedu/addressbook/commands/statistics/StatsHelpCommand.java b/src/seedu/addressbook/commands/statistics/StatsHelpCommand.java new file mode 100644 index 000000000..b72a424d8 --- /dev/null +++ b/src/seedu/addressbook/commands/statistics/StatsHelpCommand.java @@ -0,0 +1,25 @@ +package seedu.addressbook.commands.statistics; + +import seedu.addressbook.commands.Command; + +//@@author AngWM +/** + * Shows help instructions. + */ +public class StatsHelpCommand extends Command { + + public static final String COMMAND_WORD = "statistics"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Shows statistics usage instructions.\n\t" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_ALL_USAGES = StatsEmployeeCommand.MESSAGE_USAGE + + "\n" + StatsMenuCommand.MESSAGE_USAGE + + "\n" + StatsMemberCommand.MESSAGE_USAGE + + "\n" + StatsOrderCommand.MESSAGE_USAGE; + + @Override + public StatsCommandResult execute() { + return new StatsCommandResult(MESSAGE_ALL_USAGES); + } +} diff --git a/src/seedu/addressbook/commands/statistics/StatsMemberCommand.java b/src/seedu/addressbook/commands/statistics/StatsMemberCommand.java new file mode 100644 index 000000000..ca1877c82 --- /dev/null +++ b/src/seedu/addressbook/commands/statistics/StatsMemberCommand.java @@ -0,0 +1,82 @@ +package seedu.addressbook.commands.statistics; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.statistics.AsciiTable; +import seedu.addressbook.data.statistics.MemberDateTable; + +//@@author AngWM +/** + * + */ +public class StatsMemberCommand extends Command { + + public static final String COMMAND_WORD = "statsmember"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays statistics information for members.\n\t" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_NO_MEMBERS = "There are no members in the system."; + + + @Override + public CommandResult execute() { + return new StatsCommandResult(getOverviewStats()); + } + + private String getOverviewStats() { + StringBuilder res = new StringBuilder(); + List allMembers = rms.getAllMembers().immutableListView(); + if (allMembers.isEmpty()) { + return MESSAGE_NO_MEMBERS; + } + MemberDateTable dateTable = new MemberDateTable(); + int[] tierCount = new int[]{0, 0, 0}; + for (ReadOnlyMember member : allMembers) { + Date signupDate = member.getDate(); + dateTable.addData(signupDate); + String tier = getTier(member); + if (tier.equalsIgnoreCase("Bronze")) { + tierCount[0]++; + } else if (tier.equalsIgnoreCase("Silver")) { + tierCount[1]++; + } else if (tier.equalsIgnoreCase("Gold")) { + tierCount[2]++; + } + } + res.append("Number of members: ").append(allMembers.size()).append("\n\n"); + res.append("New members this year: ").append(dateTable.getYearCount(new Date())).append("\n\n"); + res.append("New members this month: ").append(dateTable.getMonthCount(new Date())).append("\n\n"); + res.append("New members today: ").append(dateTable.getDayCount()); + res.append("\n\n\n"); + + res.append("Tier Table\n"); + AsciiTable table = createTierTable(); + String[] values = convertIntArrToStrArr(tierCount); + table.addRow(values); + res.append(table.toString()); + + return res.toString(); + } + + private AsciiTable createTierTable() { + String[] headings = new String[]{"Bronze", "Silver", "Gold"}; + return new AsciiTable(headings); + } + + private String[] convertIntArrToStrArr(int[] in) { + String listString = Arrays.toString(in); + return listString.replaceAll("[\\[\\]]", "").split("\\s*,\\s*"); + } + + private String getTier(ReadOnlyMember member) { + return member.getMemberTier().toString(); + } + +} diff --git a/src/seedu/addressbook/commands/statistics/StatsMenuCommand.java b/src/seedu/addressbook/commands/statistics/StatsMenuCommand.java new file mode 100644 index 000000000..025a350fc --- /dev/null +++ b/src/seedu/addressbook/commands/statistics/StatsMenuCommand.java @@ -0,0 +1,213 @@ +package seedu.addressbook.commands.statistics; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.order.ReadOnlyOrder; +import seedu.addressbook.data.statistics.AsciiTable; +import seedu.addressbook.data.statistics.QuantityRevenuePair; + +//@@author AngWM +/** + * Lists all menu statistics in the Rms to the user. + */ +public class StatsMenuCommand extends Command { + + public static final String COMMAND_WORD = "statsmenu"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays statistics information for menu items.\n" + + "Select date range from ddmmyyyy to ddmmyyyy with f/ddmmyyyy and t/ddmmyyyy\n\t" + + "Format: " + COMMAND_WORD + " [f/DDMMYYYY] [t/DDMMYYYY]\n\t" + + "Example: " + COMMAND_WORD + " f/12122017 t/11112018\n\t" + + " " + COMMAND_WORD + " f/01012018"; + + public static final String MESSAGE_NO_ORDER = "There are no orders in the system to calculate menu stats."; + + private Date dateFrom; + private Date dateTo; + private String heading; + + public StatsMenuCommand(String dateFrom, String dateTo) { + DateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy"); + StringBuilder sb = new StringBuilder(); + sb.append("Displaying menu statistics "); + if (dateFrom != null) { + this.dateFrom = stringToDate(dateFrom); + sb.append("from ").append(dateFormat.format(this.dateFrom)).append(" "); + } else { + this.dateFrom = new Date(0); + } + if (dateTo != null) { + this.dateTo = stringToDate(dateTo); + sb.append("until ").append(dateFormat.format(this.dateTo)); + } else { + this.dateTo = new Date(); + } + sb.append("\n================\n\n"); + this.heading = sb.toString(); + } + + @Override + public CommandResult execute() { + if (getMenuStats().equalsIgnoreCase(MESSAGE_NO_ORDER)) { + return new StatsCommandResult(MESSAGE_NO_ORDER); + } else { + return new StatsCommandResult(heading + getMenuStats()); + } + } + + private String getMenuStats() { + StringBuilder sb = new StringBuilder(); + List allOrders = rms.getAllOrders().immutableListView(); + if (allOrders.isEmpty()) { + return MESSAGE_NO_ORDER; + } + List allMenu = rms.getAllMenus().immutableListView(); + Map allMenuSales = new TreeMap<>(); + Map bestsellers = new HashMap<>(); + Map worstsellers = new HashMap<>(); + + // For every menu in every order, add the menu and quantity sold into allMenuSales + for (ReadOnlyOrder order : allOrders) { + Date orderDate = order.getDate(); + if (orderDate.compareTo(dateFrom) < 0 || orderDate.compareTo(dateTo) > 0) { + continue; + } + Map dishItems = order.getDishItems(); + // ========================================== + for (Map.Entry entry : dishItems.entrySet()) { + if (!allMenuSales.containsKey(entry.getKey())) { + allMenuSales.put(entry.getKey(), + new QuantityRevenuePair(entry.getValue(), + entry.getKey().getPrice().convertValueOfPriceToDouble())); + } else { + allMenuSales.put(entry.getKey(), + allMenuSales.get(entry.getKey()).addData(entry.getValue(), + entry.getKey().getPrice().convertValueOfPriceToDouble())); + } + } + } + + // Check for menu items with no sales and insert into allMenuSales + for (ReadOnlyMenus menu: allMenu) { + if (!allMenuSales.containsKey(menu)) { + allMenuSales.put(menu, new QuantityRevenuePair()); + } + } + + // Sort allMenuSales by quantity sold + List> sortedMenu = Utils.sortByValue(allMenuSales); + for (int i = sortedMenu.size() - 1; i >= 0; i--) { + ReadOnlyMenus menu = sortedMenu.get(i).getKey(); + int quantity = sortedMenu.get(i).getValue().getQuantity(); + sb.append(menu.getName()); + sb.append(" sold ").append(quantity).append("\n"); + + // Replace with menu.type during merge + String type = menu.getType().value; + // ========================================== + if (!bestsellers.containsKey(type) && quantity > 0) { + bestsellers.put(type, menu); + } else if (quantity < 100) { + worstsellers.put(type, menu); + } + } + + sb.append("\n\nBest Sellers\n"); + sb.append(toTable(bestsellers, allMenuSales)); + + sb.append("Unpopular Items\n"); + sb.append(toTable(worstsellers, allMenuSales)); + + return sb.toString(); + } + + /** + * Parse the data into a table and return the table as a String + */ + private String toTable(Map in, Map allMenuSales) { + String[] tableHeadings = {"Type", "Name", "Quantity Sold", "Sales Revenue"}; + AsciiTable table = new AsciiTable(tableHeadings); + for (Map.Entry worstEntry : in.entrySet()) { + String type = worstEntry.getKey(); + String menuName = worstEntry.getValue().getName().toString(); + int quantity = allMenuSales.get(worstEntry.getValue()).getQuantity(); + String revenue = Utils.formatCurrency(allMenuSales.get(worstEntry.getValue()).getRevenue()); + String[] rowData = {type, menuName, Integer.toString(quantity), "$" + revenue}; + table.addRow(rowData); + } + return table.toString(); + } + + /** + * Convert a date String into a Date object + */ + private Date stringToDate(String input) { + Calendar calendar = new GregorianCalendar(); + calendar.set(Integer.parseInt(input.substring(4)), + Integer.parseInt(input.substring(2, 4)) - 1, + Integer.parseInt(input.substring(0, 2))); + return calendar.getTime(); + } + + public static Map getBs(List allOrders, List allMenu) { + if (allOrders.isEmpty()) { + return null; + } + Map allMenuSales = new TreeMap<>(); + Map bestsellers = new HashMap<>(); + + // For every menu in every order, add the menu and quantity sold into allMenuSales + for (ReadOnlyOrder order : allOrders) { + Map dishItems = order.getDishItems(); + // ========================================== + for (Map.Entry entry : dishItems.entrySet()) { + if (!allMenuSales.containsKey(entry.getKey())) { + int quantity = entry.getValue(); + double revenue = entry.getKey().getPrice().convertValueOfPriceToDouble(); + QuantityRevenuePair qr = new QuantityRevenuePair(quantity, revenue); + allMenuSales.put(entry.getKey(), qr); + } else { + int quantity = entry.getValue(); + double revenue = entry.getKey().getPrice().convertValueOfPriceToDouble(); + QuantityRevenuePair qr = allMenuSales.get(entry.getKey()); + qr.addData(quantity, revenue); + allMenuSales.put(entry.getKey(), qr); + } + } + } + + // Check for menu items with no sales and insert into allMenuSales + for (ReadOnlyMenus menu: allMenu) { + if (!allMenuSales.containsKey(menu)) { + allMenuSales.put(menu, new QuantityRevenuePair()); + } + } + + // Sort allMenuSales by quantity sold + List> sortedMenu = Utils.sortByValue(allMenuSales); + for (int i = sortedMenu.size() - 1; i >= 0; i--) { + ReadOnlyMenus menu = sortedMenu.get(i).getKey(); + int quantity = sortedMenu.get(i).getValue().getQuantity(); + String type = menu.getType().value; + // ========================================== + if (!bestsellers.containsKey(type) && quantity > 0) { + bestsellers.put(type, menu); + } + } + + return bestsellers; + } +} diff --git a/src/seedu/addressbook/commands/statistics/StatsOrderCommand.java b/src/seedu/addressbook/commands/statistics/StatsOrderCommand.java new file mode 100644 index 000000000..776482ba7 --- /dev/null +++ b/src/seedu/addressbook/commands/statistics/StatsOrderCommand.java @@ -0,0 +1,96 @@ +package seedu.addressbook.commands.statistics; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.List; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.CommandResult; +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.order.ReadOnlyOrder; +import seedu.addressbook.data.statistics.AsciiTable; +import seedu.addressbook.data.statistics.OrderDateTable; + +//@@author AngWM +/** + * Lists all order statistics in the Rms to the user. + */ +public class StatsOrderCommand extends Command { + + public static final String COMMAND_WORD = "statsorder"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + + "Displays statistics information for orders.\n\t" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_NO_ORDER = "There are no orders in the system."; + + + @Override + public CommandResult execute() { + return new StatsCommandResult(getOrderStats()); + } + + private String getOrderStats() { + StringBuilder sb = new StringBuilder(); + List allOrders = rms.getAllOrders().immutableListView(); + if (allOrders.isEmpty()) { + return MESSAGE_NO_ORDER; + } + + OrderDateTable dateTable = new OrderDateTable(); + + for (ReadOnlyOrder order : allOrders) { + dateTable.addData(order); + } + Date currentDate = new Date(); + Calendar calendar = new GregorianCalendar(); + calendar.setTime(currentDate); + + sb.append("This year's statistics\n"); + sb.append("========================\n"); + sb.append("Number of orders: ").append(Integer.toString(dateTable.getYearCount(currentDate))).append("\n"); + sb.append("Revenue: $").append(Utils.formatCurrency(dateTable.getYearRevenue(currentDate))).append("\n\n"); + sb.append("This month's statistics\n"); + sb.append("========================\n"); + sb.append("Number of orders: ").append(Integer.toString(dateTable.getMonthCount(currentDate))).append("\n"); + sb.append("Revenue: $").append(Utils.formatCurrency(dateTable.getMonthRevenue(currentDate))).append("\n\n"); + sb.append("Today's statistics\n"); + sb.append("========================\n"); + sb.append("Number of orders: ").append(Integer.toString(dateTable.getDayCount(currentDate))).append("\n"); + sb.append("Revenue: $").append(Utils.formatCurrency(dateTable.getDayRevenue(currentDate))); + sb.append("\n\n\n"); + + sb.append("Past 12 Months Sales\n"); + int currentMonth = calendar.get(Calendar.MONTH); + int currentYear = calendar.get(Calendar.YEAR); + String[] months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + months = rotateRight(months, 12 - currentMonth); + AsciiTable table = new AsciiTable(months); + String[] dataRow = new String[12]; + for (int i = 0; i < 12; i++) { + calendar.set(Calendar.MONTH, i); + if (currentMonth <= i) { + calendar.set(Calendar.YEAR, currentYear - 1); + } + dataRow[i] = "$" + Utils.formatCurrency((dateTable.getMonthRevenue(calendar.getTime()))); + } + dataRow = rotateRight(dataRow, 12 - currentMonth); + table.addRow(dataRow); + sb.append(table.toString()); + return sb.toString(); + } + + /** + * Rotate the columns to the right to display the last 12 months in correct order + */ + private String[] rotateRight(String[] in, int rotation) { + String[] out = in.clone(); + for (int x = 0; x <= in.length - 1; x++) { + out[(x + rotation) % in.length ] = in[x]; + } + + return out; + } +} diff --git a/src/seedu/addressbook/data/person/Email.java b/src/seedu/addressbook/common/Email.java similarity index 60% rename from src/seedu/addressbook/data/person/Email.java rename to src/seedu/addressbook/common/Email.java index c946f1eb3..bfec9976e 100644 --- a/src/seedu/addressbook/data/person/Email.java +++ b/src/seedu/addressbook/common/Email.java @@ -1,37 +1,41 @@ -package seedu.addressbook.data.person; +package seedu.addressbook.common; import seedu.addressbook.data.exception.IllegalValueException; /** - * Represents a Person's email in the address book. + * Represents a Person's email in the Rms. * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} */ public class Email { - - public static final String EXAMPLE = "valid@e.mail"; + public static final String EXAMPLE = "Example2018@rms.com"; public static final String MESSAGE_EMAIL_CONSTRAINTS = - "Person emails should be 2 alphanumeric/period strings separated by '@'"; - public static final String EMAIL_VALIDATION_REGEX = "[\\w\\.]+@[\\w\\.]+"; + "Emails should be 2 alphanumeric/period strings that are no longer than 20 characters separated by '@'"; + public static final String EMAIL_VALIDATION_REGEX = "[\\w.]{1,20}+@[\\w.]{1,20}+"; public final String value; - private boolean isPrivate; + + /** + * Empty constructor + */ + public Email() { + this.value = ""; + } /** * Validates given email. * * @throws IllegalValueException if given email address string is invalid. */ - public Email(String email, boolean isPrivate) throws IllegalValueException { - this.isPrivate = isPrivate; - email = email.trim(); - if (!isValidEmail(email)) { + public Email(String email) throws IllegalValueException { + String trimmedEmail = email.trim(); + if (!isValidEmail(trimmedEmail)) { throw new IllegalValueException(MESSAGE_EMAIL_CONSTRAINTS); } - this.value = email; + this.value = trimmedEmail; } /** - * Checks if a given string is a valid person email. + * Checks if a given string is a valid email. */ public static boolean isValidEmail(String test) { return test.matches(EMAIL_VALIDATION_REGEX); @@ -54,8 +58,4 @@ public int hashCode() { return value.hashCode(); } - - public boolean isPrivate() { - return isPrivate; - } -} \ No newline at end of file +} diff --git a/src/seedu/addressbook/common/Messages.java b/src/seedu/addressbook/common/Messages.java index 02cfe6155..bd419e85d 100644 --- a/src/seedu/addressbook/common/Messages.java +++ b/src/seedu/addressbook/common/Messages.java @@ -6,11 +6,45 @@ public class Messages { public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSON_NOT_IN_ADDRESSBOOK = "Person could not be found in address book"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - public static final String MESSAGE_PROGRAM_LAUNCH_ARGS_USAGE = "Launch command format: " + - "java seedu.addressbook.Main [STORAGE_FILE_PATH]"; - public static final String MESSAGE_WELCOME = "Welcome to your Address Book!"; - public static final String MESSAGE_USING_STORAGE_FILE = "Using storage file : %1$s"; + //@@author px1099 + public static final String MESSAGE_ENTERED_COMMAND_FORMAT = "Entered command: %1$s\n"; + + //@@author kianhong95 + public static final String MESSAGE_INVALID_EMPLOYEE_DISPLAYED_INDEX = "The employee index provided is invalid."; + public static final String MESSAGE_EMPLOYEE_NOT_IN_RMS = "Employee could not be found in Rms."; + public static final String MESSAGE_EMPLOYEES_LISTED_OVERVIEW = "%1$d employees listed."; + public static final String MESSAGE_NO_EMPLOYEES_IN_SYSTEM = "There are currently no employees added in the system."; + + //@@author kangmingtay + public static final String MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX = "The member index provided is invalid"; + public static final String MESSAGE_MEMBER_NOT_IN_RMS = "Member could not be found in Rms"; + public static final String MESSAGE_MEMBERS_LISTED_OVERVIEW = "%1$d members listed!"; + + //@@author SalsabilTasnia + public static final String MESSAGE_INVALID_MENU_ITEM_DISPLAYED_INDEX = "The menu item index provided is invalid"; + public static final String MESSAGE_MENU_ITEM_NOT_IN_ADDRESSBOOK = "Menu item could not be found in Rms"; + public static final String MESSAGE_MENUS_LISTED_OVERVIEW = "%1$d food items listed!"; + + //@@author px1099 + public static final String MESSAGE_INVALID_ORDER_DISPLAYED_INDEX = "The order index provided is invalid"; + public static final String MESSAGE_DRAFT_ORDER_DETAILS = "Current draft order:\n%1$s"; + public static final String MESSAGE_ORDER_NOT_IN_ORDER_LIST = "Order could not be found in order list"; + public static final String MESSAGE_ORDERS_LISTED_OVERVIEW = "%1$d orders listed!"; + public static final String MESSAGE_USING_ORDER_LIST_STORAGE_FILE = "Using order list storage file : %1$s"; + + //@@author + public static final String MESSAGE_PROGRAM_LAUNCH_ARGS_USAGE = "Launch command format: " + + "java seedu.addressbook.Main [STORAGE_FILE_PATH]"; + + //@@author SalsabilTasnia + public static final String MESSAGE_WELCOME = "Welcome to Restaurant Management System! \n" + + "- To access EMPLOYEE DATABASE, key in 'listemp'\n" + + "- To access MEMBER DATABASE, key in 'listmember'\n" + + "- To access MENU, key in 'listmenu'\n" + + "- To access ORDER, key in 'listorder'\n" + + "- To access STATISITCS information, key in 'statistics'\n" + + "- For further assistance, key in 'help'\n" + + "----------------------------------------------------------"; + + //@@author } diff --git a/src/seedu/addressbook/data/person/Name.java b/src/seedu/addressbook/common/Name.java similarity index 69% rename from src/seedu/addressbook/data/person/Name.java rename to src/seedu/addressbook/common/Name.java index 487b7ad9c..b93928e06 100644 --- a/src/seedu/addressbook/data/person/Name.java +++ b/src/seedu/addressbook/common/Name.java @@ -1,19 +1,17 @@ -package seedu.addressbook.data.person; +package seedu.addressbook.common; import seedu.addressbook.data.exception.IllegalValueException; -import java.util.Arrays; -import java.util.List; - /** - * Represents a Person's name in the address book. + * Represents a Person's name in the Rms. * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} */ public class Name { public static final String EXAMPLE = "John Doe"; - public static final String MESSAGE_NAME_CONSTRAINTS = "Person names should be spaces or alphanumeric characters"; - public static final String NAME_VALIDATION_REGEX = "[\\p{Alnum} ]+"; + public static final String MESSAGE_NAME_CONSTRAINTS = "Name cannot be longer than " + + "30 alphanumeric characters and spaces."; + public static final String NAME_VALIDATION_REGEX = "[\\p{Alnum} ]{1,30}+"; public final String fullName; @@ -23,11 +21,11 @@ public class Name { * @throws IllegalValueException if given name string is invalid. */ public Name(String name) throws IllegalValueException { - name = name.trim(); - if (!isValidName(name)) { + String trimmedName = name.trim(); + if (!isValidName(trimmedName)) { throw new IllegalValueException(MESSAGE_NAME_CONSTRAINTS); } - this.fullName = name; + this.fullName = trimmedName; } /** @@ -37,13 +35,6 @@ public static boolean isValidName(String test) { return test.matches(NAME_VALIDATION_REGEX); } - /** - * Retrieves a listing of every word in the name, in order. - */ - public List getWordsInName() { - return Arrays.asList(fullName.split("\\s+")); - } - @Override public String toString() { return fullName; diff --git a/src/seedu/addressbook/common/Utils.java b/src/seedu/addressbook/common/Utils.java index 245f347c4..427f5c7b1 100644 --- a/src/seedu/addressbook/common/Utils.java +++ b/src/seedu/addressbook/common/Utils.java @@ -1,9 +1,16 @@ package seedu.addressbook.common; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Set; + /** * Utility methods */ @@ -21,6 +28,15 @@ public static boolean isAnyNull(Object... items) { return false; } + //@@author kianhong95 + /** + * Returns true if {@code items} contain any elements that are non-null. + */ + public static boolean isAnyNonNull(Object... items) { + return items != null && Arrays.stream(items).anyMatch(Objects::nonNull); + } + + //@@author /** * Checks if every element in a collection are unique by {@link Object#equals(Object)}. */ @@ -34,4 +50,49 @@ public static boolean elementsAreUnique(Collection items) { } return true; } + + //@@author AngWM + /** + * Sort a map by the values + */ + public static > List> sortByValue(Map map) { + List> list = new ArrayList<>(map.entrySet()); + list.sort(Map.Entry.comparingByValue()); + + return list; + } + + /** + * Format a double into a currency String + */ + public static String formatCurrency(double input) { + DecimalFormat df = new DecimalFormat("0.00"); + return df.format(input); + } + + //@@author px1099 + /** + * Create blank space to position the next String at an exact distance compared to the start of the prefix String. + * @param prefix the String after which the blank space is inserted. + * @param distance the distance between the start of the two String that the blank space between them aims to fill. + * @return the filler space required + */ + public static String blankSpace(String prefix, int distance) { + StringBuilder sb = new StringBuilder(); + int fillingSpaceLength = distance - prefix.length(); + if (fillingSpaceLength > 0) { + for (int i = 0; i < fillingSpaceLength; i++) { + sb.append(" "); + } + } else { + return "\t"; + } + return sb.toString(); + } + + public static String blankSpace(int distance) { + return blankSpace("", distance); + } + + //@@author } diff --git a/src/seedu/addressbook/data/AddressBook.java b/src/seedu/addressbook/data/AddressBook.java deleted file mode 100644 index ba69c21e3..000000000 --- a/src/seedu/addressbook/data/AddressBook.java +++ /dev/null @@ -1,86 +0,0 @@ -package seedu.addressbook.data; - -import seedu.addressbook.data.person.Person; -import seedu.addressbook.data.person.ReadOnlyPerson; -import seedu.addressbook.data.person.UniquePersonList; -import seedu.addressbook.data.person.UniquePersonList.DuplicatePersonException; -import seedu.addressbook.data.person.UniquePersonList.PersonNotFoundException; - -/** - * Represents the entire address book. Contains the data of the address book. - */ -public class AddressBook { - - private final UniquePersonList allPersons; - - public static AddressBook empty() { - return new AddressBook(); - } - - /** - * Creates an empty address book. - */ - public AddressBook() { - allPersons = new UniquePersonList(); - } - - /** - * Constructs an address book with the given data. - * - * @param persons external changes to this will not affect this address book - */ - public AddressBook(UniquePersonList persons) { - this.allPersons = new UniquePersonList(persons); - } - - /** - * Adds a person to the address book. - * - * @throws DuplicatePersonException if an equivalent person already exists. - */ - public void addPerson(Person toAdd) throws DuplicatePersonException { - allPersons.add(toAdd); - } - - /** - * Checks if an equivalent person exists in the address book. - */ - public boolean containsPerson(ReadOnlyPerson key) { - return allPersons.contains(key); - } - - /** - * Removes the equivalent person from the address book. - * - * @throws PersonNotFoundException if no such Person could be found. - */ - public void removePerson(ReadOnlyPerson toRemove) throws PersonNotFoundException { - allPersons.remove(toRemove); - } - - /** - * Clears all persons from the address book. - */ - public void clear() { - allPersons.clear(); - } - - /** - * Defensively copied UniquePersonList of all persons in the address book at the time of the call. - */ - public UniquePersonList getAllPersons() { - return new UniquePersonList(allPersons); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddressBook // instanceof handles nulls - && this.allPersons.equals(((AddressBook) other).allPersons)); - } - - @Override - public int hashCode() { - return allPersons.hashCode(); - } -} diff --git a/src/seedu/addressbook/data/Rms.java b/src/seedu/addressbook/data/Rms.java new file mode 100644 index 000000000..ad6775b81 --- /dev/null +++ b/src/seedu/addressbook/data/Rms.java @@ -0,0 +1,336 @@ +package seedu.addressbook.data; + +import java.util.Objects; + +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.Employee; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.employee.UniqueAttendanceList; +import seedu.addressbook.data.employee.UniqueEmployeeList; +import seedu.addressbook.data.employee.UniqueEmployeeList.DuplicateEmployeeException; +import seedu.addressbook.data.employee.UniqueEmployeeList.EmployeeNotFoundException; +import seedu.addressbook.data.member.Member; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.member.UniqueMemberList; +import seedu.addressbook.data.member.UniqueMemberList.DuplicateMemberException; +import seedu.addressbook.data.member.UniqueMemberList.MemberNotFoundException; +import seedu.addressbook.data.menu.Menu; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.menu.UniqueMenuList; +import seedu.addressbook.data.menu.UniqueMenuList.DuplicateMenuException; +import seedu.addressbook.data.menu.UniqueMenuList.MenuNotFoundException; +import seedu.addressbook.data.order.Order; +import seedu.addressbook.data.order.ReadOnlyOrder; +import seedu.addressbook.data.order.UniqueOrderList; +import seedu.addressbook.data.order.UniqueOrderList.DuplicateOrderException; +import seedu.addressbook.data.order.UniqueOrderList.OrderNotFoundException; + +/** + * Represents the entire Rms. Contains the data of the Rms. + */ +public class Rms { + + //@@author kianhong95 + private final UniqueEmployeeList allEmployees; + //@@author kangmingtay + private final UniqueMemberList allMembers; + //@@author SalsabilTasnia + private final UniqueMenuList allFoodItems; + //@@author px1099 + private final UniqueOrderList allOrders; + //@@author kianhong95 + private final UniqueAttendanceList allAttendance; + + //@@author px1099 + private Order draftOrder = new Order(); + + //@@author AngWM + /** + * Creates an empty Rms. + */ + public Rms() { + allEmployees = new UniqueEmployeeList(); + allMembers = new UniqueMemberList(); + allFoodItems = new UniqueMenuList(); + allOrders = new UniqueOrderList(); + allAttendance = new UniqueAttendanceList(); + } + + /** + * Constructs an Rms with the given data. + */ + public Rms(UniqueMenuList menus, + UniqueEmployeeList employees, + UniqueOrderList orders, + UniqueMemberList members, + UniqueAttendanceList attendances) { + this.allEmployees = new UniqueEmployeeList(employees); + this.allMembers = new UniqueMemberList(members); + this.allFoodItems = new UniqueMenuList(menus); + this.allOrders = new UniqueOrderList(orders); + this.allAttendance = new UniqueAttendanceList(attendances); + } + + public static Rms empty() { + return new Rms(); + } + + //@@author kianhong95 + /** + * Adds an employee to the Rms. + * + * @throws DuplicateEmployeeException if an equivalent employee already exists. + */ + public void addEmployee(Employee toAdd) throws DuplicateEmployeeException { + allEmployees.add(toAdd); + } + + /** + * Adds an attendance list with the specified employee to the Rms. + */ + public void addAttendance(Attendance toAdd) { + allAttendance.add(toAdd); + } + + /** + * Gets index of the specified Attendance object. + */ + public int findAttendanceIndex(String toFind) { + return allAttendance.getAttendanceIndex(toFind); + } + + + /** + * Gets index of the specified Attendance object. + */ + public Attendance findAttendance(int toFind) { + return allAttendance.getAttendance(toFind); + } + + /** + * Adds an attendance list with the specified employee to the Rms. + */ + public void updateAttendance(Attendance oldAttendance, Attendance newAttendance) { + allAttendance.setAttendance(oldAttendance, newAttendance); } + + //@@author kangmingtay + /** + * Adds a member to the Member list. + * + * @throws DuplicateMemberException if an equivalent member already exists. + */ + + public void addMember(Member toAdd) throws DuplicateMemberException { + allMembers.add(toAdd); + } + + //@@author SalsabilTasnia + /** + * Adds a menu item to the menu list. + * + * @throws DuplicateMenuException if an equivalent menu item already exists. + */ + public void addMenu(Menu toAdd) throws DuplicateMenuException { + allFoodItems.add(toAdd); + } + + //@@author px1099 + /** + * Adds an order to the order list. + * + * @throws DuplicateOrderException if an equivalent order already exists. + */ + public void addOrder(Order toAdd) throws DuplicateOrderException { + allOrders.add(toAdd); + } + + //@@author SalsabilTasnia + /** + * Checks if an equivalent menu item exists in the menu list. + */ + public boolean containsMenus(ReadOnlyMenus key) { + return allFoodItems.contains(key); + } + + //@@author kangmingtay + /** + * Checks if an equivalent member exists in the member list. + */ + public boolean containsMember(ReadOnlyMember key) { + return allMembers.contains(key); + } + + //@@author SalsabilTasnia + /** + * Removes the equivalent menu item from the menu. + * + * @throws MenuNotFoundException if no such Order could be found. + */ + public void removeMenuItem(ReadOnlyMenus toRemove) throws MenuNotFoundException { + allFoodItems.remove(toRemove); + } + + //@@author px1099 + /** + * Removes the equivalent order from the order list. + * + * @throws OrderNotFoundException if no such Order could be found. + */ + public void removeOrder(ReadOnlyOrder toRemove) throws OrderNotFoundException { + allOrders.remove(toRemove); + } + + //@@author kangmingtay + /** + * Removes the equivalent member from the member list. + * + * @throws MemberNotFoundException if no such Member could be found. + */ + public void removeMember(ReadOnlyMember toRemove) throws MemberNotFoundException { + allMembers.remove(toRemove); + } + + //@@author kianhong95 + /** + * Removes the equivalent employee from the Rms. + * + * @throws EmployeeNotFoundException if no such Employee could be found. + */ + public void removeEmployee(ReadOnlyEmployee toRemove) throws EmployeeNotFoundException { + allEmployees.remove(toRemove); + } + + /** + * Removes an attendance list with the specified employee from the Rms. + */ + public void removeAttendance(Attendance toRemove) { + allAttendance.remove(toRemove); + } + + /** + * Edits the equivalent employee from Rms + * + * @throws EmployeeNotFoundException if no such Employee could be found. + */ + public void editEmployee(ReadOnlyEmployee toRemove, Employee toReplace) throws EmployeeNotFoundException { + allEmployees.edit(toRemove, toReplace); + } + + //@@author SalsabilTasnia + /** + * Clears all menu items from the menu. + */ + public void clearMenu() { + allFoodItems.clear(); + } + + //@@author px1099 + /** + * Clears all orders from the order list. + */ + public void clearOrderList() { + allOrders.clear(); + } + + //@@author kangmingtay + /** + * Defensively copied UniqueMemberList of all members in the member list at the time of the call. + */ + public UniqueMemberList getAllMembers() { + return new UniqueMemberList(allMembers); + } + + //@@author kianhong95 + /** + * Defensively copied UniqueEmployeeList of all employees in the employee list at the time of the call. + */ + public UniqueEmployeeList getAllEmployees() { + return new UniqueEmployeeList(allEmployees); + } + + /** + * Defensively copied UniqueEmployeeList of all employees in the employee list at the time of the call. + */ + public UniqueAttendanceList getAllAttendance() { + return new UniqueAttendanceList(allAttendance); + } + + //@@author SalsabilTasnia + /** + * Defensively copied UniqueMenuList of all menu items in the menu at the time of the call. + */ + public UniqueMenuList getAllMenus() { + return new UniqueMenuList(allFoodItems); + } + + //@@author px1099 + /** + * Defensively copied UniqueOrderList of all orders in the employee list at the time of the call. + */ + public UniqueOrderList getAllOrders() { + return new UniqueOrderList(allOrders); + } + + public ReadOnlyOrder getDraftOrder() { + return draftOrder; + } + + public String getDraftOrderAsText() { + return draftOrder.getDraftDetailsAsText(); + } + + /** + * Set a member to be the customer of the draft order + */ + public void editDraftOrderCustomer(ReadOnlyMember customer) { + draftOrder.setCustomer(customer); + } + + /** + * Adjust the dish and its quantity in the draft order to add, remove or edit dish items in the draft. + */ + public void editDraftOrderDishItem(ReadOnlyMenus dish, int quantity) { + draftOrder.changeDishQuantity(dish, quantity); + } + + //@@author kangmingtay + /** + * Edit the number of points to be redeemed + */ + public void editDraftOrderPoints(int points) { + draftOrder.setPoints(points); + } + + //@@author px1099 + /** + * Update the member points of a customer + * @param customer the ReadOnlyMember interface of the Member object to update points + * @param price the price of the added order made by the customer + * @param points the redeemed points the customer used in the order + */ + public void updatePointsOfCustomer(ReadOnlyMember customer, double price, int points) { + allMembers.updatePointsOfCustomer(customer, price, points); + } + + public void clearDraftOrder() { + draftOrder = new Order(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Rms // instanceof handles nulls + && this.allAttendance.equals(((Rms) other).allAttendance) + && this.allEmployees.equals(((Rms) other).allEmployees) + && this.allFoodItems.equals(((Rms) other).allFoodItems) + && this.allMembers.equals(((Rms) other).allMembers) + && this.allOrders.equals(((Rms) other).allOrders)); + } + + @Override + public int hashCode() { + return Objects.hash(allAttendance, allEmployees, allFoodItems, allMembers, allOrders); + } + + //@@author +} diff --git a/src/seedu/addressbook/data/employee/Attendance.java b/src/seedu/addressbook/data/employee/Attendance.java new file mode 100644 index 000000000..6d0774a36 --- /dev/null +++ b/src/seedu/addressbook/data/employee/Attendance.java @@ -0,0 +1,82 @@ +package seedu.addressbook.data.employee; + +import java.util.LinkedHashSet; +import java.util.Objects; +import java.util.Set; + +//@@author kianhong95 +/** + * Represents an Attendance list in the Rms. + * Guarantees: EmployeeName is present, not null and validated + * as this command only works when an employee has been properly created. + */ +public class Attendance { + private String name; + private boolean isClockedIn; + + private final Set timings = new LinkedHashSet<>(); + + public Attendance(){} + + public Attendance(String name) { + this.name = name.trim(); + this.isClockedIn = false; + } + + public Attendance(String name, boolean isClockedIn, Set timings) { + this.name = name; + this.isClockedIn = isClockedIn; + this.timings.addAll(timings); + } + + /** + * Copy constructor. + */ + public Attendance(Attendance source) { + this(source.getName(), source.getClockedIn(), source.getTimings()); + } + + public String getName() { + return name; + } + + public boolean getClockedIn() { + return isClockedIn; + } + + public Set getTimings() { + return new LinkedHashSet<>(timings); + } + + @Override + public int hashCode() { + return Objects.hash(name, isClockedIn , timings); + } + + @Override + public String toString() { + return getAsTextShowAll(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Attendance // instanceof handles nulls + && this.name.equals(((Attendance) other).name) + && this.isClockedIn == ((Attendance) other).isClockedIn + && this.timings.equals(((Attendance) other).timings)); // state check + } + + /** + * Formats the attendance as text, showing all check in and check out timings. + */ + public String getAsTextShowAll() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()) + .append(" Timings: "); + for (Timing timing : getTimings()) { + builder.append(timing); + } + return builder.toString(); + } +} diff --git a/src/seedu/addressbook/data/employee/EditEmployeeDescriptor.java b/src/seedu/addressbook/data/employee/EditEmployeeDescriptor.java new file mode 100644 index 000000000..49234129a --- /dev/null +++ b/src/seedu/addressbook/data/employee/EditEmployeeDescriptor.java @@ -0,0 +1,107 @@ +package seedu.addressbook.data.employee; + +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kianhong95 +/** + * Stores the details to edit the employee with. Each non-empty field value will replace the + * corresponding field value of the employee. + */ +public class EditEmployeeDescriptor { + private EmployeeEmail email; + private EmployeePhone phone; + private EmployeeAddress address; + private EmployeePosition position; + + public EditEmployeeDescriptor(){} + + public EditEmployeeDescriptor(String phone, + String email, + String address, + String position) throws IllegalValueException { + if (phone == null) { + this.phone = new EmployeePhone(); + } else { + this.phone = new EmployeePhone(phone); + } + + if (email == null) { + this.email = new EmployeeEmail(); + } else { + this.email = new EmployeeEmail(email); + } + if (address == null) { + this.address = new EmployeeAddress(); + } else { + this.address = new EmployeeAddress(address); + } + if (position == null) { + this.position = new EmployeePosition(); + } else { + this.position = new EmployeePosition(position); + } + } + + /** + * Copy constructor. + */ + public EditEmployeeDescriptor(EditEmployeeDescriptor toCopy) { + setPhone(toCopy.phone); + setEmail(toCopy.email); + setAddress(toCopy.address); + setPosition(toCopy.position); + } + + public void setPhone(EmployeePhone phone) { + this.phone = phone; + } + + public EmployeePhone getPhone() { + return phone; + } + + public void setEmail(EmployeeEmail email) { + this.email = email; + } + + public EmployeeEmail getEmail() { + return email; + } + + public void setAddress(EmployeeAddress address) { + this.address = address; + } + + public EmployeeAddress getAddress() { + return address; + } + + public void setPosition(EmployeePosition position) { + this.position = position; + } + + public EmployeePosition getPosition() { + return position; + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditEmployeeDescriptor)) { + return false; + } + + // state check + EditEmployeeDescriptor e = (EditEmployeeDescriptor) other; + + return getPhone().equals(e.getPhone()) + && getEmail().equals(e.getEmail()) + && getAddress().equals(e.getAddress()) + && getPosition().equals(e.getPosition()); + } +} diff --git a/src/seedu/addressbook/data/employee/Employee.java b/src/seedu/addressbook/data/employee/Employee.java new file mode 100644 index 000000000..466d5b481 --- /dev/null +++ b/src/seedu/addressbook/data/employee/Employee.java @@ -0,0 +1,82 @@ +package seedu.addressbook.data.employee; + +import java.util.Objects; + +//@@author kianhong95 +/** + * Represents an Employee in the system. + * Guarantees: details are present and not null, field values are validated. + */ +public class Employee implements ReadOnlyEmployee { + + private EmployeeName name; + private EmployeePhone phone; + private EmployeeEmail email; + private EmployeeAddress address; + private EmployeePosition position; + + /** + * Assumption: Every field must be present and not null. + */ + public Employee(EmployeeName name, + EmployeePhone phone, + EmployeeEmail email, + EmployeeAddress address, + EmployeePosition position) { + this.name = name; + this.phone = phone; + this.email = email; + this.address = address; + this.position = position; + } + + /** + * Copy constructor. + */ + public Employee(ReadOnlyEmployee source) { + this(source.getName(), source.getPhone(), source.getEmail(), source.getAddress(), source.getPosition()); + } + + @Override + public EmployeeName getName() { + return name; + } + + @Override + public EmployeePhone getPhone() { + return phone; + } + + @Override + public EmployeeEmail getEmail() { + return email; + } + + @Override + public EmployeeAddress getAddress() { + return address; + } + + @Override + public EmployeePosition getPosition() { + return position; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ReadOnlyEmployee // instanceof handles nulls + && this.isSameStateAs((ReadOnlyEmployee) other)); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, phone, email, address, position); + } + + @Override + public String toString() { + return getAsTextShowDetails(); + } +} diff --git a/src/seedu/addressbook/data/employee/EmployeeAddress.java b/src/seedu/addressbook/data/employee/EmployeeAddress.java new file mode 100644 index 000000000..df847fa7a --- /dev/null +++ b/src/seedu/addressbook/data/employee/EmployeeAddress.java @@ -0,0 +1,62 @@ +package seedu.addressbook.data.employee; + +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kianhong95 +/** + * Represents an Employee's address in the Rms. + * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} + */ +public class EmployeeAddress { + + public static final String EXAMPLE = "Clementi Ave 2, Blk 543 #13-12"; + public static final String MESSAGE_ADDRESS_CONSTRAINTS = "Employee addresses can be in any format" + + " but cannot be more than 50 characters"; + public static final String ADDRESS_VALIDATION_REGEX = ".{1,50}+"; + + public final String value; + + /** + * Empty constructor + */ + public EmployeeAddress() { + this.value = ""; + } + + /** + * Validates given address. + * + * @throws IllegalValueException if given address string is invalid. + */ + public EmployeeAddress(String address) throws IllegalValueException { + String trimmedAddress = address.trim(); + if (!isValidAddress(trimmedAddress)) { + throw new IllegalValueException(MESSAGE_ADDRESS_CONSTRAINTS); + } + this.value = trimmedAddress; + } + + /** + * Returns true if a given string is a valid Employee email. + */ + public static boolean isValidAddress(String test) { + return test.matches(ADDRESS_VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EmployeeAddress // instanceof handles nulls + && this.value.equals(((EmployeeAddress) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/seedu/addressbook/data/employee/EmployeeEmail.java b/src/seedu/addressbook/data/employee/EmployeeEmail.java new file mode 100644 index 000000000..9e036cedc --- /dev/null +++ b/src/seedu/addressbook/data/employee/EmployeeEmail.java @@ -0,0 +1,28 @@ +package seedu.addressbook.data.employee; + +import seedu.addressbook.common.Email; +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kianhong95 +/** + * Represents an Employee's email in the Rms. + * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} + */ +public class EmployeeEmail extends Email { + + /** + * Empty constructor + */ + public EmployeeEmail() { + super(); + } + + /** + * Validates given email. + * + * @throws IllegalValueException if given email address string is invalid. + */ + public EmployeeEmail(String email) throws IllegalValueException { + super(email); + } +} diff --git a/src/seedu/addressbook/data/employee/EmployeeName.java b/src/seedu/addressbook/data/employee/EmployeeName.java new file mode 100644 index 000000000..cd2f45cf4 --- /dev/null +++ b/src/seedu/addressbook/data/employee/EmployeeName.java @@ -0,0 +1,21 @@ +package seedu.addressbook.data.employee; + +import seedu.addressbook.common.Name; +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kianhong95 +/** + * Represents an Employee's name in the Rms. + * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} + */ +public class EmployeeName extends Name { + + /** + * Validates given name. + * + * @throws IllegalValueException if given name string is invalid. + */ + public EmployeeName(String name) throws IllegalValueException { + super(name); + } +} diff --git a/src/seedu/addressbook/data/employee/EmployeePhone.java b/src/seedu/addressbook/data/employee/EmployeePhone.java new file mode 100644 index 000000000..d626dca2d --- /dev/null +++ b/src/seedu/addressbook/data/employee/EmployeePhone.java @@ -0,0 +1,62 @@ +package seedu.addressbook.data.employee; + +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kianhong95 +/** + * Represents an Employee's phone number in the Rms. + * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} + */ +public class EmployeePhone { + + public static final String EXAMPLE = "91234567"; + public static final String MESSAGE_PHONE_CONSTRAINTS = "Employee phone numbers should only contain numbers" + + "and must be 8 digits"; + public static final String PHONE_VALIDATION_REGEX = "\\d{8}"; + + public final String value; + + /** + * Empty constructor + */ + public EmployeePhone() { + this.value = ""; + } + + /** + * Validates given phone number. + * + * @throws IllegalValueException if given phone string is invalid. + */ + public EmployeePhone(String phone) throws IllegalValueException { + String trimmedPhone = phone.trim(); + if (!isValidPhone(trimmedPhone)) { + throw new IllegalValueException(MESSAGE_PHONE_CONSTRAINTS); + } + this.value = trimmedPhone; + } + + /** + * Checks if a given string is a valid employee phone number. + */ + public static boolean isValidPhone(String test) { + return test.matches(PHONE_VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EmployeePhone // instanceof handles nulls + && this.value.equals(((EmployeePhone) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/seedu/addressbook/data/employee/EmployeePosition.java b/src/seedu/addressbook/data/employee/EmployeePosition.java new file mode 100644 index 000000000..9e652910a --- /dev/null +++ b/src/seedu/addressbook/data/employee/EmployeePosition.java @@ -0,0 +1,63 @@ +package seedu.addressbook.data.employee; + +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kianhong95 +/** + * Represents an Employee's position in the Rms. + * Guarantees: immutable; is valid as declared in {@link #isValidPosition(String)} + */ +public class EmployeePosition { + + + public static final String EXAMPLE = "Cashier"; + public static final String MESSAGE_POSITION_CONSTRAINTS = "Position cannot be longer than " + + "30 alphanumeric characters and spaces."; + public static final String POSITION_VALIDATION_REGEX = "[\\p{Alnum} ]{1,30}+"; + + public final String value; + + /** + * Empty constructor + */ + public EmployeePosition() { + this.value = ""; + } + + /** + * Validates given name. + * + * @throws IllegalValueException if given name string is invalid. + */ + public EmployeePosition(String value) throws IllegalValueException { + String trimmedValue = value.trim(); + if (!isValidPosition(trimmedValue)) { + throw new IllegalValueException(MESSAGE_POSITION_CONSTRAINTS); + } + this.value = trimmedValue; + } + + /** + * Returns true if a given string is a valid employee name. + */ + public static boolean isValidPosition(String test) { + return test.matches(POSITION_VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EmployeePosition // instanceof handles nulls + && this.value.equals(((EmployeePosition) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/seedu/addressbook/data/employee/ReadOnlyEmployee.java b/src/seedu/addressbook/data/employee/ReadOnlyEmployee.java new file mode 100644 index 000000000..9bbc70141 --- /dev/null +++ b/src/seedu/addressbook/data/employee/ReadOnlyEmployee.java @@ -0,0 +1,52 @@ +package seedu.addressbook.data.employee; + +//@@author kianhong95 +/** + * A read-only immutable interface for an Employee in the Rms. + * Implementations should guarantee: details are present and not null, field values are validated. + */ +public interface ReadOnlyEmployee { + + EmployeeName getName(); + EmployeePhone getPhone(); + EmployeeEmail getEmail(); + EmployeeAddress getAddress(); + EmployeePosition getPosition(); + + + /** + * Returns true if the values inside this object is same as those of the other + * (Note: interfaces cannot override .equals) + */ + default boolean isSameStateAs(ReadOnlyEmployee other) { + return other == this // short circuit if same object + || (other != null // this is first to avoid NPE below + && other.getName().equals(this.getName()) // state checks here onwards + && other.getPhone().equals(this.getPhone()) + && other.getEmail().equals(this.getEmail()) + && other.getAddress().equals(this.getAddress()) + && other.getPosition().equals(this.getPosition())); + } + + + // Deal with this after creating variable classes + /** + * Formats the Employee as text, showing all details. + * Value of each attribute is trimmed to prevent whitespace errors during tests + */ + default String getAsTextShowDetails() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName().fullName.trim()) + .append(" | Phone: "); + builder.append(getPhone().value.trim()) + .append(" | Email: "); + builder.append(getEmail().value.trim()) + .append(" | Address: "); + builder.append(getAddress().value.trim()) + .append(" | Position: "); + builder.append(getPosition().value.trim()); + return builder.toString(); + } + + +} diff --git a/src/seedu/addressbook/data/employee/Timing.java b/src/seedu/addressbook/data/employee/Timing.java new file mode 100644 index 000000000..c4ae65cca --- /dev/null +++ b/src/seedu/addressbook/data/employee/Timing.java @@ -0,0 +1,48 @@ +package seedu.addressbook.data.employee; + +import java.util.Objects; + +//@@author kianhong95 +/** + * Represents a Timing field in the Rms. + */ +public class Timing { + + public final String time; + public final String date; + public final boolean isClockIn; + + + public Timing(String time, String date, boolean isClockIn) { + this.time = time; + this.date = date; + this.isClockIn = isClockIn; + } + + public String getDate() { + return date; + } + + public boolean isClockIn() { + return isClockIn; + } + + @Override + public int hashCode() { + return Objects.hash(time, date, isClockIn); + } + + @Override + public String toString() { + return "Date = " + date + " Time = " + time + " isClockIn = " + isClockIn; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Timing // instanceof handles nulls + && this.time.equals(((Timing) other).time) + && this.date.equals(((Timing) other).date) + && this.isClockIn == ((Timing) other).isClockIn); // state check + } +} diff --git a/src/seedu/addressbook/data/employee/UniqueAttendanceList.java b/src/seedu/addressbook/data/employee/UniqueAttendanceList.java new file mode 100644 index 000000000..dd8b983b6 --- /dev/null +++ b/src/seedu/addressbook/data/employee/UniqueAttendanceList.java @@ -0,0 +1,103 @@ +package seedu.addressbook.data.employee; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +//@@author kianhong95 +/** + * A list of attendance timings. Does not allow null elements. + */ +public class UniqueAttendanceList implements Iterable { + + private final List attendanceInternalList = new ArrayList<>(); + + /** + * Constructs empty attendance list. + */ + public UniqueAttendanceList() {} + + /** + * Constructs an attendance list with the given attendance timings. + */ + public UniqueAttendanceList(Attendance... attendances) { + final List initialTags = Arrays.asList(attendances); + attendanceInternalList.addAll(initialTags); + } + + /** + * Constructs a list from the items in the given collection. + * @param attendances a collection of attendance + */ + public UniqueAttendanceList(Collection attendances) { + attendanceInternalList.addAll(attendances); + } + + /** + * Constructs a shallow copy of the list. + */ + public UniqueAttendanceList(UniqueAttendanceList source) { + attendanceInternalList.addAll(source.attendanceInternalList); + } + + /** + * Adds an attendance to the list. + */ + public void add(Attendance toAdd) { + attendanceInternalList.add(toAdd); + } + + /** + * Removes the equivalent employee from the list. + */ + public void remove(Attendance toRemove) { + attendanceInternalList.remove(toRemove); + } + + /** + * Gets index of the specified Attendance object. + */ + public int getAttendanceIndex(String target) { + for (Attendance attendance : attendanceInternalList) { + if (attendance.getName().equals(target)) { + return attendanceInternalList.indexOf(attendance); + } + } + return -1; + } + + /** + * Gets the Attendance object at target index. + */ + public Attendance getAttendance(int target) { + return attendanceInternalList.get(target); + } + + /** + * Adds a time field to the attendance for the specified employee in the list. + */ + public void setAttendance(Attendance target, Attendance newAttendance) { + int index = attendanceInternalList.indexOf(target); + + attendanceInternalList.set(index, newAttendance); + } + + @Override + public Iterator iterator() { + return attendanceInternalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueAttendanceList // instanceof handles nulls + && this.attendanceInternalList.equals(((UniqueAttendanceList) other).attendanceInternalList)); + } + + @Override + public int hashCode() { + return attendanceInternalList.hashCode(); + } +} diff --git a/src/seedu/addressbook/data/employee/UniqueEmployeeList.java b/src/seedu/addressbook/data/employee/UniqueEmployeeList.java new file mode 100644 index 000000000..320b86d6d --- /dev/null +++ b/src/seedu/addressbook/data/employee/UniqueEmployeeList.java @@ -0,0 +1,147 @@ +package seedu.addressbook.data.employee; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.exception.DuplicateDataException; + +//@@author kianhong95 +/** + * A list of employees. Does not allow null elements or duplicates. + * + * @see Employee#equals(Object) + * @see Utils#elementsAreUnique(Collection) + */ +public class UniqueEmployeeList implements Iterable { + + private final List employeeInternalList = new ArrayList<>(); + + /** + * Signals that an operation would have violated the 'no duplicates' property of the list. + */ + public static class DuplicateEmployeeException extends DuplicateDataException { + protected DuplicateEmployeeException() { + super("Operation would result in duplicate employees"); + } + } + + /** + * Signals that an operation targeting a specified employee in the list would fail because + * there is no such matching employee in the list. + */ + public static class EmployeeNotFoundException extends Exception {} + + /** + * Constructs empty employee list. + */ + public UniqueEmployeeList(){} + + /** + * Constructs an employee list with the given employees. + */ + public UniqueEmployeeList(Employee... employees) throws DuplicateEmployeeException { + final List initialTags = Arrays.asList(employees); + if (!Utils.elementsAreUnique(initialTags)) { + throw new DuplicateEmployeeException(); + } + employeeInternalList.addAll(initialTags); + } + + /** + * Constructs a list from the items in the given collection. + * @param employees a collection of employees + * @throws DuplicateEmployeeException if the {@code employees} contains duplicate employees + */ + public UniqueEmployeeList(Collection employees) throws DuplicateEmployeeException { + if (!Utils.elementsAreUnique(employees)) { + throw new DuplicateEmployeeException(); + } + employeeInternalList.addAll(employees); + } + + /** + * Constructs a shallow copy of the list. + */ + public UniqueEmployeeList(UniqueEmployeeList source) { + employeeInternalList.addAll(source.employeeInternalList); + } + + public List immutableListView() { + return Collections.unmodifiableList(employeeInternalList); + } + + /** + * Checks if the list contains an employee that already exists in Rms. + */ + public boolean containsDuplicate(ReadOnlyEmployee toCheck) { + String nameToCheck = toCheck.getName().toString().toLowerCase(); + for (ReadOnlyEmployee employee: employeeInternalList) { + String employeeName = employee.getName().toString().toLowerCase(); + if (employeeName.equals(nameToCheck)) { + return true; + } + } + return false; + } + + /** + * Adds an employee to the list. + * + * @throws UniqueEmployeeList.DuplicateEmployeeException + * if the employee to add is a duplicate of an existing employee in the list. + */ + public void add(Employee toAdd) throws UniqueEmployeeList.DuplicateEmployeeException { + if (containsDuplicate(toAdd)) { + throw new UniqueEmployeeList.DuplicateEmployeeException(); + } + employeeInternalList.add(toAdd); + } + + + /** + * Removes the equivalent employee from the list. + * + * @throws EmployeeNotFoundException if no such employee could be found in the list. + */ + public void remove(ReadOnlyEmployee toRemove) throws EmployeeNotFoundException { + final boolean employeeFoundAndDeleted = employeeInternalList.remove(toRemove); + if (!employeeFoundAndDeleted) { + throw new EmployeeNotFoundException(); + } + } + + /** + * Removes the equivalent employee from the list. + * + * @throws EmployeeNotFoundException if no such employee could be found in the list. + */ + public void edit(ReadOnlyEmployee toRemove, Employee toReplace) throws EmployeeNotFoundException { + final boolean employeeFoundAndDeleted = employeeInternalList.remove(toRemove); + if (!employeeFoundAndDeleted) { + throw new EmployeeNotFoundException(); + } + employeeInternalList.add(toReplace); + } + + @Override + public Iterator iterator() { + return employeeInternalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueEmployeeList // instanceof handles nulls + && this.employeeInternalList.equals(((UniqueEmployeeList) other).employeeInternalList)); + } + + @Override + public int hashCode() { + return employeeInternalList.hashCode(); + } +} diff --git a/src/seedu/addressbook/data/member/Member.java b/src/seedu/addressbook/data/member/Member.java new file mode 100644 index 000000000..d260d2590 --- /dev/null +++ b/src/seedu/addressbook/data/member/Member.java @@ -0,0 +1,131 @@ +package seedu.addressbook.data.member; + +import java.util.Date; +import java.util.Objects; + +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kangmingtay +/** + * Represents a Member in the member list. + */ +public class Member implements ReadOnlyMember { + + public static final String EMPTY_NAME_STRING = "baLpcbImfjsHuIhCnEKM"; + public static final String EMPTY_EMAIL_STRING = "baLpcbImfjsHuIhCnEKM@rms.com"; + + private MemberName name; + private MemberEmail email; + private Points points; + private Date date; + private MemberTier tier; + + public Member() { + try { + this.name = new MemberName(EMPTY_NAME_STRING); + this.email = new MemberEmail(EMPTY_EMAIL_STRING); + } catch (IllegalValueException ie) { + this.name = null; + this.email = null; + } + this.points = new Points(); + this.date = new Date(); + this.tier = new MemberTier(); + } + + public Member(MemberName name, MemberEmail email) { + this.name = name; + this.email = email; + this.points = new Points(); + this.date = new Date(); + this.tier = new MemberTier(); + } + + public Member(MemberName name, MemberEmail email, Points points, Date date, MemberTier tier) { + this.name = name; + this.email = email; + this.points = points; + this.date = date; + this.tier = tier; + } + /** + * Copy constructor. + */ + public Member(ReadOnlyMember source) { + this(source.getName(), source.getEmail(), source.getCurrentPoints(), source.getDate(), source.getMemberTier()); + } + + @Override + public MemberName getName() { + return name; + } + + public MemberEmail getEmail() { + return email; + } + + @Override + public Points getCurrentPoints() { + return points; + } + + @Override + public int getCurrentPointsValue() { + return points.getCurrentPoints(); + } + + @Override + public int getTotalPointsValue() { + return points.getTotalPoints(); + } + + @Override + public Date getDate() { + return date; + } + + @Override + public MemberTier getMemberTier() { + return tier; + } + + public void setPoints(int value) { + points.setCurrentPoints(value); + } + + public Points updatePoints(double price, int pointsToRedeem) { + return this.points.updatePoints(price, pointsToRedeem); + } + + public void updateTier(Points points) { + tier.updateTier(points); + } + + + + /** + * Updates the points and membership tier of the member + */ + public void updatePointsAndTier(double price, int pointsToRedeem) { + Points newPoints = updatePoints(price, pointsToRedeem); + updateTier(newPoints); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ReadOnlyMember // instanceof handles nulls + && this.isSameStateAs((ReadOnlyMember) other)); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, email); + } + + @Override + public String toString() { + return getAsText(); + } +} diff --git a/src/seedu/addressbook/data/member/MemberEmail.java b/src/seedu/addressbook/data/member/MemberEmail.java new file mode 100644 index 000000000..d994879fb --- /dev/null +++ b/src/seedu/addressbook/data/member/MemberEmail.java @@ -0,0 +1,15 @@ +package seedu.addressbook.data.member; + +import seedu.addressbook.common.Email; +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kangmingtay +/** + * Represents a member's email in the RMS + */ +public class MemberEmail extends Email { + + public MemberEmail(String email) throws IllegalValueException { + super(email); + } +} diff --git a/src/seedu/addressbook/data/member/MemberName.java b/src/seedu/addressbook/data/member/MemberName.java new file mode 100644 index 000000000..95b177029 --- /dev/null +++ b/src/seedu/addressbook/data/member/MemberName.java @@ -0,0 +1,21 @@ +package seedu.addressbook.data.member; + +import seedu.addressbook.common.Name; +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kangmingtay +/** + * Represents an Employee's name in the Rms. + * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} + */ +public class MemberName extends Name { + + /** + * Validates given name. + * + * @throws IllegalValueException if given name string is invalid. + */ + public MemberName(String name) throws IllegalValueException { + super(name); + } +} diff --git a/src/seedu/addressbook/data/member/MemberTier.java b/src/seedu/addressbook/data/member/MemberTier.java new file mode 100644 index 000000000..975df4140 --- /dev/null +++ b/src/seedu/addressbook/data/member/MemberTier.java @@ -0,0 +1,43 @@ +package seedu.addressbook.data.member; + +//@@author kangmingtay +/** + * Represents the number of membership tier of a Member in the member list. + */ + +public class MemberTier { + + public static final int GOLD_TIER = 400; + public static final int SILVER_TIER = 200; + public static final int BRONZE_TIER = 0; + + private String tier; + + public MemberTier() { + this.tier = "Bronze"; + } + + public MemberTier(String tier) { + this.tier = tier; + } + + @Override + public String toString() { + return tier; + } + + /** + * Checks the points and updates the existing tier. + * @param points the number of Member points used for tier checking + */ + public void updateTier(Points points) { + int value = points.getCurrentPoints(); + if (value > GOLD_TIER) { + this.tier = "Gold"; + } else if (value > SILVER_TIER) { + this.tier = "Silver"; + } else if (value >= BRONZE_TIER) { + this.tier = "Bronze"; + } + } +} diff --git a/src/seedu/addressbook/data/member/Points.java b/src/seedu/addressbook/data/member/Points.java new file mode 100644 index 000000000..1a359bba8 --- /dev/null +++ b/src/seedu/addressbook/data/member/Points.java @@ -0,0 +1,117 @@ +package seedu.addressbook.data.member; + +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kangmingtay +/** + * Represents the number of membership point of a Member in the member list. + */ +public class Points { + + public static final int EARNED_POINTS_PER_DOLLAR = 10; + public static final int REDEEMED_POINTS_PER_DOLLAR = 100; + public static final int MAX_CURRENT_POINTS = 2000000000; + public static final int MAX_TOTAL_POINTS = 2000000000; + + public static final String MESSAGE_NEGATIVE_POINTS = "Update points cannot result in negative points."; + public static final String MESSAGE_MAXIMUM_POINTS_EXCEEDED = "Maximum points earned has been reached."; + public static final String MESSAGE_MAXIMUM_TOTAL_POINTS_EXCEEDED = "Maximum total points has been reached."; + + private int currentPoints; + private int totalPoints; + + + + public Points() { + this.currentPoints = 0; + this.totalPoints = 0; + } + + public Points (int pointsToRedeem) { + this.currentPoints = pointsToRedeem; + this.totalPoints = pointsToRedeem; + } + public Points(int currentPoints, int totalPoints) { + this.currentPoints = currentPoints; + this.totalPoints = totalPoints; + } + + /** + * Converts the price into points and adds in to the existing points for the member + * @param price of the order being made + * @return updated points + */ + protected Points updatePoints(double price, int pointsToRedeem) { + try { + if (this.currentPoints < pointsToRedeem) { + throw new IllegalValueException(MESSAGE_NEGATIVE_POINTS); + } + final int pointsEarned = getEarnedPointsValue(price); + this.currentPoints -= pointsToRedeem; + this.currentPoints += pointsEarned; + this.totalPoints += pointsEarned; + if (currentPoints > MAX_CURRENT_POINTS) { + throw new IllegalValueException(MESSAGE_MAXIMUM_POINTS_EXCEEDED); + } else if (totalPoints > MAX_TOTAL_POINTS) { + throw new IllegalValueException(MESSAGE_MAXIMUM_TOTAL_POINTS_EXCEEDED); + } + return this; + } catch (IllegalValueException e) { + if (e.getMessage() == MESSAGE_NEGATIVE_POINTS) { + return this; + } else if (e.getMessage() == MESSAGE_MAXIMUM_POINTS_EXCEEDED) { + this.currentPoints = MAX_TOTAL_POINTS; + this.totalPoints = MAX_TOTAL_POINTS; + return this; + } else if (e.getMessage() == MESSAGE_MAXIMUM_TOTAL_POINTS_EXCEEDED) { + this.totalPoints = MAX_TOTAL_POINTS; + return this; + } + return this; + } + + } + + public int getCurrentPoints() { + return this.currentPoints; + } + + public int getTotalPoints() { + return this.totalPoints; + } + + public void setCurrentPoints(int points) { + this.currentPoints = points; + } + + public double getRedeemedDiscount() { + return (((double) currentPoints) / REDEEMED_POINTS_PER_DOLLAR); + } + + public static int getEarnedPointsValue(double price) { + return (int) (price * EARNED_POINTS_PER_DOLLAR); + } + + public static int getRedeemedPointsValue(double price) { + return (int) (price * REDEEMED_POINTS_PER_DOLLAR); + } + + @Override + public String toString() { + return Integer.toString(currentPoints) + Integer.toString(totalPoints); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Points // instanceof handles nulls + && this.currentPoints == ((Points) other).currentPoints + && this.totalPoints == ((Points) other).totalPoints); // state check + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + +} diff --git a/src/seedu/addressbook/data/member/ReadOnlyMember.java b/src/seedu/addressbook/data/member/ReadOnlyMember.java new file mode 100644 index 000000000..f4b8434d6 --- /dev/null +++ b/src/seedu/addressbook/data/member/ReadOnlyMember.java @@ -0,0 +1,63 @@ +package seedu.addressbook.data.member; + +import java.util.Date; + +//@@author kangmingtay +/** + * A read-only immutable interface for a Member in the Restaurant Management System. + * Implementations should guarantee: details are present and not null, field values are validated. + */ +public interface ReadOnlyMember { + + MemberName getName(); + MemberEmail getEmail(); + Points getCurrentPoints(); + Date getDate(); + MemberTier getMemberTier(); + int getCurrentPointsValue(); + int getTotalPointsValue(); + + + /** + * The returned {@code Set} is a deep copy of the internal {@code Set}, + * changes on the returned list will not affect the person's internal tags. + */ + // Set getTags(); + + /** + * Returns true if the values inside this object is same as those of the other + * (Note: interfaces cannot override .equals) + */ + default boolean isSameStateAs(ReadOnlyMember other) { + return other == this // short circuit if same object + || (other != null // this is first to avoid NPE below + && other.getName().equals(this.getName()) + && other.getEmail().equals(this.getEmail())); // state checks here onwards + } + + /** + * Formats a member as text, showing all details. + */ + default String getAsText() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()); + builder.append(" | Email: ").append(getEmail()); + builder.append(" | Available Points: ").append(getCurrentPointsValue()); + builder.append(" | Total Points: ").append(getTotalPointsValue()); + builder.append(" | Tier: ").append(getMemberTier().toString()); + builder.append(" | Date: ").append(getDate()); + builder.append("\n"); + return builder.toString(); + } + + /** + * Formats a member as text, showing only contact details and member tier. + */ + default String getAsTextInOrderList() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()); + builder.append(" | Email: ").append(getEmail()); + builder.append(" | Tier: ").append(getMemberTier().toString()); + return builder.toString(); + } +} diff --git a/src/seedu/addressbook/data/member/UniqueMemberList.java b/src/seedu/addressbook/data/member/UniqueMemberList.java new file mode 100644 index 000000000..dc4b6ef1a --- /dev/null +++ b/src/seedu/addressbook/data/member/UniqueMemberList.java @@ -0,0 +1,158 @@ +package seedu.addressbook.data.member; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.exception.DuplicateDataException; + +//@@author kangmingtay +/** + * A list of members. Does not allow null elements or duplicates. + * + * @see Member#equals(Object) + * @see Utils#elementsAreUnique(Collection) + */ +public class UniqueMemberList implements Iterable { + + private final List internalList = new ArrayList<>(); + + /** + * Signals that an operation would have violated the 'no duplicates' property of the list. + */ + public static class DuplicateMemberException extends DuplicateDataException { + protected DuplicateMemberException() { + super("Operation would result in duplicate members"); + } + } + + /** + * Signals that an operation targeting a specified member in the list would fail because + * there is no such matching member in the list. + */ + public static class MemberNotFoundException extends Exception {} + + /** + * Constructs empty person list. + */ + public UniqueMemberList() {} + + /** + * Constructs a person list with the given persons. + */ + public UniqueMemberList(Member... members) throws DuplicateMemberException { + final List initialTags = Arrays.asList(members); + if (!Utils.elementsAreUnique(initialTags)) { + throw new DuplicateMemberException(); + } + internalList.addAll(initialTags); + } + + /** + * Constructs a list from the items in the given collection. + * @param members a collection of persons + * @throws DuplicateMemberException if the {@code persons} contains duplicate persons + */ + public UniqueMemberList(Collection members) throws DuplicateMemberException { + if (!Utils.elementsAreUnique(members)) { + throw new DuplicateMemberException(); + } + internalList.addAll(members); + } + + /** + * Constructs a shallow copy of the list. + */ + public UniqueMemberList(UniqueMemberList source) { + internalList.addAll(source.internalList); + } + + /** + * Unmodifiable java List view with elements cast as immutable {@link ReadOnlyMember}s. + * For use with other methods/libraries. + * Any changes to the internal list/elements are immediately visible in the returned list. + */ + public List immutableListView() { + return Collections.unmodifiableList(internalList); + } + + + /** + * Checks if the list contains an equivalent member as the given argument. + */ + public boolean contains(ReadOnlyMember toCheck) { + return internalList.contains(toCheck); + } + + /** + * Adds a member to the list. + * + * @throws DuplicateMemberException if the member to add is a duplicate of an existing person in the list. + */ + public void add(Member toAdd) throws DuplicateMemberException { + if (contains(toAdd)) { + throw new DuplicateMemberException(); + } + internalList.add(toAdd); + } + + /** + * Removes the equivalent member from the list. + * + * @throws MemberNotFoundException if no such member could be found in the list. + */ + public void remove(ReadOnlyMember toRemove) throws MemberNotFoundException { + final boolean memberFoundAndDeleted = internalList.remove(toRemove); + if (!memberFoundAndDeleted) { + throw new MemberNotFoundException(); + } + } + + //@@author px1099 + /** + * Checks if a member in another feature is in a list of members + * Returns the member if found, else create a new Member using the data from the member in the order + */ + public static Member retrieveMember(ReadOnlyMember target, List memberList) { + for (Member member : memberList) { + if (target.isSameStateAs(member)) { + return member; + } + } + return new Member(target); + } + + /** + * Update the member points of a customer + * @param target the ReadOnlyMember interface of the Member object to update points + * @param price the price of the order made by the customer + * @param usedPoints the redeemed points the customer used in the order + */ + public void updatePointsOfCustomer(ReadOnlyMember target, double price, int usedPoints) { + final Member customerToUpdatePoints = retrieveMember(target, internalList); + customerToUpdatePoints.updatePointsAndTier(price, usedPoints); + } + + //@@author kangmingtay + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueMemberList // instanceof handles nulls + && this.internalList.equals(((UniqueMemberList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + +} diff --git a/src/seedu/addressbook/data/menu/Menu.java b/src/seedu/addressbook/data/menu/Menu.java new file mode 100644 index 000000000..de50cc40a --- /dev/null +++ b/src/seedu/addressbook/data/menu/Menu.java @@ -0,0 +1,81 @@ +package seedu.addressbook.data.menu; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import seedu.addressbook.data.tag.Tag; + +//@@author SalsabilTasnia +/** + * Represents a Menu in the Rms. + * Guarantees: details are present and not null, field values are validated. + */ +public class Menu implements ReadOnlyMenus, Comparable { + + private MenuName name; + private Price price; + private Type type; + + private final Set tags = new HashSet<>(); + /** + * Assumption: Every field must be present and not null. + */ + public Menu(MenuName name, Price price, Type type, Set tags) { + this.name = name; + this.price = price; + this.type = type; + this.tags.addAll(tags); + } + + /** + * Copy constructor. + */ + public Menu(ReadOnlyMenus source) { + this(source.getName(), source.getPrice(), source.getType(), source.getTags()); + } + + @Override + public MenuName getName() { + return name; + } + + @Override + public Price getPrice() { + return price; + } + + @Override + public Type getType() { + return type; + } + + + @Override + public Set getTags() { + return new HashSet<>(tags); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ReadOnlyMenus // instanceof handles nulls + && this.isSameStateAs((ReadOnlyMenus) other)); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name); + } + + @Override + public String toString() { + return getAsText(); + } + + @Override + public int compareTo(Menu target) { + return this.name.toString().compareTo(target.name.toString()); + } +} diff --git a/src/seedu/addressbook/data/menu/MenuName.java b/src/seedu/addressbook/data/menu/MenuName.java new file mode 100644 index 000000000..27266f917 --- /dev/null +++ b/src/seedu/addressbook/data/menu/MenuName.java @@ -0,0 +1,67 @@ +package seedu.addressbook.data.menu; + +import java.util.Arrays; +import java.util.List; + +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author SalsabilTasnia +/** + * Represents a Menu's name in the Menu list. + * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} + */ +public class MenuName { + + public static final String EXAMPLE = "Cheese Burger"; + public static final String MESSAGE_NAME_CONSTRAINTS = "Menu Item names should be spaces or alphanumeric characters." + + " It should contain minimum one character and must not be longer than" + + " 30 alphanumeric characters and spaces"; + public static final String NAME_VALIDATION_REGEX = "[\\p{Alnum} ]{1,30}+"; + + public final String fullName; + + /** + * Validates given name. + * + * @throws IllegalValueException if given name string is invalid. + */ + public MenuName(String name) throws IllegalValueException { + String trimmedName = name.trim(); + if (!isValidName(trimmedName)) { + throw new IllegalValueException(MESSAGE_NAME_CONSTRAINTS); + } + this.fullName = trimmedName; + } + + /** + * Returns true if a given string is a valid person name. + */ + public static boolean isValidName(String test) { + return test.matches(NAME_VALIDATION_REGEX); + } + + /** + * Retrieves a listing of every word in the name, in order. + */ + public List getWordsInName() { + return Arrays.asList(fullName.toLowerCase().split("\\s+")); + } + + @Override + public String toString() { + return fullName; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof MenuName // instanceof handles nulls + && this.fullName.equals(((MenuName) other).fullName)); // state check + } + + @Override + public int hashCode() { + return fullName.hashCode(); + } + +} diff --git a/src/seedu/addressbook/data/menu/Price.java b/src/seedu/addressbook/data/menu/Price.java new file mode 100644 index 000000000..96098c375 --- /dev/null +++ b/src/seedu/addressbook/data/menu/Price.java @@ -0,0 +1,85 @@ +package seedu.addressbook.data.menu; + +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author SalsabilTasnia +/** + * Price of a particular menu item in the Restaurant Management System. + * Guarantees: immutable; is valid as declared in {@link #isValidPrice(String)} + */ +public class Price { + + public static final String EXAMPLE = "$4.40"; + public static final String MESSAGE_PRICE_CONSTRAINTS = "Price must follow " + + "the format $A.BC or $A, " + + "where A is a number of 1-3 digits and B and C are 1 digit each"; + + public static final String PRICE_VALIDATION_REGEX = "\\$[1-9][0-9]{0,2}(\\.[0-9]{2})?|\\$0\\.[0-9]{2}|\\$0"; + + public final String value; + + /** + * Validates given phone number. + * + * @throws IllegalValueException if given phone string is invalid. + */ + public Price(String price) throws IllegalValueException { + String trimmedPrice = price.trim(); + if (!isValidPrice(trimmedPrice)) { + throw new IllegalValueException(MESSAGE_PRICE_CONSTRAINTS); + } + this.value = trimmedPrice; + } + + /** + * Convert price value from String to double + */ + public double convertValueOfPriceToDouble() { + String doubleValue = this.value.substring(1); + return Double.parseDouble(doubleValue); + } + + /** + * Convert any double into a currency String format + */ + public static String convertPriceToString(double priceInDouble) { + String valueAsString = Double.toString(priceInDouble); + String valueAsPrice = "$" + valueAsString; + //ensuring the final answer is always returned in 2 decimal places + int decimalIndex = valueAsPrice.indexOf("."); + if ((valueAsPrice.substring(decimalIndex)).length() < 3) { + valueAsPrice = valueAsPrice + "0"; + } else if ((valueAsPrice.substring(decimalIndex)).length() >= 3) { + valueAsPrice = valueAsPrice.substring(0, decimalIndex + 3); + + } + + return valueAsPrice; + } + + + /** + * Checks if a given string is a valid menu item price. + */ + public static boolean isValidPrice(String test) { + return test.matches(PRICE_VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Price // instanceof handles nulls + && this.value.equals(((Price) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/seedu/addressbook/data/menu/ReadOnlyMenus.java b/src/seedu/addressbook/data/menu/ReadOnlyMenus.java new file mode 100644 index 000000000..eccbc8268 --- /dev/null +++ b/src/seedu/addressbook/data/menu/ReadOnlyMenus.java @@ -0,0 +1,46 @@ +package seedu.addressbook.data.menu; + +import java.util.Set; + +import seedu.addressbook.data.tag.Tag; + +//@@author SalsabilTasnia +/** + * A read-only immutable interface for a Menu Item in the Rms. + * Implementations should guarantee: details are present and not null, field values are validated. + */ +public interface ReadOnlyMenus { + + MenuName getName(); + Price getPrice(); + Type getType(); + + /** + * The returned {@code Set} is a deep copy of the internal {@code Set}, + * changes on the returned list will not affect the menu item's internal tags. + */ + Set getTags(); + + /** + * Returns true if the values inside this object is same as those of the other + * (Note: interfaces cannot override .equals) + */ + default boolean isSameStateAs(ReadOnlyMenus other) { + return other == this // short circuit if same object + || (other != null // this is first to avoid NPE below + && other.getName().equals(this.getName())); + } + + /** + * Formats a menu item as text, showing all contact details. + */ + default String getAsText() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()).append(" | Price ").append(getPrice()).append(" | Type: ").append(getType()); + builder.append(" | Tags: "); + for (Tag tag : getTags()) { + builder.append(tag); + } + return builder.toString(); + } +} diff --git a/src/seedu/addressbook/data/menu/Type.java b/src/seedu/addressbook/data/menu/Type.java new file mode 100644 index 000000000..b897ce1c2 --- /dev/null +++ b/src/seedu/addressbook/data/menu/Type.java @@ -0,0 +1,74 @@ +package seedu.addressbook.data.menu; + +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author SalsabilTasnia +/** + * Represents a Menu type in the Menu list. + * Guarantees: immutable; is valid as declared in {@link #isValidTypeName(String)} + */ +public class Type { + + public static final String EXAMPLE = "main"; + public static final String MESSAGE_TYPE_CONSTRAINTS = + "Item Type should only be one of the few Category:" + + "\n" + "main" + + "\n" + "sides" + + "\n" + "beverage" + + "\n" + "dessert" + + "\n" + "others" + + "\n" + "set meal"; + public static final String TYPE_VALIDATION_REGEX = "[\\p{Alnum} ]+"; + + public final String value; + + /** + * Validates given name. + * + * @throws IllegalValueException if given name string is invalid. + */ + public Type(String type) throws IllegalValueException { + String trimmedType = type.trim(); + if (!isValidTypeName(trimmedType)) { + throw new IllegalValueException(MESSAGE_TYPE_CONSTRAINTS); + } + this.value = trimmedType; + } + + public String getValue() { + return value; + } + + /** + * Returns true if a given string is a valid dish type. + */ + public static boolean isValidTypeName(String test) { + return ("main".equals(test) + || "sides".equals(test) + || "beverage".equals(test) + || "dessert".equals(test) + || "others".equals(test) + || "set meal".equals(test)) + && test.matches(TYPE_VALIDATION_REGEX); + + + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Type // instanceof handles nulls + && this.value.equals(((Type) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/seedu/addressbook/data/menu/UniqueMenuList.java b/src/seedu/addressbook/data/menu/UniqueMenuList.java new file mode 100644 index 000000000..34d74f087 --- /dev/null +++ b/src/seedu/addressbook/data/menu/UniqueMenuList.java @@ -0,0 +1,128 @@ +package seedu.addressbook.data.menu; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.exception.DuplicateDataException; + +//@@author SalsabilTasnia +/** + * A list of menus. Does not allow null elements or duplicates. + * + * @see Menu#equals(Object) + * @see Utils#elementsAreUnique(Collection) + */ +public class UniqueMenuList implements Iterable { + + private final List internalMenuList = new ArrayList<>(); + + /** + * Signals that an operation would have violated the 'no duplicates' property of the list. + */ + public static class DuplicateMenuException extends DuplicateDataException { + protected DuplicateMenuException() { + + super("Operation would result in duplicate menu items"); + } + } + + /** + * Signals that an operation targeting a specified menu item in the list would fail because + * there is no such matching person in the list. + */ + public static class MenuNotFoundException extends Exception {} + + /** + * Constructs empty menu list. + */ + public UniqueMenuList() {} + + /** + * Constructs a list from the items in the given collection. + * @param menus a collection of menus + * @throws DuplicateMenuException if the {@code menus} contains duplicate menus + */ + public UniqueMenuList(Collection menus) throws DuplicateMenuException { + if (!Utils.elementsAreUnique(menus)) { + throw new DuplicateMenuException(); + } + internalMenuList.addAll(menus); + } + + /** + * Constructs a shallow copy of the list. + */ + public UniqueMenuList(UniqueMenuList source) { + internalMenuList.addAll(source.internalMenuList); + } + + /** + * Unmodifiable java List view with elements cast as immutable {@link ReadOnlyMenus}s. + * For use with other methods/libraries. + * Any changes to the internal list/elements are immediately visible in the returned list. + */ + public List immutableListView() { + return Collections.unmodifiableList(internalMenuList); + } + + + /** + * Checks if the list contains an equivalent menu item as the given argument. + */ + public boolean contains(ReadOnlyMenus toCheck) { + return internalMenuList.contains(toCheck); + } + + /** + * Adds a menu item to the list. + * + * @throws DuplicateMenuException if the menu item to add is a duplicate of an existing menu item in the list. + */ + public void add(Menu toAdd) throws DuplicateMenuException { + if (contains(toAdd)) { + throw new DuplicateMenuException(); + } + internalMenuList.add(toAdd); + } + + /** + * Removes the equivalent menu item from the list. + * + * @throws MenuNotFoundException if no such menu item could be found in the list. + */ + public void remove(ReadOnlyMenus toRemove) throws MenuNotFoundException { + final boolean menuFoundAndDeleted = internalMenuList.remove(toRemove); + if (!menuFoundAndDeleted) { + throw new MenuNotFoundException(); + } + } + + /** + * Clears all menu items in list. + */ + public void clear() { + internalMenuList.clear(); + } + + @Override + public Iterator iterator() { + return internalMenuList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueMenuList // instanceof handles nulls + && this.internalMenuList.equals(((UniqueMenuList) other).internalMenuList)); + } + + @Override + public int hashCode() { + return internalMenuList.hashCode(); + } + +} diff --git a/src/seedu/addressbook/data/order/Order.java b/src/seedu/addressbook/data/order/Order.java new file mode 100644 index 000000000..b9d68b632 --- /dev/null +++ b/src/seedu/addressbook/data/order/Order.java @@ -0,0 +1,205 @@ +package seedu.addressbook.data.order; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import seedu.addressbook.data.member.Member; +import seedu.addressbook.data.member.Points; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.menu.Menu; +import seedu.addressbook.data.menu.ReadOnlyMenus; + +//@@author px1099 +/** + * Represents an Order in the ordering list. + */ + +public class Order implements ReadOnlyOrder { + + private ReadOnlyMember customer; + private Date date; + private Points points; + private double price; + + /** + * Map with Dishes as keys and quantities as Integer values. + * + * Use {@code entrySet()} to create a Set for iteration. + */ + private final Map dishItems = new HashMap<>(); + + /** + * Constructor used for drafting new order. Uses empty customer instead of null. + */ + public Order() { + this.customer = new Member(); + this.date = new Date(); + this.points = new Points(); + this.price = 0; + } + + /** + * Constructor for new order to be added to the order list. + */ + public Order(ReadOnlyMember customer, Map dishItems, int pointsToRedeem) { + this.customer = customer; + this.dishItems.putAll(dishItems); + this.points = new Points(pointsToRedeem); + this.price = calculatePrice(); + this.date = new Date(); + } + + /** + * Constructor for edited order to keep the original ordered date. + */ + public Order(ReadOnlyMember customer, Date date, Map dishItems, int pointsToRedeem) { + this.customer = customer; + this.dishItems.putAll(dishItems); + this.points = new Points(pointsToRedeem); + this.price = calculatePrice(); + this.date = date; + } + + /** + * Full constructor. + */ + public Order(ReadOnlyMember customer, + Date date, + double price, + Map dishItems, + int pointsToRedeem) { + this.customer = customer; + this.dishItems.putAll(dishItems); + this.points = new Points(pointsToRedeem); + this.price = price; + this.date = date; + } + + /** + * Copy constructor. + */ + public Order(ReadOnlyOrder source) { + this(source.getCustomer(), source.getDate(), source.getPrice(), source.getDishItems(), source.getPoints()); + } + + @Override + public ReadOnlyMember getCustomer() { + return customer; + } + + /** + * Defensively returning the copy of the order's date + */ + @Override + public Date getDate() { + return new Date(date.getTime()); + } + + @Override + public double getPrice() { + return price; + } + + @Override + public int getPoints() { + return points.getCurrentPoints(); + } + + @Override + public Map getDishItems() { + return new HashMap<>(dishItems); + } + + @Override + public double getOriginalPrice() { + double result = 0; + for (Map.Entry m: getDishItems().entrySet()) { + double dishPrice = m.getKey().getPrice().convertValueOfPriceToDouble(); + int dishQuantity = m.getValue(); + result += (dishPrice * dishQuantity); + } + return result; + } + + @Override + public int getMaxPointsRedeemable() { + int pointsLimitByPrice = Points.getRedeemedPointsValue(getOriginalPrice()); + int pointsLimitByMember = customer.getCurrentPointsValue(); + return Integer.min(pointsLimitByPrice, pointsLimitByMember); + } + + @Override + public int getEarnedPointsValue() { + return Points.getEarnedPointsValue(price); + } + + + + public void setCustomer(ReadOnlyMember customer) { + this.customer = customer; + } + + public void setPoints(int value) { + points.setCurrentPoints(value); + price = calculatePrice(); + } + + /** + * Calculate and return the total price of an order. + */ + public double calculatePrice() { + double result = getOriginalPrice(); + result -= points.getRedeemedDiscount(); + return result; + } + + /** + * Change the quantity of a dish in an order. + * Used to add, remove and edit dishes in an order. + */ + public void changeDishQuantity(ReadOnlyMenus readOnlyDish, int quantity) { + ReadOnlyMenus dish = new Menu(readOnlyDish); + if (quantity == 0) { + dishItems.remove(dish); + } else if (quantity > 0) { + dishItems.put(dish, quantity); + } + price = calculatePrice(); + } + + @Override + public boolean hasCustomerField() { + return !(customer.equals(new Member())); + } + + @Override + public boolean hasDishItems() { + return !(dishItems.isEmpty()); + } + + @Override + public boolean hasPoints() { + return customer.getCurrentPointsValue() != 0; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ReadOnlyOrder // instanceof handles nulls + && this.isSameStateAs((ReadOnlyOrder) other)); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(customer, date, price, dishItems, points); + } + + @Override + public String toString() { + return getAsText(); + } + +} diff --git a/src/seedu/addressbook/data/order/ReadOnlyOrder.java b/src/seedu/addressbook/data/order/ReadOnlyOrder.java new file mode 100644 index 000000000..ab6d8a37b --- /dev/null +++ b/src/seedu/addressbook/data/order/ReadOnlyOrder.java @@ -0,0 +1,173 @@ +package seedu.addressbook.data.order; + +import java.util.Date; +import java.util.Map; + +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.menu.Price; +import seedu.addressbook.data.menu.ReadOnlyMenus; + +//@@author px1099 +/** + * A read-only immutable interface for an Order in the ordering list. + */ +public interface ReadOnlyOrder { + + int MAX_DISH_NAME_DISPLAY_LENGTH = 24; + int MAX_DISH_PRICE_DISPLAY_LENGTH = 12; + int MAX_DISH_QUANTITY_DIGITS = 3; + String MULTIPLY_SIGN = "x "; + + ReadOnlyMember getCustomer(); + Date getDate(); + double getPrice(); + double getOriginalPrice(); + int getPoints(); + int getMaxPointsRedeemable(); + int getEarnedPointsValue(); + Map getDishItems(); + + boolean hasCustomerField(); + boolean hasDishItems(); + boolean hasPoints(); + + + /** + * Returns true if the values inside this object is same as those of the other + * (Note: interfaces cannot override .equals) + */ + default boolean isSameStateAs(ReadOnlyOrder other) { + return other == this // short circuit if same object + || (other != null // this is first to avoid NPE below + && other.getCustomer().equals(this.getCustomer()) // state checks here onwards + && other.getDate().equals(this.getDate()) + && other.getDishItems().equals(this.getDishItems())); + } + + /** + * Formats an order as text, showing all details. + */ + default String getAsText() { + final StringBuilder builder = new StringBuilder(); + if (hasCustomerField()) { + builder.append("Customer: ").append(getCustomer().getAsTextInOrderList()); + builder.append("\n\t").append(" "); + } + builder.append("Date: ").append(getDate()); + int i = 0; + for (Map.Entry m: getDishItems().entrySet()) { + i++; + String dishName = m.getKey().getName().toString(); + String dishPrice = "(" + m.getKey().getPrice().toString() + ")"; + String dishQuantity = "" + m.getValue(); + dishQuantity = MULTIPLY_SIGN + + Utils.blankSpace(MAX_DISH_QUANTITY_DIGITS - dishQuantity.length()) + + dishQuantity; + builder.append("\n\t\t"); + builder.append(i).append(". "); + builder.append(dishName); + if (dishName.length() > MAX_DISH_NAME_DISPLAY_LENGTH) { + builder.append("\n\t\t").append(" "); + builder.append(Utils.blankSpace(MAX_DISH_NAME_DISPLAY_LENGTH)); + } else { + builder.append(Utils.blankSpace(dishName, MAX_DISH_NAME_DISPLAY_LENGTH)); + } + builder.append(dishPrice); + builder.append(Utils.blankSpace(dishPrice, MAX_DISH_PRICE_DISPLAY_LENGTH)); + builder.append(dishQuantity); + } + if (hasCustomerField()) { + builder.append("\n\t Redeemed points:\t").append(getPoints()); + } + builder.append("\n\t Total price:\t\t"); + builder.append(Price.convertPriceToString(getPrice())); + return builder.toString(); + } + + default String getAsTextAfterAdd() { + final StringBuilder builder = new StringBuilder(); + if (hasCustomerField()) { + builder.append("\tCustomer: ").append(getCustomer().getAsTextInOrderList()).append("\n"); + } + builder.append("\tDate: ").append(getDate()); + int i = 0; + for (Map.Entry m: getDishItems().entrySet()) { + i++; + String dishName = m.getKey().getName().toString(); + String dishPrice = "(" + m.getKey().getPrice().toString() + ")"; + String dishQuantity = "" + m.getValue(); + dishQuantity = MULTIPLY_SIGN + + Utils.blankSpace(MAX_DISH_QUANTITY_DIGITS - dishQuantity.length()) + + dishQuantity; + builder.append("\n\t\t"); + builder.append(i).append(". "); + builder.append(dishName); + if (dishName.length() > MAX_DISH_NAME_DISPLAY_LENGTH) { + builder.append("\n\t\t").append(" "); + builder.append(Utils.blankSpace(MAX_DISH_NAME_DISPLAY_LENGTH)); + } else { + builder.append(Utils.blankSpace(dishName, MAX_DISH_NAME_DISPLAY_LENGTH)); + } + builder.append(dishPrice); + builder.append(Utils.blankSpace(dishPrice, MAX_DISH_PRICE_DISPLAY_LENGTH)); + builder.append(dishQuantity); + } + if (hasCustomerField()) { + builder.append("\n\tRedeemed points:\t\t").append(getPoints()); + } + builder.append("\n\tTotal price:\t\t"); + builder.append(Price.convertPriceToString(getPrice())); + builder.append("\n\tEarned points:\t\t").append(getEarnedPointsValue()); + return builder.toString(); + } + + /** + * Formats an draft order as text. Null fields are shown as empty. + */ + default String getDraftDetailsAsText() { + final StringBuilder builder = new StringBuilder(); + builder.append("\tCustomer: "); + if (hasCustomerField()) { + builder.append(getCustomer().getAsTextInOrderList()); + } else { + builder.append(""); + } + builder.append("\n\tDishes: "); + if (hasDishItems()) { + int i = 0; + for (Map.Entry m: getDishItems().entrySet()) { + i++; + String dishName = m.getKey().getName().toString(); + String dishPrice = "(" + m.getKey().getPrice().toString() + ")"; + String dishQuantity = "" + m.getValue(); + dishQuantity = MULTIPLY_SIGN + + Utils.blankSpace(MAX_DISH_QUANTITY_DIGITS - dishQuantity.length()) + + dishQuantity; + if (i != 1) { + builder.append("\n\t\t"); + } + builder.append(i).append(". "); + builder.append(dishName); + if (dishName.length() > MAX_DISH_NAME_DISPLAY_LENGTH) { + builder.append("\n\t\t").append(" "); + builder.append(Utils.blankSpace(MAX_DISH_NAME_DISPLAY_LENGTH)); + } else { + builder.append(Utils.blankSpace(dishName, MAX_DISH_NAME_DISPLAY_LENGTH)); + } + builder.append(dishPrice); + builder.append(Utils.blankSpace(dishPrice, MAX_DISH_PRICE_DISPLAY_LENGTH)); + builder.append(dishQuantity); + } + } else { + builder.append(""); + } + if (hasCustomerField() && getMaxPointsRedeemable() != 0) { + builder.append("\n\tRedeemed points:\t\t").append(getPoints()); + builder.append(" / ").append(getMaxPointsRedeemable()); + } + builder.append("\n\tTotal price:\t\t"); + builder.append(Price.convertPriceToString(getPrice())); + return builder.toString(); + } +} diff --git a/src/seedu/addressbook/data/order/UniqueOrderList.java b/src/seedu/addressbook/data/order/UniqueOrderList.java new file mode 100644 index 000000000..24cb1a047 --- /dev/null +++ b/src/seedu/addressbook/data/order/UniqueOrderList.java @@ -0,0 +1,139 @@ +package seedu.addressbook.data.order; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.exception.DuplicateDataException; + +//@@author px1099 +/** + * A list of orders. Does not allow null element or duplicates. + * + * @see Order#equals(Object) + * @see Utils#elementsAreUnique(Collection) + */ +public class UniqueOrderList implements Iterable { + + private final List internalList = new ArrayList<>(); + + /** + * Signals that an operation would have violated the 'no duplicates' property of the list. + */ + public static class DuplicateOrderException extends DuplicateDataException { + protected DuplicateOrderException() { + super("Operation would result in duplicate orders"); + } + } + + /** + * Signals that an operation targeting a specified order in the list would fail because + * there is no such matching order in the list. + */ + public static class OrderNotFoundException extends Exception {} + + /** + * Constructs empty order list. + */ + public UniqueOrderList() {} + + /** + * Constructs an order list with the given orders. + */ + public UniqueOrderList(Order... orders) throws DuplicateOrderException { + final List initialTags = Arrays.asList(orders); + if (!Utils.elementsAreUnique(initialTags)) { + throw new DuplicateOrderException(); + } + internalList.addAll(initialTags); + } + + /** + * Constructs a list from the items in the given collection. + * @param orders a collection of persons + * @throws DuplicateOrderException if the {@code persons} contains duplicate persons + */ + public UniqueOrderList(Collection orders) throws DuplicateOrderException { + if (!Utils.elementsAreUnique(orders)) { + throw new DuplicateOrderException(); + } + internalList.addAll(orders); + } + + /** + * Constructs a shallow copy of the list. + */ + public UniqueOrderList(UniqueOrderList source) { + internalList.addAll(source.internalList); + } + + /** + * Unmodifiable java List view with elements cast as immutable {@link ReadOnlyOrder}s. + * For use with other methods/libraries. + * Any changes to the internal list/elements are immediately visible in the returned list. + */ + public List immutableListView() { + return Collections.unmodifiableList(internalList); + } + + + /** + * Checks if the list contains an equivalent order as the given argument. + */ + public boolean contains(ReadOnlyOrder toCheck) { + return internalList.contains(toCheck); + } + + /** + * Adds an order to the list. + * + * @throws DuplicateOrderException if the person to add is a duplicate of an existing person in the list. + */ + public void add(Order toAdd) throws DuplicateOrderException { + if (contains(toAdd)) { + throw new DuplicateOrderException(); + } + internalList.add(toAdd); + } + + /** + * Removes the equivalent order from the list. + * + * @throws OrderNotFoundException if no such order could be found in the list. + */ + public void remove(ReadOnlyOrder toRemove) throws OrderNotFoundException { + final boolean orderFoundAndDeleted = internalList.remove(toRemove); + if (!orderFoundAndDeleted) { + throw new OrderNotFoundException(); + } + } + + /** + * Clears all orders in list. + */ + public void clear() { + internalList.clear(); + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueOrderList // instanceof handles nulls + && this.internalList.equals(((UniqueOrderList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + +} diff --git a/src/seedu/addressbook/data/person/Address.java b/src/seedu/addressbook/data/person/Address.java deleted file mode 100644 index 8ac726444..000000000 --- a/src/seedu/addressbook/data/person/Address.java +++ /dev/null @@ -1,58 +0,0 @@ -package seedu.addressbook.data.person; - -import seedu.addressbook.data.exception.IllegalValueException; - -/** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} - */ -public class Address { - - public static final String EXAMPLE = "123, some street"; - public static final String MESSAGE_ADDRESS_CONSTRAINTS = "Person addresses can be in any format"; - public static final String ADDRESS_VALIDATION_REGEX = ".+"; - - public final String value; - private boolean isPrivate; - - /** - * Validates given address. - * - * @throws IllegalValueException if given address string is invalid. - */ - public Address(String address, boolean isPrivate) throws IllegalValueException { - this.isPrivate = isPrivate; - if (!isValidAddress(address)) { - throw new IllegalValueException(MESSAGE_ADDRESS_CONSTRAINTS); - } - this.value = address; - } - - /** - * Returns true if a given string is a valid person email. - */ - public static boolean isValidAddress(String test) { - return test.matches(ADDRESS_VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && this.value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - - public boolean isPrivate() { - return isPrivate; - } -} \ No newline at end of file diff --git a/src/seedu/addressbook/data/person/Person.java b/src/seedu/addressbook/data/person/Person.java deleted file mode 100644 index fdd99358b..000000000 --- a/src/seedu/addressbook/data/person/Person.java +++ /dev/null @@ -1,90 +0,0 @@ -package seedu.addressbook.data.person; - -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.addressbook.data.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated. - */ -public class Person implements ReadOnlyPerson { - - private Name name; - private Phone phone; - private Email email; - private Address address; - - private final Set tags = new HashSet<>(); - /** - * Assumption: Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - /** - * Copy constructor. - */ - public Person(ReadOnlyPerson source) { - this(source.getName(), source.getPhone(), source.getEmail(), source.getAddress(), source.getTags()); - } - - @Override - public Name getName() { - return name; - } - - @Override - public Phone getPhone() { - return phone; - } - - @Override - public Email getEmail() { - return email; - } - - @Override - public Address getAddress() { - return address; - } - - @Override - public Set getTags() { - return new HashSet<>(tags); - } - - /** - * Replaces this person's tags with the tags in {@code replacement}. - */ - public void setTags(Set replacement) { - tags.clear(); - tags.addAll(replacement); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof ReadOnlyPerson // instanceof handles nulls - && this.isSameStateAs((ReadOnlyPerson) other)); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - return getAsTextShowAll(); - } - -} diff --git a/src/seedu/addressbook/data/person/Phone.java b/src/seedu/addressbook/data/person/Phone.java deleted file mode 100644 index b5a556de4..000000000 --- a/src/seedu/addressbook/data/person/Phone.java +++ /dev/null @@ -1,59 +0,0 @@ -package seedu.addressbook.data.person; - -import seedu.addressbook.data.exception.IllegalValueException; - -/** - * Represents a Person's phone number in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} - */ -public class Phone { - - public static final String EXAMPLE = "123456789"; - public static final String MESSAGE_PHONE_CONSTRAINTS = "Person phone numbers should only contain numbers"; - public static final String PHONE_VALIDATION_REGEX = "\\d+"; - - public final String value; - private boolean isPrivate; - - /** - * Validates given phone number. - * - * @throws IllegalValueException if given phone string is invalid. - */ - public Phone(String phone, boolean isPrivate) throws IllegalValueException { - this.isPrivate = isPrivate; - phone = phone.trim(); - if (!isValidPhone(phone)) { - throw new IllegalValueException(MESSAGE_PHONE_CONSTRAINTS); - } - this.value = phone; - } - - /** - * Checks if a given string is a valid person phone number. - */ - public static boolean isValidPhone(String test) { - return test.matches(PHONE_VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Phone // instanceof handles nulls - && this.value.equals(((Phone) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - - public boolean isPrivate() { - return isPrivate; - } -} diff --git a/src/seedu/addressbook/data/person/ReadOnlyPerson.java b/src/seedu/addressbook/data/person/ReadOnlyPerson.java deleted file mode 100644 index 4fab58808..000000000 --- a/src/seedu/addressbook/data/person/ReadOnlyPerson.java +++ /dev/null @@ -1,86 +0,0 @@ -package seedu.addressbook.data.person; - -import java.util.Set; - -import seedu.addressbook.data.tag.Tag; - -/** - * A read-only immutable interface for a Person in the addressbook. - * Implementations should guarantee: details are present and not null, field values are validated. - */ -public interface ReadOnlyPerson { - - Name getName(); - Phone getPhone(); - Email getEmail(); - Address getAddress(); - - /** - * The returned {@code Set} is a deep copy of the internal {@code Set}, - * changes on the returned list will not affect the person's internal tags. - */ - Set getTags(); - - /** - * Returns true if the values inside this object is same as those of the other (Note: interfaces cannot override .equals) - */ - default boolean isSameStateAs(ReadOnlyPerson other) { - return other == this // short circuit if same object - || (other != null // this is first to avoid NPE below - && other.getName().equals(this.getName()) // state checks here onwards - && other.getPhone().equals(this.getPhone()) - && other.getEmail().equals(this.getEmail()) - && other.getAddress().equals(this.getAddress())); - } - - /** - * Formats the person as text, showing all contact details. - */ - default String getAsTextShowAll() { - final StringBuilder builder = new StringBuilder(); - final String detailIsPrivate = "(private) "; - builder.append(getName()) - .append(" Phone: "); - if (getPhone().isPrivate()) { - builder.append(detailIsPrivate); - } - builder.append(getPhone()) - .append(" Email: "); - if (getEmail().isPrivate()) { - builder.append(detailIsPrivate); - } - builder.append(getEmail()) - .append(" Address: "); - if (getAddress().isPrivate()) { - builder.append(detailIsPrivate); - } - builder.append(getAddress()) - .append(" Tags: "); - for (Tag tag : getTags()) { - builder.append(tag); - } - return builder.toString(); - } - - /** - * Formats a person as text, showing only non-private contact details. - */ - default String getAsTextHidePrivate() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()); - if (!getPhone().isPrivate()) { - builder.append(" Phone: ").append(getPhone()); - } - if (!getEmail().isPrivate()) { - builder.append(" Email: ").append(getEmail()); - } - if (!getAddress().isPrivate()) { - builder.append(" Address: ").append(getAddress()); - } - builder.append(" Tags: "); - for (Tag tag : getTags()) { - builder.append(tag); - } - return builder.toString(); - } -} diff --git a/src/seedu/addressbook/data/person/UniquePersonList.java b/src/seedu/addressbook/data/person/UniquePersonList.java deleted file mode 100644 index c4848a1b4..000000000 --- a/src/seedu/addressbook/data/person/UniquePersonList.java +++ /dev/null @@ -1,134 +0,0 @@ -package seedu.addressbook.data.person; - -import seedu.addressbook.common.Utils; -import seedu.addressbook.data.exception.DuplicateDataException; - -import java.util.*; - -/** - * A list of persons. Does not allow null elements or duplicates. - * - * @see Person#equals(Object) - * @see Utils#elementsAreUnique(Collection) - */ -public class UniquePersonList implements Iterable { - - /** - * Signals that an operation would have violated the 'no duplicates' property of the list. - */ - public static class DuplicatePersonException extends DuplicateDataException { - protected DuplicatePersonException() { - super("Operation would result in duplicate persons"); - } - } - - /** - * Signals that an operation targeting a specified person in the list would fail because - * there is no such matching person in the list. - */ - public static class PersonNotFoundException extends Exception {} - - private final List internalList = new ArrayList<>(); - - /** - * Constructs empty person list. - */ - public UniquePersonList() {} - - /** - * Constructs a person list with the given persons. - */ - public UniquePersonList(Person... persons) throws DuplicatePersonException { - final List initialTags = Arrays.asList(persons); - if (!Utils.elementsAreUnique(initialTags)) { - throw new DuplicatePersonException(); - } - internalList.addAll(initialTags); - } - - /** - * Constructs a list from the items in the given collection. - * @param persons a collection of persons - * @throws DuplicatePersonException if the {@code persons} contains duplicate persons - */ - public UniquePersonList(Collection persons) throws DuplicatePersonException { - if (!Utils.elementsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - internalList.addAll(persons); - } - - /** - * Constructs a shallow copy of the list. - */ - public UniquePersonList(UniquePersonList source) { - internalList.addAll(source.internalList); - } - - /** - * Unmodifiable java List view with elements cast as immutable {@link ReadOnlyPerson}s. - * For use with other methods/libraries. - * Any changes to the internal list/elements are immediately visible in the returned list. - */ - public List immutableListView() { - return Collections.unmodifiableList(internalList); - } - - - /** - * Checks if the list contains an equivalent person as the given argument. - */ - public boolean contains(ReadOnlyPerson toCheck) { - return internalList.contains(toCheck); - } - - /** - * Adds a person to the list. - * - * @throws DuplicatePersonException if the person to add is a duplicate of an existing person in the list. - */ - public void add(Person toAdd) throws DuplicatePersonException { - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Removes the equivalent person from the list. - * - * @throws PersonNotFoundException if no such person could be found in the list. - */ - public void remove(ReadOnlyPerson toRemove) throws PersonNotFoundException { - final boolean personFoundAndDeleted = internalList.remove(toRemove); - if (!personFoundAndDeleted) { - throw new PersonNotFoundException(); - } - } - - /** - * Clears all persons in list. - */ - public void clear() { - internalList.clear(); - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof UniquePersonList // instanceof handles nulls - && this.internalList.equals( - ((UniquePersonList) other).internalList)); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - -} diff --git a/src/seedu/addressbook/data/statistics/AsciiTable.java b/src/seedu/addressbook/data/statistics/AsciiTable.java new file mode 100644 index 000000000..0d05d993f --- /dev/null +++ b/src/seedu/addressbook/data/statistics/AsciiTable.java @@ -0,0 +1,133 @@ +package seedu.addressbook.data.statistics; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +//@@author AngWM +/** + * Represents a table made in Ascii + */ +public class AsciiTable { + private String[] headings; + private List> data; + private int noOfColumns; + private int[] columnWidths; + + private char colBorder; + private char rowBorder; + private char rowHBorder; + private String prePad; + private String postPad; + + public AsciiTable(String[] headings) { + if (headings == null) { + throw new IllegalArgumentException("Headings is null."); + } + if (headings.length == 0) { + throw new IllegalArgumentException("No headings."); + } + + this.colBorder = '|'; + this.rowBorder = '-'; + this.rowHBorder = '='; + this.prePad = " "; + this.postPad = " "; + + this.noOfColumns = headings.length; + this.columnWidths = new int[noOfColumns]; + this.headings = headings; + this.data = new ArrayList<>(); + for (int i = 0; i < noOfColumns; i++) { + this.columnWidths[i] = headings[i].length(); + } + } + + /** + * Add and populate a row if the number number of entries is the same as the number of columns + */ + public void addRow(String[] rowData) { + if (rowData.length != this.noOfColumns) { + return; + } + + data.add(Arrays.asList(rowData)); + for (int i = 0; i < this.noOfColumns; i++) { + if (columnWidths[i] < rowData[i].length()) { + columnWidths[i] = rowData[i].length(); + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(createRowBorder(true)); + + for (int i = 0; i < noOfColumns; i++) { + String cellString = headings[i]; + sb.append(padCell(cellString, columnWidths[i], ' ', i == 0)); + } + sb.append("\n"); + sb.append(createRowBorder(true)); + + for (int i = 0; i < data.size(); i++) { + for (int j = 0; j < noOfColumns; j++) { + String cellString = data.get(i).get(j); + sb.append(padCell(cellString, columnWidths[j], ' ', j == 0)); + } + sb.append("\n"); + sb.append(createRowBorder(false)); + } + sb.append("\n"); + return sb.toString(); + } + + /** + * Create a border for a row + */ + private String createRowBorder(boolean heading) { + int tableWidth = calculateTableWidth(); + StringBuffer outputBuffer = new StringBuffer(tableWidth); + for (int i = 0; i < tableWidth; i++) { + if (heading) { + outputBuffer.append(rowHBorder); + } else { + outputBuffer.append(rowBorder); + } + } + return outputBuffer.toString() + "\n"; + } + + /** + * Calculate and return the width of a table + */ + private int calculateTableWidth() { + int width = 0; + width += 1; + for (int columnWidth : columnWidths) { + width += columnWidth; + width += 1; + width += prePad.length(); + width += postPad.length(); + } + + return width; + } + + /** + * Return a String after padding a String into a table cell + */ + private String padCell(String in, int width, char pad, boolean first) { + int cellSize = in.length(); + int padSize = width - cellSize; + StringBuffer outputBuffer = new StringBuffer(padSize); + for (int i = 0; i < padSize; i++) { + outputBuffer.append(pad); + } + + + return ((first) ? colBorder : "") + this.prePad + in + outputBuffer.toString() + this.postPad + colBorder; + } + +} diff --git a/src/seedu/addressbook/data/statistics/MemberDateTable.java b/src/seedu/addressbook/data/statistics/MemberDateTable.java new file mode 100644 index 000000000..0d580938e --- /dev/null +++ b/src/seedu/addressbook/data/statistics/MemberDateTable.java @@ -0,0 +1,147 @@ +package seedu.addressbook.data.statistics; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; + +//@@author AngWM +/** + * Represent a table storing the registering day of the members in the member list + */ +public class MemberDateTable { + private Map yearMap; + private Calendar calendar; + + public MemberDateTable() { + this.calendar = new GregorianCalendar(); + this.yearMap = new HashMap<>(); + } + + /** + * Adjust the yearMap based on the added Date + */ + public void addData(Date date) { + calendar.setTime(date); + if (!yearMap.containsKey(calendar.get(Calendar.YEAR))) { + YearMember newYear = new YearMember(); + yearMap.put(calendar.get(Calendar.YEAR), newYear.addData(date)); + } else { + yearMap.put(calendar.get(Calendar.YEAR), yearMap.get(calendar.get(Calendar.YEAR)).addData(date)); + } + } + + public int getYearCount(Date date) { + calendar.setTime(date); + if (yearMap.containsKey(calendar.get(Calendar.YEAR))) { + return yearMap.get(calendar.get(Calendar.YEAR)).getCount(); + } else { + return 0; + } + } + + public int getMonthCount(Date date) { + calendar.setTime(date); + return yearMap.get(calendar.get(Calendar.YEAR)).getMonthMap().get(calendar.get(Calendar.MONTH)).getCount(); + } + + public int getDayCount() { + return yearMap.get(calendar.get(Calendar.YEAR)) + .getMonthMap().get(calendar.get(Calendar.MONTH)) + .getDayMap().get(calendar.get(Calendar.DATE)) + .getCount(); + } +} + +/** + * Represents an year in the yearMap + */ +class YearMember { + private int count; + private Map monthMap; + private Calendar calendar; + + public YearMember() { + this.calendar = new GregorianCalendar(); + this.count = 0; + this.monthMap = new HashMap<>(); + for (int i = 0; i < 12; i++) { + getMonthMap().put(i, new MonthMember()); + } + } + + /** + * Adjust the monthMap based on the added Date and return the yearMember container object + */ + public YearMember addData(Date date) { + calendar.setTime(date); + count = getCount() + 1; + getMonthMap().put(calendar.get(Calendar.MONTH), getMonthMap().get(calendar.get(Calendar.MONTH)).addData(date)); + return this; + } + + public int getCount() { + return count; + } + + public Map getMonthMap() { + return monthMap; + } +} + +/** + * Represents a month in the monthMap + */ +class MonthMember { + private int count; + private Map dayMap; + private Calendar calendar; + + public MonthMember() { + this.calendar = new GregorianCalendar(); + this.count = 0; + this.dayMap = new HashMap<>(); + for (int i = 1; i <= 31; i++) { + getDayMap().put(i, new DayMember()); + } + } + + /** + * Adjust the dayMap based on the added Date and return the MonthMember container object + */ + public MonthMember addData(Date date) { + calendar.setTime(date); + count = getCount() + 1; + getDayMap().put(calendar.get(Calendar.DATE), getDayMap().get(calendar.get(Calendar.DATE)).addData()); + return this; + } + + public int getCount() { + return count; + } + + public Map getDayMap() { + return dayMap; + } +} + +/** + * Represents a day in the dayMap + */ +class DayMember { + private int count; + + public DayMember() { + this.count = 0; + } + + public DayMember addData() { + count = getCount() + 1; + return this; + } + + public int getCount() { + return count; + } +} diff --git a/src/seedu/addressbook/data/statistics/OrderDateTable.java b/src/seedu/addressbook/data/statistics/OrderDateTable.java new file mode 100644 index 000000000..2cb1f3d55 --- /dev/null +++ b/src/seedu/addressbook/data/statistics/OrderDateTable.java @@ -0,0 +1,211 @@ +package seedu.addressbook.data.statistics; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; + +import seedu.addressbook.data.order.ReadOnlyOrder; + +//@@author AngWM +/** + * Represent a table storing the ordering date of the orders in the order list + */ +public class OrderDateTable { + private Map yearMap; + private Calendar calendar; + + public OrderDateTable() { + this.calendar = new GregorianCalendar(); + yearMap = new HashMap<>(); + } + + /** + * Adjust the yearMap based on the added Date + */ + public void addData(ReadOnlyOrder order) { + calendar.setTime(order.getDate()); + if (!yearMap.containsKey(calendar.get(Calendar.YEAR))) { + YearOrder newYear = new YearOrder(); + yearMap.put(calendar.get(Calendar.YEAR), newYear.addData(order)); + } else { + yearMap.put(calendar.get(Calendar.YEAR), yearMap.get(calendar.get(Calendar.YEAR)).addData(order)); + } + } + + public int getYearCount(Date date) { + calendar.setTime(date); + if (yearMap.containsKey(calendar.get(Calendar.YEAR))) { + return yearMap.get(calendar.get(Calendar.YEAR)).getCount(); + } else { + return 0; + } + } + + public Double getYearRevenue(Date date) { + calendar.setTime(date); + if (yearMap.containsKey(calendar.get(Calendar.YEAR))) { + return yearMap.get(calendar.get(Calendar.YEAR)).getTotalRevenue(); + } else { + return 0.0; + } + } + + public int getMonthCount(Date date) { + calendar.setTime(date); + if (yearMap.containsKey(calendar.get(Calendar.YEAR))) { + return yearMap.get(calendar.get(Calendar.YEAR)).getMonthMap().get(calendar.get(Calendar.MONTH)).getCount(); + } else { + return 0; + } + } + + public Double getMonthRevenue(Date date) { + calendar.setTime(date); + if (yearMap.containsKey(calendar.get(Calendar.YEAR))) { + return yearMap.get(calendar.get(Calendar.YEAR)) + .getMonthMap().get(calendar.get(Calendar.MONTH)) + .getTotalRevenue(); + } else { + return 0.0; + } + } + + public int getDayCount(Date date) { + calendar.setTime(date); + if (yearMap.containsKey(calendar.get(Calendar.YEAR))) { + return yearMap.get(calendar.get(Calendar.YEAR)) + .getMonthMap().get(calendar.get(Calendar.MONTH)) + .getDayMap().get(calendar.get(Calendar.DATE)) + .getCount(); + } else { + return 0; + } + } + + public Double getDayRevenue(Date date) { + calendar.setTime(date); + if (yearMap.containsKey(calendar.get(Calendar.YEAR))) { + return yearMap.get(calendar.get(Calendar.YEAR)) + .getMonthMap().get(calendar.get(Calendar.MONTH)) + .getDayMap().get(calendar.get(Calendar.DATE)) + .getTotalRevenue(); + } else { + return 0.0; + } + } +} + +/** + * Represents an year in the yearMap + */ +class YearOrder { + private int count; + private Double totalRevenue; + private Map monthMap; + private Calendar calendar; + + public YearOrder() { + this.calendar = new GregorianCalendar(); + this.count = 0; + this.totalRevenue = 0.0; + this.monthMap = new HashMap<>(); + for (int i = 0; i < 12; i++) { + getMonthMap().put(i, new MonthOrder()); + } + } + + /** + * Adjust the monthMap based on the added Date and return the yearOrder container object + */ + public YearOrder addData(ReadOnlyOrder order) { + calendar.setTime(order.getDate()); + count = getCount() + 1; + totalRevenue = getTotalRevenue() + order.getPrice(); + getMonthMap().put(calendar.get(Calendar.MONTH), getMonthMap().get(calendar.get(Calendar.MONTH)).addData(order)); + return this; + } + + public int getCount() { + return count; + } + + public Double getTotalRevenue() { + return totalRevenue; + } + + public Map getMonthMap() { + return monthMap; + } +} + +/** + * Represents a month in the monthMap + */ +class MonthOrder { + private int count; + private Double totalRevenue; + private Map dayMap; + private Calendar calendar; + + public MonthOrder() { + this.calendar = new GregorianCalendar(); + this.count = 0; + this.totalRevenue = 0.0; + this.dayMap = new HashMap<>(); + for (int i = 1; i <= 31; i++) { + getDayMap().put(i, new DayOrder()); + } + } + + /** + * Adjust the dayMap based on the added Date and return the MonthOrder container object + */ + public MonthOrder addData(ReadOnlyOrder order) { + calendar.setTime(order.getDate()); + count = getCount() + 1; + totalRevenue = getTotalRevenue() + order.getPrice(); + getDayMap().put(calendar.get(Calendar.DATE), getDayMap().get(calendar.get(Calendar.DATE)).addData(order)); + return this; + } + + public Map getDayMap() { + return dayMap; + } + + public Double getTotalRevenue() { + return totalRevenue; + } + + public int getCount() { + return count; + } +} + +/** + * Represents a day in the dayMap + */ +class DayOrder { + private int count; + private Double totalRevenue; + + public DayOrder() { + this.count = 0; + this.totalRevenue = 0.0; + } + + public DayOrder addData(ReadOnlyOrder order) { + count = getCount() + 1; + totalRevenue = getTotalRevenue() + order.getPrice(); + return this; + } + + public Double getTotalRevenue() { + return totalRevenue; + } + + public int getCount() { + return count; + } +} diff --git a/src/seedu/addressbook/data/statistics/QuantityRevenuePair.java b/src/seedu/addressbook/data/statistics/QuantityRevenuePair.java new file mode 100644 index 000000000..3fe53eae9 --- /dev/null +++ b/src/seedu/addressbook/data/statistics/QuantityRevenuePair.java @@ -0,0 +1,48 @@ +package seedu.addressbook.data.statistics; + +//@@author AngWM +/** + * Represents a pair of data: quantity and revenue of a statistic field + */ +public class QuantityRevenuePair implements Comparable { + private int quantity; + private double revenue; + + public QuantityRevenuePair() { + this.quantity = 0; + this.revenue = 0; + } + + public QuantityRevenuePair(int quantity, double revenue) { + this.quantity = quantity; + this.revenue = revenue; + } + + /** + * Update the data in the quantity - revenue pair + */ + public QuantityRevenuePair addData(int quantity, double price) { + this.quantity += quantity; + this.revenue += quantity * price; + return this; + } + + public int getQuantity() { + return quantity; + } + + public double getRevenue() { + return revenue; + } + + @Override + public int compareTo(QuantityRevenuePair target) { + if (this.getQuantity() < target.getQuantity()) { + return -1; + } else if (this.getQuantity() > target.getQuantity()) { + return 1; + } else { + return 0; + } + } +} diff --git a/src/seedu/addressbook/data/tag/Tag.java b/src/seedu/addressbook/data/tag/Tag.java index 4e5f595dc..0fd04af93 100644 --- a/src/seedu/addressbook/data/tag/Tag.java +++ b/src/seedu/addressbook/data/tag/Tag.java @@ -3,7 +3,7 @@ import seedu.addressbook.data.exception.IllegalValueException; /** - * Represents a Tag in the address book. + * Represents a Tag in the Menu list. * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} */ public class Tag { @@ -18,12 +18,12 @@ public class Tag { * * @throws IllegalValueException if the given tag name string is invalid. */ - public Tag(String name) throws IllegalValueException { - name = name.trim(); - if (!isValidTagName(name)) { + public Tag(String tag) throws IllegalValueException { + String trimmedTag = tag.trim(); + if (!isValidTagName(trimmedTag)) { throw new IllegalValueException(MESSAGE_TAG_CONSTRAINTS); } - this.tagName = name; + this.tagName = trimmedTag; } /** diff --git a/src/seedu/addressbook/logic/Logic.java b/src/seedu/addressbook/logic/Logic.java index 17afd61a0..49711c66a 100644 --- a/src/seedu/addressbook/logic/Logic.java +++ b/src/seedu/addressbook/logic/Logic.java @@ -1,44 +1,75 @@ package seedu.addressbook.logic; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + import seedu.addressbook.commands.Command; import seedu.addressbook.commands.CommandResult; -import seedu.addressbook.data.AddressBook; -import seedu.addressbook.data.person.ReadOnlyPerson; +import seedu.addressbook.data.Rms; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.order.ReadOnlyOrder; import seedu.addressbook.parser.Parser; import seedu.addressbook.storage.StorageFile; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - /** - * Represents the main Logic of the AddressBook. + * Represents the main Logic of the Rms. */ public class Logic { - private StorageFile storage; - private AddressBook addressBook; + //@@author px1099 + private Rms rms; + + //@@author kangmingtay + /** + * The list of member shown to the user most recently. + */ + private List lastShownMemberList = Collections.emptyList(); + + //@@author SalsabilTasnia + /** + * The list of menu shown to the user most recently. + */ + private List lastShownMenuList = Collections.emptyList(); - /** The list of person shown to the user most recently. */ - private List lastShownList = Collections.emptyList(); + //@@author px1099 + /** + * The list of order shown to the user most recently. + */ + private List lastShownOrderList = Collections.emptyList(); + + //@@author kianhong95 + /** + * The list of employee shown to the user most recently. + */ + private List lastShownEmployeeList = Collections.emptyList(); - public Logic() throws Exception{ + /** + * The list of employee shown to the user most recently. + */ + private List lastShownAttendanceList = Collections.emptyList(); + + //@@author + public Logic() throws Exception { setStorage(initializeStorage()); - setAddressBook(storage.load()); + setRms(storage.load()); } - Logic(StorageFile storageFile, AddressBook addressBook){ + Logic(StorageFile storageFile, Rms rms) { setStorage(storageFile); - setAddressBook(addressBook); + setRms(rms); } - void setStorage(StorageFile storage){ + public void setStorage(StorageFile storage) { this.storage = storage; } - void setAddressBook(AddressBook addressBook){ - this.addressBook = addressBook; + public void setRms(Rms rms) { + this.rms = rms; } /** @@ -53,17 +84,70 @@ public String getStorageFilePath() { return storage.getPath(); } + //@@author kangmingtay /** - * Unmodifiable view of the current last shown list. + * Unmodifiable view of the current last shown member list. */ - public List getLastShownList() { - return Collections.unmodifiableList(lastShownList); + public List getLastShownMemberList() { + return Collections.unmodifiableList(lastShownMemberList); } - protected void setLastShownList(List newList) { - lastShownList = newList; + //@@author kianhong95 + /** + * Unmodifiable view of the current last shown order list. + */ + public List getLastShownEmployeeList() { + return Collections.unmodifiableList(lastShownEmployeeList); } + /** + * Unmodifiable view of the current last shown order list. + */ + public List getLastShownAttendanceList() { + return Collections.unmodifiableList(lastShownAttendanceList); + } + + //@@author SalsabilTasnia + /** + * Unmodifiable view of the current last shown menu list. + */ + public List getLastShownMenuList() { + return Collections.unmodifiableList(lastShownMenuList); + } + + //@@author px1099 + /** + * Unmodifiable view of the current last shown order list. + */ + public List getLastShownOrderList() { + return Collections.unmodifiableList(lastShownOrderList); + } + + //@@author SalsabilTasnia + protected void setLastShownMenuList(List newList) { + lastShownMenuList = newList; + } + + //@@author px1099 + protected void setLastShownOrderList(List newList) { + lastShownOrderList = newList; + } + + //@@author kangmingtay + protected void setLastShownMemberList(List newList) { + lastShownMemberList = newList; + } + + //@@author kianhong95 + protected void setLastShownEmployeeList(List newList) { + lastShownEmployeeList = newList; + } + + protected void setLastShownAttendanceList(List newList) { + lastShownAttendanceList = newList; + } + + //@@author /** * Parses the user command, executes it, and returns the result. * @throws Exception if there was any problem during command execution. @@ -83,17 +167,36 @@ public CommandResult execute(String userCommandText) throws Exception { * @throws Exception if there was any problem during command execution. */ private CommandResult execute(Command command) throws Exception { - command.setData(addressBook, lastShownList); + command.setData(rms, + lastShownMenuList, + lastShownOrderList, + lastShownMemberList, + lastShownEmployeeList); CommandResult result = command.execute(); - storage.save(addressBook); + storage.save(rms); return result; } - /** Updates the {@link #lastShownList} if the result contains a list of Persons. */ + //@@author px1099 + /** + * Updates the last shown lists if the result contains a list of result Objects. + */ private void recordResult(CommandResult result) { - final Optional> personList = result.getRelevantPersons(); - if (personList.isPresent()) { - lastShownList = personList.get(); + final Optional> menuList = result.getRelevantMenus(); + final Optional> orderList = result.getRelevantOrders(); + final Optional> memberList = result.getRelevantMember(); + final Optional> employeeList = result.getRelevantEmployee(); + if (menuList.isPresent()) { + lastShownMenuList = menuList.get(); + } else if (orderList.isPresent()) { + lastShownOrderList = orderList.get(); + } else if (memberList.isPresent()) { + lastShownMemberList = memberList.get(); + } else if (employeeList.isPresent()) { + lastShownEmployeeList = employeeList.get(); } } + + //@@author } + diff --git a/src/seedu/addressbook/parser/Parser.java b/src/seedu/addressbook/parser/Parser.java index 58f4f7e6c..1c7600e08 100644 --- a/src/seedu/addressbook/parser/Parser.java +++ b/src/seedu/addressbook/parser/Parser.java @@ -1,32 +1,127 @@ package seedu.addressbook.parser; -import seedu.addressbook.commands.*; -import seedu.addressbook.data.exception.IllegalValueException; +import static seedu.addressbook.common.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static seedu.addressbook.common.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.ExitCommand; +import seedu.addressbook.commands.HelpCommand; +import seedu.addressbook.commands.IncorrectCommand; +import seedu.addressbook.commands.employee.EmployeeAddCommand; +import seedu.addressbook.commands.employee.EmployeeClockInCommand; +import seedu.addressbook.commands.employee.EmployeeClockOutCommand; +import seedu.addressbook.commands.employee.EmployeeDeleteCommand; +import seedu.addressbook.commands.employee.EmployeeEditCommand; +import seedu.addressbook.commands.employee.EmployeeListCommand; +import seedu.addressbook.commands.member.MemberAddCommand; +import seedu.addressbook.commands.member.MemberDeleteCommand; +import seedu.addressbook.commands.member.MemberListCommand; +import seedu.addressbook.commands.menu.MenuAddCommand; +import seedu.addressbook.commands.menu.MenuClearCommand; +import seedu.addressbook.commands.menu.MenuDeleteCommand; +import seedu.addressbook.commands.menu.MenuFindCommand; +import seedu.addressbook.commands.menu.MenuListByTypeCommand; +import seedu.addressbook.commands.menu.MenuListCommand; +import seedu.addressbook.commands.menu.MenuRecommendationCommand; +import seedu.addressbook.commands.menu.MenuShowMainMenuCommand; +import seedu.addressbook.commands.order.DraftOrderClearCommand; +import seedu.addressbook.commands.order.DraftOrderConfirmCommand; +import seedu.addressbook.commands.order.DraftOrderEditCustomerCommand; +import seedu.addressbook.commands.order.DraftOrderEditDishCommand; +import seedu.addressbook.commands.order.DraftOrderEditPointsCommand; +import seedu.addressbook.commands.order.OrderAddCommand; +import seedu.addressbook.commands.order.OrderClearCommand; +import seedu.addressbook.commands.order.OrderDeleteCommand; +import seedu.addressbook.commands.order.OrderListCommand; +import seedu.addressbook.commands.statistics.StatsEmployeeCommand; +import seedu.addressbook.commands.statistics.StatsHelpCommand; +import seedu.addressbook.commands.statistics.StatsMemberCommand; +import seedu.addressbook.commands.statistics.StatsMenuCommand; +import seedu.addressbook.commands.statistics.StatsOrderCommand; +import seedu.addressbook.data.exception.IllegalValueException; /** * Parses user input. */ public class Parser { - public static final Pattern PERSON_INDEX_ARGS_FORMAT = Pattern.compile("(?.+)"); + /** + * Used for initial separation of command word and args. + */ + public static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + public static final Pattern INDEX_ARGS_FORMAT = Pattern.compile("(?.+)"); public static final Pattern KEYWORDS_ARGS_FORMAT = Pattern.compile("(?\\S+(?:\\s+\\S+)*)"); // one or more keywords separated by whitespace - public static final Pattern PERSON_DATA_ARGS_FORMAT = // '/' forward slashes are reserved for delimiter prefixes + //@@author SalsabilTasnia + public static final Pattern ITEMWORD_ARGS_FORMAT = Pattern.compile("(?[^/]+)"); //one keyword only + + //@@author kianhong95 + // '/' forward slashes are reserved for delimiter prefixes + public static final Pattern EMPLOYEE_DATA_ARGS_FORMAT = + Pattern.compile("(?[^/]+)" + + "p/(?[^/]+)" + + "e/(?[^/]+)" + + "a/(?
[^/]+)" + + "pos/(?[^/]+)"); + + // '/' forward slashes are reserved for delimiter prefixes + public static final Pattern EMPLOYEE_EDIT_DATA_ARGS_FORMAT = + Pattern.compile("(?\\d+ )" + + "((p/(?[^/]+))?)" + + "((e/(?[^/]+))?)" + + "((a/(?
[^/]+))?)" + + "((pos/(?[^/]+))?)"); + + // '/' forward slashes are reserved for delimiter prefixes + public static final Pattern EMPLOYEE_EDIT_DATA_NOARGS_FORMAT = + Pattern.compile("(?\\d+)"); + + // '/' forward slashes are reserved for delimiter prefixes + public static final Pattern CLOCK_IN_DATA_ARGS_FORMAT = + Pattern.compile("(?[^/]+)"); + + //@@author kangmingtay + public static final Pattern MEMBER_DATA_ARGS_FORMAT = + Pattern.compile("(?[^/]+)" + + "e/(?[^/]+)" + ); // variable number of tags + + //@@author SalsabilTasnia + // '/' forward slashes are reserved for delimiter prefixes + public static final Pattern MENU_DATA_ARGS_FORMAT = Pattern.compile("(?[^/]+)" - + " (?p?)p/(?[^/]+)" - + " (?p?)e/(?[^/]+)" - + " (?p?)a/(?
[^/]+)" + + " p/(?[^/]+)" + + "type/(?[^/]+)" + "(?(?: t/[^/]+)*)"); // variable number of tags + //@@author px1099 + public static final Pattern DRAFT_DISH_ARGS_FORMAT = + Pattern.compile("\\d+\\s+q/\\d{1,3}(?:\\s+\\d+\\s+q/\\d{1,3})*"); + + //@@author kangmingtay + public static final Pattern REDEEM_POINTS_ARGS_FORMAT = Pattern.compile("(?[^/]+)"); + + //@@author AngWM + public static final String STATSMENU_DATE_ARGS_FORMAT_PATTERN_COMPILE_STRING = + "(f/(?(0[1-9]|[12]\\d|3[01])(0[1-9]|1[0-2])[12]\\d{3}))?" + + " ?(t/(?(0[1-9]|[12]\\d|3[01])(0[1-9]|1[0-2])[12]\\d{3}))?"; // variable number of tags + public static final Pattern STATSMENU_DATE_ARGS_FORMAT = + Pattern.compile(STATSMENU_DATE_ARGS_FORMAT_PATTERN_COMPILE_STRING); + + //@@author /** * Signals that the user input could not be parsed. */ @@ -36,11 +131,6 @@ public static class ParseException extends Exception { } } - /** - * Used for initial separation of command word and args. - */ - public static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - /** * Parses user input into command for execution. * @@ -53,84 +143,300 @@ public Command parseCommand(String userInput) { return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); } - final String commandWord = matcher.group("commandWord"); + final String commandWord = matcher.group("commandWord").toLowerCase(); final String arguments = matcher.group("arguments"); + switch (commandWord) { + case EmployeeAddCommand.COMMAND_WORD: + return prepareEmployeeAdd(arguments); + + case EmployeeDeleteCommand.COMMAND_WORD: + return prepareEmployeeDelete(arguments); + + case EmployeeEditCommand.COMMAND_WORD: + return prepareEmployeeEdit(arguments); + + case EmployeeListCommand.COMMAND_WORD: + return new EmployeeListCommand(); + + case EmployeeClockInCommand.COMMAND_WORD: + return prepareClockIn(arguments); + + case EmployeeClockOutCommand.COMMAND_WORD: + return prepareClockOut(arguments); + + case MemberListCommand.COMMAND_WORD: + return new MemberListCommand(); + + case MemberAddCommand.COMMAND_WORD: + return prepareAddMember(arguments); + + case MemberDeleteCommand.COMMAND_WORD: + return prepareMemberDelete(arguments); + + case MenuAddCommand.COMMAND_WORD: + return prepareAddMenu(arguments); + + case MenuListCommand.COMMAND_WORD: + return new MenuListCommand(); + + case MenuShowMainMenuCommand.COMMAND_WORD: + return new MenuShowMainMenuCommand(); + + case MenuListByTypeCommand.COMMAND_WORD: + return prepareMenuListByType(arguments); + + case MenuRecommendationCommand.COMMAND_WORD: + return new MenuRecommendationCommand(); + + case MenuDeleteCommand.COMMAND_WORD: + return prepareMenuDelete(arguments); + + case MenuFindCommand.COMMAND_WORD: + return prepareMenuFind(arguments); + + case MenuClearCommand.COMMAND_WORD: + return new MenuClearCommand(); + + case OrderAddCommand.COMMAND_WORD: + return new OrderAddCommand(); + + case OrderDeleteCommand.COMMAND_WORD: + return prepareOrderDelete(arguments); + + case OrderClearCommand.COMMAND_WORD: + return new OrderClearCommand(); + + case OrderListCommand.COMMAND_WORD: + return new OrderListCommand(); + + case DraftOrderEditCustomerCommand.COMMAND_WORD: + return prepareDraftOrderEditCustomer(arguments); + + case DraftOrderEditDishCommand.COMMAND_WORD: + return prepareDraftOrderEditDish(arguments); + + case DraftOrderEditPointsCommand.COMMAND_WORD: + return prepareDraftOrderEditPoints(arguments); + + case DraftOrderClearCommand.COMMAND_WORD: + return new DraftOrderClearCommand(); + + case DraftOrderConfirmCommand.COMMAND_WORD: + return new DraftOrderConfirmCommand(); - case AddCommand.COMMAND_WORD: - return prepareAdd(arguments); + case StatsEmployeeCommand.COMMAND_WORD: + return new StatsEmployeeCommand(); - case DeleteCommand.COMMAND_WORD: - return prepareDelete(arguments); + case StatsMemberCommand.COMMAND_WORD: + return new StatsMemberCommand(); - case ClearCommand.COMMAND_WORD: - return new ClearCommand(); + case StatsMenuCommand.COMMAND_WORD: + return prepareStatsMenu(arguments); - case FindCommand.COMMAND_WORD: - return prepareFind(arguments); + case StatsOrderCommand.COMMAND_WORD: + return new StatsOrderCommand(); - case ListCommand.COMMAND_WORD: - return new ListCommand(); + case StatsHelpCommand.COMMAND_WORD: + return new StatsHelpCommand(); - case ViewCommand.COMMAND_WORD: - return prepareView(arguments); + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); - case ViewAllCommand.COMMAND_WORD: - return prepareViewAll(arguments); + case HelpCommand.COMMAND_WORD: // Fallthrough - case ExitCommand.COMMAND_WORD: - return new ExitCommand(); + default: + return new HelpCommand(); + } + } - case HelpCommand.COMMAND_WORD: // Fallthrough - default: - return new HelpCommand(); + //@@author kangmingtay + /** + * Parses arguments in the context of the add member command. + * @param args full command args string + * @return the prepared command + */ + private Command prepareAddMember(String args) { + final Matcher matcher = MEMBER_DATA_ARGS_FORMAT.matcher(args.trim()); + // Validate arg string format + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MemberAddCommand.MESSAGE_USAGE)); + } + try { + return new MemberAddCommand( + matcher.group("name"), + matcher.group("email") + ); + } catch (IllegalValueException ive) { + return new IncorrectCommand(ive.getMessage()); } } + //@@author SalsabilTasnia /** - * Parses arguments in the context of the add person command. + * Parses arguments in the context of the add menu command. * * @param args full command args string * @return the prepared command */ - private Command prepareAdd(String args){ - final Matcher matcher = PERSON_DATA_ARGS_FORMAT.matcher(args.trim()); + private Command prepareAddMenu(String args) { + final Matcher matcher = MENU_DATA_ARGS_FORMAT.matcher(args.trim()); // Validate arg string format if (!matcher.matches()) { - return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MenuAddCommand.MESSAGE_USAGE)); } try { - return new AddCommand( + return new MenuAddCommand( matcher.group("name"), + matcher.group("price"), + matcher.group("type"), + getTagsFromArgs(matcher.group("tagArguments")) + ); + } catch (IllegalValueException ive) { + return new IncorrectCommand(ive.getMessage()); + } + } - matcher.group("phone"), - isPrivatePrefixPresent(matcher.group("isPhonePrivate")), + //@@author kianhong95 + /** + * Parses arguments in the context of the add employee command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareEmployeeAdd(String args) { + final Matcher matcher = EMPLOYEE_DATA_ARGS_FORMAT.matcher(args.trim()); + // Validate arg string format + if (!matcher.matches()) { + return new IncorrectCommand(String.format( + MESSAGE_INVALID_COMMAND_FORMAT, + EmployeeAddCommand.MESSAGE_USAGE)); + } + try { + return new EmployeeAddCommand( + matcher.group("name"), + matcher.group("phone"), matcher.group("email"), - isPrivatePrefixPresent(matcher.group("isEmailPrivate")), matcher.group("address"), - isPrivatePrefixPresent(matcher.group("isAddressPrivate")), + matcher.group("position")); - getTagsFromArgs(matcher.group("tagArguments")) - ); } catch (IllegalValueException ive) { return new IncorrectCommand(ive.getMessage()); } } /** - * Checks whether the private prefix of a contact detail in the add command's arguments string is present. + * Parses arguments in the context of the clock in command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareClockIn(String args) { + final Matcher matcher = CLOCK_IN_DATA_ARGS_FORMAT.matcher(args.trim()); + // Validate arg string format + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EmployeeClockInCommand.MESSAGE_USAGE)); + } + return new EmployeeClockInCommand(matcher.group("name")); + } + + /** + * Parses arguments in the context of the clock out command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareClockOut(String args) { + final Matcher matcher = CLOCK_IN_DATA_ARGS_FORMAT.matcher(args.trim()); + // Validate arg string format + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EmployeeClockOutCommand.MESSAGE_USAGE)); + } + return new EmployeeClockOutCommand(matcher.group("name")); + } + + /** + * Parses arguments in the context of the delete employee command. + * + * @param args full command args string + * @return the prepared command */ - private static boolean isPrivatePrefixPresent(String matchedPrefix) { - return matchedPrefix.equals("p"); + private Command prepareEmployeeDelete(String args) { + try { + final int targetIndex = parseArgsAsDisplayedIndex(args); + return new EmployeeDeleteCommand(targetIndex); + } catch (ParseException | NumberFormatException e) { + return new IncorrectCommand(String.format( + MESSAGE_INVALID_COMMAND_FORMAT, + EmployeeDeleteCommand.MESSAGE_USAGE)); + } } + //@@author kangmingtay /** - * Extracts the new person's tags from the add command's tag arguments string. + * Parses arguments in the context of the delete member command. + * @param args full command args string + * @return the prepared command + */ + private Command prepareMemberDelete(String args) { + try { + final int targetIndex = parseArgsAsDisplayedIndex(args); + return new MemberDeleteCommand(targetIndex); + } catch (ParseException | NumberFormatException e) { + return new IncorrectCommand(String.format( + MESSAGE_INVALID_COMMAND_FORMAT, + MemberDeleteCommand.MESSAGE_USAGE)); + } + } + + //@@author kianhong95 + /** + * Parses arguments in the context of the edit employee command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareEmployeeEdit(String args) { + final Matcher checkForArgs = EMPLOYEE_EDIT_DATA_NOARGS_FORMAT.matcher(args.trim()); + if (checkForArgs.matches()) { + return new IncorrectCommand(String.format( + EmployeeEditCommand.MESSAGE_NOARGS, + EmployeeEditCommand.MESSAGE_USAGE)); + } + final Matcher matcher = EMPLOYEE_EDIT_DATA_ARGS_FORMAT.matcher(args.trim()); + if (!matcher.matches()) { + return new IncorrectCommand(String.format( + MESSAGE_INVALID_COMMAND_FORMAT, + EmployeeEditCommand.MESSAGE_USAGE)); + } + try { + final int targetIndex = parseArgsAsDisplayedIndex(matcher.group("targetIndex")); + return new EmployeeEditCommand( + targetIndex, + matcher.group("phone"), + matcher.group("email"), + matcher.group("address"), + matcher.group("position") + ); + } catch (ParseException | NumberFormatException e) { + return new IncorrectCommand(String.format( + MESSAGE_INVALID_COMMAND_FORMAT, + EmployeeEditCommand.MESSAGE_USAGE)); + } catch (IllegalValueException ive) { + return new IncorrectCommand(ive.getMessage()); + } + } + + //@@author + /** + * Extracts the new menu's tags from the add command's tag arguments string. * Merges duplicate tag strings. */ - private static Set getTagsFromArgs(String tagArguments) throws IllegalValueException { + private static Set getTagsFromArgs(String tagArguments) { // no tags if (tagArguments.isEmpty()) { return Collections.emptySet(); @@ -140,56 +446,139 @@ private static Set getTagsFromArgs(String tagArguments) throws IllegalVa return new HashSet<>(tagStrings); } + //@@author SalsabilTasnia + /** + * Parses arguments in the context of the delete menu item command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareMenuDelete(String args) { + try { + final int targetIndex = parseArgsAsDisplayedIndex(args); + return new MenuDeleteCommand(targetIndex); + } catch (ParseException | NumberFormatException e) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MenuDeleteCommand.MESSAGE_USAGE)); + } + } + //@@author px1099 /** - * Parses arguments in the context of the delete person command. + * Parses arguments in the context of the delete order command. * * @param args full command args string * @return the prepared command */ - private Command prepareDelete(String args) { + private Command prepareOrderDelete(String args) { try { final int targetIndex = parseArgsAsDisplayedIndex(args); - return new DeleteCommand(targetIndex); + return new OrderDeleteCommand(targetIndex); } catch (ParseException | NumberFormatException e) { - return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + OrderDeleteCommand.MESSAGE_USAGE)); } } + //@@author SalsabilTasnia /** - * Parses arguments in the context of the view command. + * Parses arguments in the context of the find menu command. * * @param args full command args string * @return the prepared command */ - private Command prepareView(String args) { + private Command prepareMenuFind(String args) { + final Matcher matcher = KEYWORDS_ARGS_FORMAT.matcher(args.trim()); + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + MenuFindCommand.MESSAGE_USAGE)); + } + + // keywords delimited by whitespace + final String[] keywords = matcher.group("keywords").toLowerCase().split("\\s+"); + final Set keywordSet = new HashSet<>(Arrays.asList(keywords)); + return new MenuFindCommand(keywordSet); + } + //@@author px1099 + /** + * Parses arguments in the context of the edit draft order customer command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareDraftOrderEditCustomer(String args) { try { final int targetIndex = parseArgsAsDisplayedIndex(args); - return new ViewCommand(targetIndex); + return new DraftOrderEditCustomerCommand(targetIndex); } catch (ParseException | NumberFormatException e) { return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, - ViewCommand.MESSAGE_USAGE)); + DraftOrderEditCustomerCommand.MESSAGE_USAGE)); } } + //@@author px1099 /** - * Parses arguments in the context of the view all command. + * Parses arguments in the context of the edit draft order dish command. * * @param args full command args string * @return the prepared command */ - private Command prepareViewAll(String args) { + private Command prepareDraftOrderEditDish(String args) { + try { + args = args.trim(); + final Matcher matcher = DRAFT_DISH_ARGS_FORMAT.matcher(args); + // Validate arg string format + if (!matcher.matches()) { + throw new ParseException("Could not find index numbers and quantities to parse"); + } + Map indexQuantityPairs = new HashMap<>(); + int i = 0; + int index = 0; + int quantity; + for (String argument: args.split("\\s+")) { + if (!argument.isEmpty()) { + if (i % 2 == 0) { + index = Integer.parseInt(argument); + if (indexQuantityPairs.containsKey(index)) { + throw new IllegalValueException("Duplicate indexes detected"); + } + } else { + quantity = Integer.parseInt(argument.substring(2)); + indexQuantityPairs.put(index, quantity); + } + i++; + } + } + return new DraftOrderEditDishCommand(indexQuantityPairs); + } catch (ParseException | NumberFormatException e) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DraftOrderEditDishCommand.MESSAGE_INVALID_FORMAT)); + } catch (IllegalValueException ive) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DraftOrderEditDishCommand.MESSAGE_DUPLICATE_INDEX)); + } + } + //@@author kangmingtay + /** + * Parses arguments in the context of the edit draft points command. + */ + private Command prepareDraftOrderEditPoints(String args) { try { - final int targetIndex = parseArgsAsDisplayedIndex(args); - return new ViewAllCommand(targetIndex); + final Matcher matcher = REDEEM_POINTS_ARGS_FORMAT.matcher(args.trim()); + // Validate arg string format + if (!matcher.matches()) { + throw new ParseException("Could not find the points to redeem"); + } + final int points = Integer.parseInt(matcher.group("points")); + return new DraftOrderEditPointsCommand(points); } catch (ParseException | NumberFormatException e) { return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, - ViewAllCommand.MESSAGE_USAGE)); + DraftOrderEditPointsCommand.MESSAGE_USAGE)); } } + //@@author /** * Parses the given arguments string as a single index number. * @@ -199,32 +588,48 @@ private Command prepareViewAll(String args) { * @throws NumberFormatException the args string region is not a valid number */ private int parseArgsAsDisplayedIndex(String args) throws ParseException, NumberFormatException { - final Matcher matcher = PERSON_INDEX_ARGS_FORMAT.matcher(args.trim()); + final Matcher matcher = INDEX_ARGS_FORMAT.matcher(args.trim()); if (!matcher.matches()) { throw new ParseException("Could not find index number to parse"); } return Integer.parseInt(matcher.group("targetIndex")); } - + //@@author AngWM /** - * Parses arguments in the context of the find person command. + * Parses arguments in the context of the stats menu command. * * @param args full command args string * @return the prepared command */ - private Command prepareFind(String args) { - final Matcher matcher = KEYWORDS_ARGS_FORMAT.matcher(args.trim()); + private Command prepareStatsMenu(String args) { + final Matcher matcher = STATSMENU_DATE_ARGS_FORMAT.matcher(args.trim()); + // Validate arg string format if (!matcher.matches()) { - return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, - FindCommand.MESSAGE_USAGE)); + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, StatsMenuCommand.MESSAGE_USAGE)); } + return new StatsMenuCommand( + matcher.group("dateFrom"), - // keywords delimited by whitespace - final String[] keywords = matcher.group("keywords").split("\\s+"); - final Set keywordSet = new HashSet<>(Arrays.asList(keywords)); - return new FindCommand(keywordSet); + matcher.group("dateTo") + ); } + //@@author SalsabilTasnia + /** + * Parses arguments in the context of the list menu by type command. + * + * @param args full command args string + * @return the prepared command + */ + private Command prepareMenuListByType(String args) { + final Matcher matcher = ITEMWORD_ARGS_FORMAT.matcher(args.trim()); + if (!matcher.matches()) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + MenuListByTypeCommand.MESSAGE_USAGE)); + } + return new MenuListByTypeCommand(matcher.group("type")); + } -} \ No newline at end of file + //@@author +} diff --git a/src/seedu/addressbook/storage/StorageFile.java b/src/seedu/addressbook/storage/StorageFile.java index 693097a86..e1b683b72 100644 --- a/src/seedu/addressbook/storage/StorageFile.java +++ b/src/seedu/addressbook/storage/StorageFile.java @@ -1,24 +1,37 @@ package seedu.addressbook.storage; -import seedu.addressbook.data.AddressBook; -import seedu.addressbook.data.exception.IllegalValueException; -import seedu.addressbook.storage.jaxb.AdaptedAddressBook; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.nio.file.Path; +import java.nio.file.Paths; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; -import java.io.*; -import java.nio.file.Path; -import java.nio.file.Paths; + +import seedu.addressbook.data.Rms; +import seedu.addressbook.data.exception.IllegalValueException; +import seedu.addressbook.storage.jaxb.AdaptedRms; + /** - * Represents the file used to store address book data. + * Represents the file used to store Rms data. */ public class StorageFile { /** Default file path used if the user doesn't provide the file name. */ - public static final String DEFAULT_STORAGE_FILEPATH = "addressbook.txt"; + public static final String DEFAULT_STORAGE_FILEPATH = "Rms.txt"; + + public final Path path; + + private final JAXBContext jaxbContext; /* Note: Note the use of nested classes below. * More info https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html @@ -34,7 +47,7 @@ public InvalidStorageFilePathException(String message) { } /** - * Signals that some error has occured while trying to convert and read/write data between the application + * Signals that some error has occurred while trying to convert and read/write data between the application * and the storage file. */ public static class StorageOperationException extends Exception { @@ -43,10 +56,6 @@ public StorageOperationException(String message) { } } - private final JAXBContext jaxbContext; - - public final Path path; - /** * @throws InvalidStorageFilePathException if the default path is invalid */ @@ -59,7 +68,7 @@ public StorageFile() throws InvalidStorageFilePathException { */ public StorageFile(String filePath) throws InvalidStorageFilePathException { try { - jaxbContext = JAXBContext.newInstance(AdaptedAddressBook.class); + jaxbContext = JAXBContext.newInstance(AdaptedRms.class); } catch (JAXBException jaxbe) { throw new RuntimeException("jaxb initialisation error"); } @@ -83,7 +92,7 @@ private static boolean isValidPath(Path filePath) { * * @throws StorageOperationException if there were errors converting and/or storing data to file. */ - public void save(AddressBook addressBook) throws StorageOperationException { + public void save(Rms rms) throws StorageOperationException { /* Note: Note the 'try with resource' statement below. * More info: https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html @@ -91,7 +100,7 @@ public void save(AddressBook addressBook) throws StorageOperationException { try (final Writer fileWriter = new BufferedWriter(new FileWriter(path.toFile()))) { - final AdaptedAddressBook toSave = new AdaptedAddressBook(addressBook); + final AdaptedRms toSave = new AdaptedRms(rms); final Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(toSave, fileWriter); @@ -99,7 +108,7 @@ public void save(AddressBook addressBook) throws StorageOperationException { } catch (IOException ioe) { throw new StorageOperationException("Error writing to file: " + path + " error: " + ioe.getMessage()); } catch (JAXBException jaxbe) { - throw new StorageOperationException("Error converting address book into storage format"); + throw new StorageOperationException("Error converting Rms into storage format"); } } @@ -108,12 +117,12 @@ public void save(AddressBook addressBook) throws StorageOperationException { * * @throws StorageOperationException if there were errors reading and/or converting data from file. */ - public AddressBook load() throws StorageOperationException { + public Rms load() throws StorageOperationException { try (final Reader fileReader = new BufferedReader(new FileReader(path.toFile()))) { final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); - final AdaptedAddressBook loaded = (AdaptedAddressBook) unmarshaller.unmarshal(fileReader); + final AdaptedRms loaded = (AdaptedRms) unmarshaller.unmarshal(fileReader); // manual check for missing elements if (loaded.isAnyRequiredFieldMissing()) { throw new StorageOperationException("File data missing some elements"); @@ -127,7 +136,7 @@ public AddressBook load() throws StorageOperationException { // create empty file if not found } catch (FileNotFoundException fnfe) { - final AddressBook empty = new AddressBook(); + final Rms empty = new Rms(); save(empty); return empty; diff --git a/src/seedu/addressbook/storage/jaxb/AdaptedAddressBook.java b/src/seedu/addressbook/storage/jaxb/AdaptedAddressBook.java deleted file mode 100644 index 4a8297ce3..000000000 --- a/src/seedu/addressbook/storage/jaxb/AdaptedAddressBook.java +++ /dev/null @@ -1,62 +0,0 @@ -package seedu.addressbook.storage.jaxb; - -import seedu.addressbook.data.AddressBook; -import seedu.addressbook.data.exception.IllegalValueException; -import seedu.addressbook.data.person.Person; -import seedu.addressbook.data.person.UniquePersonList; - -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import java.util.ArrayList; -import java.util.List; - -/** - * JAXB-friendly adapted address book data holder class. - */ -@XmlRootElement(name = "AddressBook") -public class AdaptedAddressBook { - - @XmlElement - private List persons = new ArrayList<>(); - - /** - * No-arg constructor for JAXB use. - */ - public AdaptedAddressBook() {} - - /** - * Converts a given AddressBook into this class for JAXB use. - * - * @param source future changes to this will not affect the created AdaptedAddressBook - */ - public AdaptedAddressBook(AddressBook source) { - persons = new ArrayList<>(); - source.getAllPersons().forEach(person -> persons.add(new AdaptedPerson(person))); - } - - - /** - * Returns true if any required field is missing. - * - * JAXB does not enforce (required = true) without a given XML schema. - * Since we do most of our validation using the data class constructors, the only extra logic we need - * is to ensure that every xml element in the document is present. JAXB sets missing elements as null, - * so we check for that. - */ - public boolean isAnyRequiredFieldMissing() { - return persons.stream().anyMatch(AdaptedPerson::isAnyRequiredFieldMissing); - } - - - /** - * Converts this jaxb-friendly {@code AdaptedAddressBook} object into the corresponding(@code AddressBook} object. - * @throws IllegalValueException if there were any data constraints violated in the adapted person - */ - public AddressBook toModelType() throws IllegalValueException { - final List personList = new ArrayList<>(); - for (AdaptedPerson person : persons) { - personList.add(person.toModelType()); - } - return new AddressBook(new UniquePersonList(personList)); - } -} diff --git a/src/seedu/addressbook/storage/jaxb/AdaptedAttendance.java b/src/seedu/addressbook/storage/jaxb/AdaptedAttendance.java new file mode 100644 index 000000000..e3f0e1088 --- /dev/null +++ b/src/seedu/addressbook/storage/jaxb/AdaptedAttendance.java @@ -0,0 +1,80 @@ +package seedu.addressbook.storage.jaxb; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; + +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.Timing; + +//@@author kianhong95 +/** + * JAXB-friendly adapted tag data holder class. + */ +public class AdaptedAttendance { + + @XmlElement(required = true) + private String name; + @XmlAttribute (required = true) + private boolean isClockedIn; + + @XmlElement + private List timings = new ArrayList<>(); + + /** + * No-arg constructor for JAXB use. + */ + public AdaptedAttendance() {} + + /** + * Converts a given Attendance into this class for JAXB use. + * + * @param source future changes to this will not affect the created AdaptedAttendance + */ + public AdaptedAttendance(Attendance source) { + name = source.getName(); + isClockedIn = source.getClockedIn(); + + timings = new ArrayList<>(); + for (Timing timing : source.getTimings()) { + timings.add(new AdaptedTiming(timing)); + } + } + + /** + * Returns true if any required field is missing. + * + * JAXB does not enforce (required = true) without a given XML schema. + * Since we do most of our validation using the data class constructors, the only extra logic we need + * is to ensure that every xml element in the document is present. JAXB sets missing elements as null, + * so we check for that. + */ + public boolean isAnyRequiredFieldMissing() { + for (AdaptedTiming timing : timings) { + if (timing.isAnyRequiredFieldMissing()) { + return true; + } + } + return Utils.isAnyNull(name); + } + + /** + * Converts this jaxb-friendly adapted attendance object into the Attendance object. + */ + public Attendance toModelType() { + final String name = this.name; + final boolean isClockedIn = this.isClockedIn; + + final Set timingSet = new LinkedHashSet<>(); + for (AdaptedTiming timing : timings) { + timingSet.add(timing.toModelType()); + } + + return new Attendance(name, isClockedIn, timingSet); + } +} diff --git a/src/seedu/addressbook/storage/jaxb/AdaptedEmployee.java b/src/seedu/addressbook/storage/jaxb/AdaptedEmployee.java new file mode 100644 index 000000000..0a66edc4b --- /dev/null +++ b/src/seedu/addressbook/storage/jaxb/AdaptedEmployee.java @@ -0,0 +1,75 @@ +package seedu.addressbook.storage.jaxb; + +import javax.xml.bind.annotation.XmlElement; + +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.employee.Employee; +import seedu.addressbook.data.employee.EmployeeAddress; +import seedu.addressbook.data.employee.EmployeeEmail; +import seedu.addressbook.data.employee.EmployeeName; +import seedu.addressbook.data.employee.EmployeePhone; +import seedu.addressbook.data.employee.EmployeePosition; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.exception.IllegalValueException; + +//@@author kianhong95 +/** + * JAXB-friendly adapted employee data holder class. + */ +public class AdaptedEmployee { + + @XmlElement(required = true) + private String name; + @XmlElement(required = true) + private String phone; + @XmlElement(required = true) + private String email; + @XmlElement(required = true) + private String address; + @XmlElement(required = true) + private String position; + + /** + * No-arg constructor for JAXB use. + */ + public AdaptedEmployee() {} + + public AdaptedEmployee(ReadOnlyEmployee source) { + + name = source.getName().fullName; + + phone = source.getPhone().value; + + email = source.getEmail().value; + + address = source.getAddress().value; + + position = source.getPosition().value; + } + + /** + * Returns true if any required field is missing. + * + * JAXB does not enforce (required = true) without a given XML schema. + * Since we do most of our validation using the data class constructors, the only extra logic we need + * is to ensure that every xml element in the document is present. JAXB sets missing elements as null, + * so we check for that. + */ + public boolean isAnyRequiredFieldMissing() { + return Utils.isAnyNull(name, phone, email, address, position); + } + + /** + * Converts this jaxb-friendly adapted employee object into the Employee object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted employee + */ + public Employee toModelType() throws IllegalValueException { + final EmployeeName name = new EmployeeName(this.name); + final EmployeePhone phone = new EmployeePhone(this.phone); + final EmployeeEmail email = new EmployeeEmail(this.email); + final EmployeeAddress address = new EmployeeAddress(this.address); + final EmployeePosition position = new EmployeePosition(this.position); + return new Employee(name, phone, email, address, position); + } +} diff --git a/src/seedu/addressbook/storage/jaxb/AdaptedMember.java b/src/seedu/addressbook/storage/jaxb/AdaptedMember.java new file mode 100644 index 000000000..ae18c6fcb --- /dev/null +++ b/src/seedu/addressbook/storage/jaxb/AdaptedMember.java @@ -0,0 +1,101 @@ +package seedu.addressbook.storage.jaxb; + +import java.util.Date; + +import javax.xml.bind.annotation.XmlElement; + +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.exception.IllegalValueException; +import seedu.addressbook.data.member.Member; +import seedu.addressbook.data.member.MemberEmail; +import seedu.addressbook.data.member.MemberName; +import seedu.addressbook.data.member.MemberTier; +import seedu.addressbook.data.member.Points; +import seedu.addressbook.data.member.ReadOnlyMember; + +//@@author kangmingtay +/** + * JAXB-friendly adapted person data holder class. + */ +public class AdaptedMember { + + @XmlElement(required = true) + private String name; + + @XmlElement(required = true) + private String email; + + @XmlElement(required = true) + private int points; + + @XmlElement(required = true) + private int totalPoints; + + @XmlElement(required = true) + private long date; + + @XmlElement(required = true) + private String tier; + + /** + * No-arg constructor for JAXB use. + */ + public AdaptedMember() {} + + + /** + * Converts a given Member into this class for JAXB use. + * + * @param source future changes to this will not affect the created AdaptedMember + */ + public AdaptedMember(ReadOnlyMember source) { + name = source.getName().fullName; + email = source.getEmail().toString(); + points = source.getCurrentPointsValue(); + totalPoints = source.getTotalPointsValue(); + date = source.getDate().getTime(); + tier = source.getMemberTier().toString(); + } + + /** + * Returns true if any required field is missing. + * + * JAXB does not enforce (required = true) without a given XML schema. + * Since we do most of our validation using the data class constructors, the only extra logic we need + * is to ensure that every xml element in the document is present. JAXB sets missing elements as null, + * so we check for that. + */ + public boolean isAnyRequiredFieldMissing() { + /* + for (AdaptedTag tag : tagged) { + if (tag.isAnyRequiredFieldMissing()) { + return true; + } + } + */ + // second call only happens if phone/email/address are all not null + return Utils.isAnyNull(name); + // || Utils.isAnyNull(phone.value, email.value, address.value); + } + + /** + * Converts this jaxb-friendly adapted member object into the member object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted person + */ + public Member toModelType() throws IllegalValueException { + /* + final Set tags = new HashSet<>(); + for (AdaptedTag tag : tagged) { + tags.add(tag.toModelType()); + } + */ + final MemberName name = new MemberName(this.name); + final MemberEmail email = new MemberEmail(this.email); + final Points points = new Points(this.points, this.totalPoints); + final Date date = new Date(this.date); + final MemberTier tier = new MemberTier(this.tier); + + return new Member(name, email, points, date, tier); + } +} diff --git a/src/seedu/addressbook/storage/jaxb/AdaptedPerson.java b/src/seedu/addressbook/storage/jaxb/AdaptedMenu.java similarity index 51% rename from src/seedu/addressbook/storage/jaxb/AdaptedPerson.java rename to src/seedu/addressbook/storage/jaxb/AdaptedMenu.java index 29a46e9fd..513b2620a 100644 --- a/src/seedu/addressbook/storage/jaxb/AdaptedPerson.java +++ b/src/seedu/addressbook/storage/jaxb/AdaptedMenu.java @@ -1,67 +1,73 @@ package seedu.addressbook.storage.jaxb; -import seedu.addressbook.common.Utils; -import seedu.addressbook.data.exception.IllegalValueException; -import seedu.addressbook.data.person.*; -import seedu.addressbook.data.tag.Tag; - -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlValue; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlValue; + +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.exception.IllegalValueException; +import seedu.addressbook.data.menu.Menu; +import seedu.addressbook.data.menu.MenuName; +import seedu.addressbook.data.menu.Price; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.menu.Type; +import seedu.addressbook.data.tag.Tag; + +//@@author SalsabilTasnia /** - * JAXB-friendly adapted person data holder class. + * JAXB-friendly adapted menu data holder class. */ -public class AdaptedPerson { - - private static class AdaptedContactDetail { - @XmlValue - public String value; - @XmlAttribute(required = true) - public boolean isPrivate; - } +public class AdaptedMenu { @XmlElement(required = true) private String name; @XmlElement(required = true) - private AdaptedContactDetail phone; + private AdaptedMenuItemDetail price; @XmlElement(required = true) - private AdaptedContactDetail email; - @XmlElement(required = true) - private AdaptedContactDetail address; - + private AdaptedMenuItemDetail type; @XmlElement private List tagged = new ArrayList<>(); + /** + * JAXB-friendly adapted menu item detail data holder class. + */ + private static class AdaptedMenuItemDetail { + private String value; + + @XmlValue + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + /** * No-arg constructor for JAXB use. */ - public AdaptedPerson() {} + public AdaptedMenu() {} /** - * Converts a given Person into this class for JAXB use. + * Converts a given Menu into this class for JAXB use. * - * @param source future changes to this will not affect the created AdaptedPerson + * @param source future changes to this will not affect the created AdaptedMenu */ - public AdaptedPerson(ReadOnlyPerson source) { + public AdaptedMenu(ReadOnlyMenus source) { name = source.getName().fullName; - phone = new AdaptedContactDetail(); - phone.isPrivate = source.getPhone().isPrivate(); - phone.value = source.getPhone().value; - - email = new AdaptedContactDetail(); - email.isPrivate = source.getEmail().isPrivate(); - email.value = source.getEmail().value; + price = new AdaptedMenuItemDetail(); + //price.isPrivate = source.getPrice().isPrivate(); + price.setValue(source.getPrice().value); - address = new AdaptedContactDetail(); - address.isPrivate = source.getAddress().isPrivate(); - address.value = source.getAddress().value; + type = new AdaptedMenuItemDetail(); + type.setValue(source.getType().value); tagged = new ArrayList<>(); for (Tag tag : source.getTags()) { @@ -84,24 +90,23 @@ public boolean isAnyRequiredFieldMissing() { } } // second call only happens if phone/email/address are all not null - return Utils.isAnyNull(name, phone, email, address) - || Utils.isAnyNull(phone.value, email.value, address.value); + return Utils.isAnyNull(name, price, type) + || Utils.isAnyNull(price.getValue(), type.getValue()); } /** - * Converts this jaxb-friendly adapted person object into the Person object. + * Converts this jaxb-friendly adapted menu object into the Menu object. * - * @throws IllegalValueException if there were any data constraints violated in the adapted person + * @throws IllegalValueException if there were any data constraints violated in the adapted menu */ - public Person toModelType() throws IllegalValueException { + public Menu toModelType() throws IllegalValueException { final Set tags = new HashSet<>(); for (AdaptedTag tag : tagged) { tags.add(tag.toModelType()); } - final Name name = new Name(this.name); - final Phone phone = new Phone(this.phone.value, this.phone.isPrivate); - final Email email = new Email(this.email.value, this.email.isPrivate); - final Address address = new Address(this.address.value, this.address.isPrivate); - return new Person(name, phone, email, address, tags); + final MenuName name = new MenuName(this.name); + final Price price = new Price(this.price.getValue()/*, this.price.isPrivate*/); + final Type type = new Type(this.type.getValue()); + return new Menu(name, price, type, tags); } } diff --git a/src/seedu/addressbook/storage/jaxb/AdaptedOrder.java b/src/seedu/addressbook/storage/jaxb/AdaptedOrder.java new file mode 100644 index 000000000..8b3d54dac --- /dev/null +++ b/src/seedu/addressbook/storage/jaxb/AdaptedOrder.java @@ -0,0 +1,124 @@ +package seedu.addressbook.storage.jaxb; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.bind.annotation.XmlElement; + +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.exception.IllegalValueException; +import seedu.addressbook.data.member.Member; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.member.UniqueMemberList; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.order.Order; +import seedu.addressbook.data.order.ReadOnlyOrder; + +//@@author px1099 +/** + * JAXB-friendly adapted order data holder class. + */ +public class AdaptedOrder { + + @XmlElement(required = true) + private AdaptedMember customer; + @XmlElement(required = true) + private long date; + @XmlElement(required = true) + private double price; + @XmlElement(required = true) + private int points; + @XmlElement + private List dishItems = new ArrayList<>(); + + /** + * JAXB-friendly adapted dish item data holder class. + */ + private static class AdaptedDishItem { + private AdaptedMenu dish; + private int quantity; + + @XmlElement(name = "dish") + public AdaptedMenu getDish() { + return dish; + } + + @XmlElement(name = "quantity") + public int getQuantity() { + return quantity; + } + + public void setDish(AdaptedMenu dish) { + this.dish = dish; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + } + + /** + * No-arg constructor for JAXB use. + */ + public AdaptedOrder() {} + + /** + * Converts a given Order into this class for JAXB use. + * + * @param source future changes to this will not affect the created AdaptedOrder + */ + public AdaptedOrder(ReadOnlyOrder source) { + customer = new AdaptedMember(source.getCustomer()); + date = source.getDate().getTime(); + price = source.getPrice(); + points = source.getPoints(); + + dishItems = new ArrayList<>(); + for (Map.Entry m: source.getDishItems().entrySet()) { + AdaptedDishItem dishItem = new AdaptedDishItem(); + dishItem.setDish(new AdaptedMenu(m.getKey())); + dishItem.setQuantity(m.getValue()); + dishItems.add(dishItem); + } + } + + /** + * Returns true if any required field is missing. + * + * JAXB does not enforce (required = true) without a given XML schema. + * Since we do most of our validation using the data class constructors, the only extra logic we need + * is to ensure that every xml element in the document is present. JAXB sets missing elements as null, + * so we check for that. + */ + public boolean isAnyRequiredFieldMissing() { + for (AdaptedDishItem dishItem : dishItems) { + if (dishItem.getDish().isAnyRequiredFieldMissing() || Utils.isAnyNull(dishItem.getQuantity())) { + return true; + } + } + return customer.isAnyRequiredFieldMissing() || Utils.isAnyNull(date, price); + } + + /** + * Converts this jaxb-friendly adapted order object into the Order object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted order + */ + public Order toModelType(List memberList) throws IllegalValueException { + final Map dishItems = new HashMap<>(); + for (AdaptedDishItem dishItem : this.dishItems) { + dishItems.put(dishItem.getDish().toModelType(), dishItem.getQuantity()); + } + ReadOnlyMember customerClone = this.customer.toModelType(); + final ReadOnlyMember customer = UniqueMemberList.retrieveMember(customerClone, memberList); + final Date date = new Date(this.date); + final double price = this.price; + final int pointsToRedeem = this.points; + return new Order(customer, date, price, dishItems, pointsToRedeem); + } + + +} diff --git a/src/seedu/addressbook/storage/jaxb/AdaptedRms.java b/src/seedu/addressbook/storage/jaxb/AdaptedRms.java new file mode 100644 index 000000000..c295e03a4 --- /dev/null +++ b/src/seedu/addressbook/storage/jaxb/AdaptedRms.java @@ -0,0 +1,118 @@ +package seedu.addressbook.storage.jaxb; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import seedu.addressbook.data.Rms; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.Employee; +import seedu.addressbook.data.employee.UniqueAttendanceList; +import seedu.addressbook.data.employee.UniqueEmployeeList; +import seedu.addressbook.data.exception.IllegalValueException; +import seedu.addressbook.data.member.Member; +import seedu.addressbook.data.member.UniqueMemberList; +import seedu.addressbook.data.menu.Menu; +import seedu.addressbook.data.menu.UniqueMenuList; +import seedu.addressbook.data.order.Order; +import seedu.addressbook.data.order.UniqueOrderList; + +/** + * JAXB-friendly adapted Rms data holder class. + */ +@XmlRootElement(name = "Rms") +public class AdaptedRms { + + //@@author SalsabilTasnia + @XmlElement(name = "menus") + private List menus = new ArrayList<>(); + //@@author kangmingtay + @XmlElement(name = "members") + private List members = new ArrayList<>(); + //@@author kianhong95 + @XmlElement(name = "employees") + private List employees = new ArrayList<>(); + //@@author px1099 + @XmlElement(name = "orders") + private List orders = new ArrayList<>(); + //@@author kianhong95 + @XmlElement(name = "attendance") + private List attendances = new ArrayList<>(); + + //@@author + /** + * No-arg constructor for JAXB use. + */ + public AdaptedRms() {} + + //@@author AngWM + public AdaptedRms(Rms source) { + source.getAllMenus().forEach(menu -> menus.add(new AdaptedMenu(menu))); + source.getAllEmployees().forEach(employee -> employees.add(new AdaptedEmployee(employee))); + source.getAllMembers().forEach(member -> members.add(new AdaptedMember(member))); + source.getAllOrders().forEach(order -> orders.add(new AdaptedOrder(order))); + source.getAllAttendance().forEach(attendance -> attendances.add(new AdaptedAttendance(attendance))); + } + + //@@author px1099 + /** + * Returns true if any required field is missing. + * + * JAXB does not enforce (required = true) without a given XML schema. + * Since we do most of our validation using the data class constructors, the only extra logic we need + * is to ensure that every xml element in the document is present. JAXB sets missing elements as null, + * so we check for that. + */ + public boolean isAnyRequiredFieldMissing() { + return (menus.stream().anyMatch(AdaptedMenu::isAnyRequiredFieldMissing) + || members.stream().anyMatch(AdaptedMember::isAnyRequiredFieldMissing) + || employees.stream().anyMatch(AdaptedEmployee::isAnyRequiredFieldMissing) + || orders.stream().anyMatch(AdaptedOrder::isAnyRequiredFieldMissing) + || attendances.stream().anyMatch(AdaptedAttendance::isAnyRequiredFieldMissing)); + } + + //@@author AngWM + /** + * Converts this jaxb-friendly {@code AdaptedRms} object into the corresponding(@code Rms} object. + * @throws IllegalValueException if there were any data constraints violated in the adapted person + */ + public Rms toModelType() throws IllegalValueException { + final List menuList = new ArrayList<>(); + final List employeeList = new ArrayList<>(); + final List memberList = new ArrayList<>(); + final List orderList = new ArrayList<>(); + final List attendanceList = new ArrayList<>(); + + for (AdaptedEmployee employee : employees) { + employeeList.add(employee.toModelType()); + } + + for (AdaptedMenu menu : menus) { + menuList.add(menu.toModelType()); + } + + for (AdaptedMember member : members) { + memberList.add(member.toModelType()); + } + + for (AdaptedOrder order : orders) { + orderList.add(order.toModelType(memberList)); + } + + for (AdaptedAttendance attendance : attendances) { + attendanceList.add(attendance.toModelType()); + } + + return new Rms( + new UniqueMenuList(menuList), + new UniqueEmployeeList(employeeList), + new UniqueOrderList(orderList), + new UniqueMemberList(memberList), + new UniqueAttendanceList(attendanceList) + ); + } + + //@@author +} diff --git a/src/seedu/addressbook/storage/jaxb/AdaptedTag.java b/src/seedu/addressbook/storage/jaxb/AdaptedTag.java index cd5286a36..3f2c60bac 100644 --- a/src/seedu/addressbook/storage/jaxb/AdaptedTag.java +++ b/src/seedu/addressbook/storage/jaxb/AdaptedTag.java @@ -1,18 +1,17 @@ package seedu.addressbook.storage.jaxb; +import javax.xml.bind.annotation.XmlValue; + import seedu.addressbook.common.Utils; import seedu.addressbook.data.exception.IllegalValueException; import seedu.addressbook.data.tag.Tag; -import javax.xml.bind.annotation.XmlValue; - /** * JAXB-friendly adapted tag data holder class. */ public class AdaptedTag { - @XmlValue - public String tagName; + private String tagName; /** * No-arg constructor for JAXB use. @@ -25,7 +24,16 @@ public AdaptedTag() {} * @param source future changes to this will not affect the created AdaptedTag */ public AdaptedTag(Tag source) { - tagName = source.tagName; + setTagName(source.tagName); + } + + @XmlValue + public String getTagName() { + return tagName; + } + + public void setTagName(String tagName) { + this.tagName = tagName; } /** @@ -37,15 +45,16 @@ public AdaptedTag(Tag source) { * so we check for that. */ public boolean isAnyRequiredFieldMissing() { - return Utils.isAnyNull(tagName); + return Utils.isAnyNull(getTagName()); } /** * Converts this jaxb-friendly adapted tag object into the Tag object. * - * @throws IllegalValueException if there were any data constraints violated in the adapted person + * @throws IllegalValueException if there were any data constraints violated in the adapted tag */ public Tag toModelType() throws IllegalValueException { - return new Tag(tagName); + return new Tag(getTagName()); } + } diff --git a/src/seedu/addressbook/storage/jaxb/AdaptedTiming.java b/src/seedu/addressbook/storage/jaxb/AdaptedTiming.java new file mode 100644 index 000000000..3d334c9e3 --- /dev/null +++ b/src/seedu/addressbook/storage/jaxb/AdaptedTiming.java @@ -0,0 +1,83 @@ +package seedu.addressbook.storage.jaxb; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlValue; + +import seedu.addressbook.common.Utils; +import seedu.addressbook.data.employee.Timing; + +//@@author kianhong95 +/** + * JAXB-friendly adapted tag data holder class. + */ +public class AdaptedTiming { + + private String time; + private String date; + private boolean isClockIn; + + + /** + * No-arg constructor for JAXB use. + */ + public AdaptedTiming() {} + + /** + * Converts a given Timing into this class for JAXB use. + * + * @param source future changes to this will not affect the created AdaptedTag + */ + public AdaptedTiming(Timing source) { + setTime(source.time); + setDate(source.date); + setClockIn(source.isClockIn); + } + + @XmlValue + public String getTime() { + return time; + } + + @XmlAttribute(name = "date") + public String getDate() { + return date; + } + + @XmlAttribute(name = "isClockIn") + public boolean isClockIn() { + return isClockIn; + } + + public void setTime(String time) { + this.time = time; + } + + public void setDate(String date) { + this.date = date; + } + + public void setClockIn(boolean clockIn) { + isClockIn = clockIn; + } + + /** + * Returns true if any required field is missing. + * + * JAXB does not enforce (required = true) without a given XML schema. + * Since we do most of our validation using the data class constructors, the only extra logic we need + * is to ensure that every xml element in the document is present. JAXB sets missing elements as null, + * so we check for that. + */ + public boolean isAnyRequiredFieldMissing() { + return Utils.isAnyNull(getTime(), getDate(), isClockIn()); + } + + /** + * Converts this jaxb-friendly adapted tag object into the Tag object. + * + */ + public Timing toModelType() { + return new Timing(getTime(), getDate(), isClockIn()); + } + +} diff --git a/src/seedu/addressbook/ui/DarkTheme.css b/src/seedu/addressbook/ui/DarkTheme.css index 9e6314894..5f6f48ec3 100644 --- a/src/seedu/addressbook/ui/DarkTheme.css +++ b/src/seedu/addressbook/ui/DarkTheme.css @@ -11,7 +11,7 @@ .text-area { -fx-background-color: black; -fx-control-inner-background: black; - -fx-font-family: "Segoe UI Semibold"; + -fx-font-family: "Lucida Console"; -fx-font-size: 10pt; -fx-padding: 5 5 5 5; } diff --git a/src/seedu/addressbook/ui/Formatter.java b/src/seedu/addressbook/ui/Formatter.java index 635df3f08..80ef87c87 100644 --- a/src/seedu/addressbook/ui/Formatter.java +++ b/src/seedu/addressbook/ui/Formatter.java @@ -1,16 +1,20 @@ package seedu.addressbook.ui; -import seedu.addressbook.data.person.ReadOnlyPerson; - import java.util.ArrayList; import java.util.List; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.order.ReadOnlyOrder; + + /** * Used for formatting text for display. e.g. for adding text decorations. */ public class Formatter { - /** A decorative prefix added to the beginning of lines printed by AddressBook */ + /** A decorative prefix added to the beginning of lines printed by Rms */ private static final String LINE_PREFIX = " "; /** A platform independent line separator. */ @@ -29,24 +33,56 @@ public class Formatter { public String format(String... messages) { StringBuilder sb = new StringBuilder(); for (String m : messages) { - sb.append(LINE_PREFIX + m.replace("\n", LS + LINE_PREFIX) + LS); + sb.append(LINE_PREFIX).append(m.replace("\n", LS + LINE_PREFIX)).append(LS); } return sb.toString(); } - /** Formats the given list of persons for displaying to the user. */ - public String format(List persons) { - final List formattedPersons = new ArrayList<>(); - for (ReadOnlyPerson person : persons) { - formattedPersons.add(person.getAsTextHidePrivate()); + //@@author SalsabilTasnia + /** Formats the given list of menus for displaying to the user. */ + public String formatMenuResult(List menus) { + final List formattedMenus = new ArrayList<>(); + for (ReadOnlyMenus menu : menus) { + formattedMenus.add(menu.getAsText()); + } + return format(asIndexedList(formattedMenus)); + } + + //@@author px1099 + /** Formats the given list of orders for displaying to the user. */ + public String formatOrderResult(List orders) { + final List formattedOrders = new ArrayList<>(); + for (ReadOnlyOrder order : orders) { + formattedOrders.add(order.getAsText()); + } + return format(asIndexedList(formattedOrders)); + } + + //@@author kangmingtay + /** Formats the given list of members for displaying to the user. */ + public String formatMemberResult(List members) { + final List formattedMembers = new ArrayList<>(); + for (ReadOnlyMember member : members) { + formattedMembers.add(member.getAsText()); + } + return format(asIndexedList(formattedMembers)); + } + + //@@author kianhong95 + /** Formats the given list of employees for displaying to the user. */ + public String formatEmployeeResult(List employees) { + final List formattedOrders = new ArrayList<>(); + for (ReadOnlyEmployee employee : employees) { + formattedOrders.add(employee.getAsTextShowDetails()); } - return format(asIndexedList(formattedPersons)); + return format(asIndexedList(formattedOrders)); } + //@@author /** Formats a list of strings as an indexed list. */ private static String asIndexedList(List listItems) { final StringBuilder formatted = new StringBuilder(); - int displayIndex = 0 + DISPLAYED_INDEX_OFFSET; + int displayIndex = DISPLAYED_INDEX_OFFSET; for (String listItem : listItems) { formatted.append(getIndexedListItem(displayIndex, listItem)).append("\n"); displayIndex++; diff --git a/src/seedu/addressbook/ui/Gui.java b/src/seedu/addressbook/ui/Gui.java index ed21989a4..ca70242af 100644 --- a/src/seedu/addressbook/ui/Gui.java +++ b/src/seedu/addressbook/ui/Gui.java @@ -1,13 +1,14 @@ package seedu.addressbook.ui; +import java.io.IOException; + import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.stage.Stage; -import seedu.addressbook.logic.Logic; + import seedu.addressbook.Main; +import seedu.addressbook.logic.Logic; -import java.io.File; -import java.io.IOException; /** * The GUI of the App @@ -17,24 +18,32 @@ public class Gui { /** Offset required to convert between 1-indexing and 0-indexing. */ public static final int DISPLAYED_INDEX_OFFSET = 1; - public static final int INITIAL_WINDOW_WIDTH = 800; + public static final int INITIAL_WINDOW_WIDTH = 900; public static final int INITIAL_WINDOW_HEIGHT = 600; private final Logic logic; private MainWindow mainWindow; private String version; - public Gui(Logic logic, String version) { - this.logic = logic; + public Gui(Logic logicRms, String version) { + this.logic = logicRms; this.version = version; } + /** + * Create the main window and display the welcome message on it + */ public void start(Stage stage, Stoppable mainApp) throws IOException { + // ADD DISPLAY OF STORAGE FILE PATH mainWindow = createMainWindow(stage, mainApp); - mainWindow.displayWelcomeMessage(version, logic.getStorageFilePath()); + mainWindow.displayRmsWelcomeMessage(version, logic.getStorageFilePath()); + } - private MainWindow createMainWindow(Stage stage, Stoppable mainApp) throws IOException{ + /** + * Create the main window of the restaurant management system using javaFX + */ + private MainWindow createMainWindow(Stage stage, Stoppable mainApp) throws IOException { FXMLLoader loader = new FXMLLoader(); /* Note: When calling getResource(), use '/', instead of File.separator or '\\' @@ -45,10 +54,10 @@ private MainWindow createMainWindow(Stage stage, Stoppable mainApp) throws IOExc stage.setTitle(version); stage.setScene(new Scene(loader.load(), INITIAL_WINDOW_WIDTH, INITIAL_WINDOW_HEIGHT)); stage.show(); - MainWindow mainWindow = loader.getController(); - mainWindow.setLogic(logic); - mainWindow.setMainApp(mainApp); - return mainWindow; + MainWindow mainWindowRms = loader.getController(); + mainWindowRms.setLogic(logic); + mainWindowRms.setMainApp(mainApp); + return mainWindowRms; } } diff --git a/src/seedu/addressbook/ui/MainWindow.java b/src/seedu/addressbook/ui/MainWindow.java index 1fdde2a4f..0b4cba89a 100644 --- a/src/seedu/addressbook/ui/MainWindow.java +++ b/src/seedu/addressbook/ui/MainWindow.java @@ -1,19 +1,26 @@ package seedu.addressbook.ui; +import static seedu.addressbook.common.Messages.MESSAGE_PROGRAM_LAUNCH_ARGS_USAGE; +import static seedu.addressbook.common.Messages.MESSAGE_USING_ORDER_LIST_STORAGE_FILE; +import static seedu.addressbook.common.Messages.MESSAGE_WELCOME; + +import java.util.List; +import java.util.Optional; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; + +import seedu.addressbook.commands.CommandResult; import seedu.addressbook.commands.ExitCommand; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.order.ReadOnlyOrder; import seedu.addressbook.logic.Logic; -import seedu.addressbook.commands.CommandResult; -import seedu.addressbook.data.person.ReadOnlyPerson; -import java.util.List; -import java.util.Optional; - -import static seedu.addressbook.common.Messages.*; /** * Main Window of the GUI. @@ -23,34 +30,35 @@ public class MainWindow { private Logic logic; private Stoppable mainApp; - public MainWindow(){ - } - - public void setLogic(Logic logic){ - this.logic = logic; - } - - public void setMainApp(Stoppable mainApp){ - this.mainApp = mainApp; - } - @FXML private TextArea outputConsole; @FXML private TextField commandInput; + public void setLogic(Logic logic) { + this.logic = logic; + } + public void setMainApp(Stoppable mainApp) { + this.mainApp = mainApp; + } + + //@@author px1099 + /** + * Handle the text interface command line + * Exit the program if the exit command is given + */ @FXML - void onCommand(ActionEvent event) { + private void onCommand(ActionEvent event) { try { String userCommandText = commandInput.getText(); CommandResult result = logic.execute(userCommandText); - if(isExitCommand(result)){ + if (isExitCommand(result)) { exitApp(); return; } - displayResult(result); + displayResult(userCommandText, result); clearCommandInput(); } catch (Exception e) { display(e.getMessage()); @@ -58,6 +66,7 @@ void onCommand(ActionEvent event) { } } + //@@author private void exitApp() throws Exception { mainApp.stop(); } @@ -73,38 +82,79 @@ private void clearCommandInput() { } /** Clears the output display area */ - public void clearOutputConsole(){ + public void clearOutputConsole() { outputConsole.clear(); } + //@@author px1099 /** Displays the result of a command execution to the user. */ - public void displayResult(CommandResult result) { + public void displayResult(String input, CommandResult result) { clearOutputConsole(); - final Optional> resultPersons = result.getRelevantPersons(); - if(resultPersons.isPresent()) { - display(resultPersons.get()); + final Optional> resultMenus = result.getRelevantMenus(); + final Optional> resultOrders = result.getRelevantOrders(); + final Optional> resultMembers = result.getRelevantMember(); + final Optional> resultEmployees = result.getRelevantEmployee(); + if (resultOrders.isPresent()) { + displayOrderResult(resultOrders.get()); + } else if (resultMenus.isPresent()) { + displayMenuResult(resultMenus.get()); + } else if (resultMembers.isPresent()) { + displayMemberResult(resultMembers.get()); + } else if (resultEmployees.isPresent()) { + displayEmployeeResult(resultEmployees.get()); } - display(result.feedbackToUser); + display(String.format(Messages.MESSAGE_ENTERED_COMMAND_FORMAT, input), result.feedbackToUser); } - public void displayWelcomeMessage(String version, String storageFilePath) { - String storageFileInfo = String.format(MESSAGE_USING_STORAGE_FILE, storageFilePath); + //@@author + /** + * Display the welcome message with the version information and the storage file path + */ + public void displayRmsWelcomeMessage(String version, String storageFilePath) { + String storageFileInfo = String.format(MESSAGE_USING_ORDER_LIST_STORAGE_FILE, storageFilePath); display(MESSAGE_WELCOME, version, MESSAGE_PROGRAM_LAUNCH_ARGS_USAGE, storageFileInfo); } /** - * Displays the list of persons in the output display area, formatted as an indexed list. + * Displays the given messages on the output display area, after formatting appropriately. + */ + private void display(String... messages) { + outputConsole.setText(outputConsole.getText() + new Formatter().format(messages)); + } + + //@@author kangmingtay + /** + * Displays the list of members in the output display area, formatted as an indexed list. * Private contact details are hidden. */ - private void display(List persons) { - display(new Formatter().format(persons)); + private void displayMemberResult(List members) { + display(new Formatter().formatMemberResult(members)); } + //@@author px1099 /** - * Displays the given messages on the output display area, after formatting appropriately. + * Displays the list of orders in the output display area, formatted as an indexed list. + * Private contact details are hidden. */ - private void display(String... messages) { - outputConsole.setText(outputConsole.getText() + new Formatter().format(messages)); + private void displayOrderResult(List orders) { + display(new Formatter().formatOrderResult(orders)); + } + + //@@author SalsabilTasnia + /** + * Displays the menu list in the output display area, formatted as an indexed list. + */ + private void displayMenuResult(List menus) { + display(new Formatter().formatMenuResult(menus)); + } + + //@@author kianhong95 + /** + * Displays the employee list in the output display area, formatted as an indexed list. + */ + private void displayEmployeeResult(List employees) { + display(new Formatter().formatEmployeeResult(employees)); } + //@@author } diff --git a/src/seedu/addressbook/ui/Stoppable.java b/src/seedu/addressbook/ui/Stoppable.java index 3c330a505..4cd17a857 100644 --- a/src/seedu/addressbook/ui/Stoppable.java +++ b/src/seedu/addressbook/ui/Stoppable.java @@ -4,5 +4,5 @@ * An App that can be stopped by calling the stop() method. */ public interface Stoppable { - public void stop() throws Exception; -} \ No newline at end of file + void stop() throws Exception; +} diff --git a/test/data/StorageFileTest/ValidData.txt b/test/data/StorageFileTest/ValidData.txt index fc6b00df6..7f55ee6f7 100644 --- a/test/data/StorageFileTest/ValidData.txt +++ b/test/data/StorageFileTest/ValidData.txt @@ -1,17 +1,115 @@ - - - John Doe - 98765432 - johnd@gmail.com -
John street, block 123, #01-01
-
- - Betsy Crowe - 1234567 - betsycrowe@gmail.com -
Newgate Prison
- friend - criminal -
-
+ + + Burger + $5.00 + main + best + + + Fries + $2.00 + sides + best + + + Ang + Ang@gmail.com + 0 + 0 + 1000 + Bronze + + + Salsabil + Salsabil@gmail.com + 10000 + 10000 + 1000 + Gold + + + Tay + 11111111 + 11111111@gmail.com +
11111111 Street
+ Cashier +
+ + Lim + 22222222 + 22222222@gmail.com +
22222222 Street
+ Cashier +
+ + + Ang + Ang@gmail.com + 0 + 0 + 1000 + Bronze + + 1000 + 9.0 + 0 + + + Burger + $5.00 + main + best + + 1 + + + + Fries + $2.00 + sides + best + + 2 + + + + + Salsabil + Salsabil@gmail.com + 10000 + 10000 + 1000 + Gold + + 1000 + 8.0 + 100 + + + Burger + $5.00 + main + best + + 1 + + + + Fries + $2.00 + sides + best + + 2 + + + + Tay + 00:00 + + + Lim + 00:00 + +
\ No newline at end of file diff --git a/test/java/seedu/addressbook/common/UtilsTest.java b/test/java/seedu/addressbook/common/UtilsTest.java index 9d14530b7..6bc4eec71 100644 --- a/test/java/seedu/addressbook/common/UtilsTest.java +++ b/test/java/seedu/addressbook/common/UtilsTest.java @@ -1,10 +1,15 @@ package seedu.addressbook.common; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.util.AbstractMap; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.junit.Test; @@ -38,7 +43,7 @@ public void isAnyNull() { } @Test - public void elementsAreUnique() throws Exception { + public void elementsAreUnique() { // empty list assertAreUnique(); @@ -62,6 +67,46 @@ public void elementsAreUnique() throws Exception { assertNotUnique(null, "a", "b", null); } + //@@author AngWM + @Test + public void sortByValue() { + Map input = new HashMap<>(); + input.put("3", 5); + input.put("6", 12); + input.put("8", 51); + input.put("7", 32); + input.put("1", 2); + input.put("5", 8); + input.put("2", 3); + input.put("4", 6); + List> expected = new ArrayList<>(); + expected.add(new AbstractMap.SimpleEntry<>("1", 2)); + expected.add(new AbstractMap.SimpleEntry<>("2", 3)); + expected.add(new AbstractMap.SimpleEntry<>("3", 5)); + expected.add(new AbstractMap.SimpleEntry<>("4", 6)); + expected.add(new AbstractMap.SimpleEntry<>("5", 8)); + expected.add(new AbstractMap.SimpleEntry<>("6", 12)); + expected.add(new AbstractMap.SimpleEntry<>("7", 32)); + expected.add(new AbstractMap.SimpleEntry<>("8", 51)); + assertEquals(expected, Utils.sortByValue(input)); + } + + @Test + public void formatCurrency() { + double input = 9999.2; + String expected = "9999.20"; + assertEquals(expected, Utils.formatCurrency((input))); + + input = 0.5; + expected = "0.50"; + assertEquals(expected, Utils.formatCurrency((input))); + + input = 1; + expected = "1.00"; + assertEquals(expected, Utils.formatCurrency((input))); + } + + //@@author private void assertAreUnique(Object... objects) { assertTrue(Utils.elementsAreUnique(Arrays.asList(objects))); } diff --git a/test/java/seedu/addressbook/logic/LogicTest.java b/test/java/seedu/addressbook/logic/LogicTest.java index 396fb4bc9..5de67acbb 100644 --- a/test/java/seedu/addressbook/logic/LogicTest.java +++ b/test/java/seedu/addressbook/logic/LogicTest.java @@ -1,24 +1,73 @@ package seedu.addressbook.logic; +import static junit.framework.TestCase.assertEquals; + +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; + +import seedu.addressbook.commands.Command; import seedu.addressbook.commands.CommandResult; -import seedu.addressbook.commands.*; +import seedu.addressbook.commands.ExitCommand; +import seedu.addressbook.commands.HelpCommand; +import seedu.addressbook.commands.employee.EmployeeAddCommand; +import seedu.addressbook.commands.employee.EmployeeClockInCommand; +import seedu.addressbook.commands.employee.EmployeeClockOutCommand; +import seedu.addressbook.commands.employee.EmployeeDeleteCommand; +import seedu.addressbook.commands.employee.EmployeeEditCommand; +import seedu.addressbook.commands.member.MemberAddCommand; +import seedu.addressbook.commands.member.MemberDeleteCommand; +import seedu.addressbook.commands.menu.MenuAddCommand; +import seedu.addressbook.commands.menu.MenuDeleteCommand; +import seedu.addressbook.commands.menu.MenuFindCommand; +import seedu.addressbook.commands.menu.MenuListByTypeCommand; +import seedu.addressbook.commands.menu.MenuShowMainMenuCommand; +import seedu.addressbook.commands.order.DraftOrderClearCommand; +import seedu.addressbook.commands.order.DraftOrderConfirmCommand; +import seedu.addressbook.commands.order.DraftOrderEditCustomerCommand; +import seedu.addressbook.commands.order.DraftOrderEditDishCommand; +import seedu.addressbook.commands.order.DraftOrderEditPointsCommand; +import seedu.addressbook.commands.order.OrderAddCommand; +import seedu.addressbook.commands.order.OrderClearCommand; +import seedu.addressbook.commands.order.OrderDeleteCommand; +import seedu.addressbook.commands.statistics.StatsEmployeeCommand; +import seedu.addressbook.commands.statistics.StatsMemberCommand; +import seedu.addressbook.commands.statistics.StatsMenuCommand; +import seedu.addressbook.commands.statistics.StatsOrderCommand; import seedu.addressbook.common.Messages; -import seedu.addressbook.data.AddressBook; -import seedu.addressbook.data.person.*; +import seedu.addressbook.data.Rms; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.Employee; +import seedu.addressbook.data.employee.EmployeeEmail; +import seedu.addressbook.data.employee.EmployeeName; +import seedu.addressbook.data.employee.EmployeePhone; +import seedu.addressbook.data.employee.EmployeePosition; +import seedu.addressbook.data.employee.ReadOnlyEmployee; +import seedu.addressbook.data.employee.Timing; +import seedu.addressbook.data.member.Member; +import seedu.addressbook.data.member.MemberEmail; +import seedu.addressbook.data.member.MemberName; +import seedu.addressbook.data.member.MemberTier; +import seedu.addressbook.data.member.Points; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.menu.Menu; +import seedu.addressbook.data.menu.MenuName; +import seedu.addressbook.data.menu.Price; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.menu.Type; +import seedu.addressbook.data.order.Order; +import seedu.addressbook.data.order.ReadOnlyOrder; import seedu.addressbook.data.tag.Tag; import seedu.addressbook.storage.StorageFile; -import java.util.*; - -import static junit.framework.TestCase.assertEquals; -import static seedu.addressbook.common.Messages.*; - - public class LogicTest { /** @@ -28,15 +77,15 @@ public class LogicTest { public TemporaryFolder saveFolder = new TemporaryFolder(); private StorageFile saveFile; - private AddressBook addressBook; + private Rms rms; private Logic logic; @Before public void setup() throws Exception { saveFile = new StorageFile(saveFolder.newFile("testSaveFile.txt").getPath()); - addressBook = new AddressBook(); - saveFile.save(addressBook); - logic = new Logic(saveFile, addressBook); + rms = new Rms(); + saveFile.save(rms); + logic = new Logic(saveFile, rms); } @Test @@ -44,55 +93,265 @@ public void constructor() { //Constructor is called in the setup() method which executes before every test, no need to call it here again. //Confirm the last shown list is empty - assertEquals(Collections.emptyList(), logic.getLastShownList()); + assertEquals(Collections.emptyList(), logic.getLastShownMenuList()); + assertEquals(Collections.emptyList(), logic.getLastShownEmployeeList()); + assertEquals(Collections.emptyList(), logic.getLastShownAttendanceList()); + assertEquals(Collections.emptyList(), logic.getLastShownMemberList()); + assertEquals(Collections.emptyList(), logic.getLastShownOrderList()); } @Test public void execute_invalid() throws Exception { String invalidCommand = " "; assertCommandBehavior(invalidCommand, - String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); } /** * Executes the command and confirms that the result message is correct. - * Both the 'address book' and the 'last shown list' are expected to be empty. - * @see #assertCommandBehavior(String, String, AddressBook, boolean, List) + * Both the 'Rms' and the 'last shown list' are expected to be empty. + * @see #assertCommandBehavior(String, String, Rms) */ private void assertCommandBehavior(String inputCommand, String expectedMessage) throws Exception { - assertCommandBehavior(inputCommand, expectedMessage, AddressBook.empty(),false, Collections.emptyList()); + assertCommandBehavior(inputCommand, expectedMessage, Rms.empty()); + } + + /** + * Executes the command and confirms that the result message is correct and + * also confirms that the following three parts of the Logic object's state are as expected:
+ * - the internal Rms data are same as those in the {@code expectedRms}
+ * - the internal 'last shown list' matches the {@code expectedLastList}
+ * - the storage file content matches data in {@code expectedRms}
+ */ + + private void assertCommandBehavior(String inputCommand, String expectedMessage, Rms expectedRms) throws Exception { + + //Execute the command + CommandResult r = logic.execute(inputCommand); + + //Confirm the result contains the right data + assertEquals(expectedMessage, r.feedbackToUser); + + //Confirm the state of data is as expected + assertEquals(expectedRms, rms); + assertEquals(rms, saveFile.load()); + } + + //@@author kianhong95 + /** + * Executes the Employee command and confirms that the result message is correct. + * Both the 'rms' and the 'last shown list' are expected to be empty. + * @see #assertEmployeeCommandBehavior(String, String, Rms, boolean, List) + */ + private void assertEmployeeCommandBehavior(String inputCommand, String expectedMessage) throws Exception { + assertEmployeeCommandBehavior(inputCommand, expectedMessage, Rms.empty(), false, Collections.emptyList()); + } + + /** + * Executes the command and confirms that the result message is correct and + * also confirms that the following three parts of the Logic object's state are as expected:
+ * - the internal Rms data are same as those in the {@code expectedRms}
+ * - the internal 'last shown list' matches the {@code expectedLastList}
+ * - the storage file content matches data in {@code expectedRms}
+ */ + private void assertEmployeeCommandBehavior(String inputCommand, + String expectedMessage, + Rms expectedRms, + boolean isRelevantEmployeesExpected, + List lastShownList) throws Exception { + + //Execute the command + CommandResult r = logic.execute(inputCommand); + + //Confirm the result contains the right data + assertEquals(expectedMessage, r.feedbackToUser); + assertEquals(r.getRelevantEmployee().isPresent(), isRelevantEmployeesExpected); + if (isRelevantEmployeesExpected) { + assertEquals(lastShownList, r.getRelevantEmployee().get()); + } + + //Confirm the state of data is as expected + assertEquals(expectedRms, rms); + assertEquals(lastShownList, logic.getLastShownEmployeeList()); + assertEquals(rms, saveFile.load()); + } + + /** + * Executes the command and confirms that the result message is correct and assert + * also confirms that the following three parts of the Logic object's state are as expected:
+ * - the internal Rms data are same as those in the {@code expectedRms}
+ * - the internal 'last shown list' matches the {@code expectedLastList}
+ * - the storage file content matches data in {@code expectedRms}
+ */ + private void assertEmployeeAttendanceCommandBehavior(String inputCommand, + String expectedMessage, + Rms expectedRms, + boolean isRelevantEmployeesExpected, + boolean isRelevantAttendancesExpected, + List lastShownEmployeeList, + List lastShownAttendanceList) throws Exception { + + //Execute the command + CommandResult r = logic.execute(inputCommand); + + //Confirm the result contains the right data + assertEquals(expectedMessage, r.feedbackToUser); + assertEquals(r.getRelevantEmployee().isPresent(), isRelevantEmployeesExpected); + assertEquals(r.getRelevantAttendance().isPresent(), isRelevantAttendancesExpected); + if (isRelevantEmployeesExpected) { + assertEquals(lastShownEmployeeList, r.getRelevantEmployee().get()); + } + if (isRelevantAttendancesExpected) { + assertEquals(lastShownAttendanceList, r.getRelevantAttendance().get()); + } + + //Confirm the state of data is as expected + assertEquals(expectedRms, rms); + assertEquals(lastShownEmployeeList, logic.getLastShownEmployeeList()); + assertEquals(lastShownAttendanceList, logic.getLastShownAttendanceList()); + assertEquals(rms, saveFile.load()); + } + + //@@author kangmingtay + /** + * Executes the Member command and confirms that the result message is correct. + * Both the 'rms' and the 'last shown list' are expected to be empty. + * @see #assertMemberCommandBehavior(String, String, Rms, boolean, List) + */ + private void assertMemberCommandBehavior(String inputCommand, String expectedMessage) throws Exception { + assertMemberCommandBehavior(inputCommand, expectedMessage, Rms.empty(), false, Collections.emptyList()); } /** * Executes the command and confirms that the result message is correct and * also confirms that the following three parts of the Logic object's state are as expected:
- * - the internal address book data are same as those in the {@code expectedAddressBook}
+ * - the internal Rms data are same as those in the {@code expectedRms}
* - the internal 'last shown list' matches the {@code expectedLastList}
- * - the storage file content matches data in {@code expectedAddressBook}
+ * - the storage file content matches data in {@code expectedRms}
*/ - private void assertCommandBehavior(String inputCommand, - String expectedMessage, - AddressBook expectedAddressBook, - boolean isRelevantPersonsExpected, - List lastShownList) throws Exception { + private void assertMemberCommandBehavior(String inputCommand, + String expectedMessage, + Rms expectedRms, + boolean isRelevantMemberExpected, + List lastShownList) throws Exception { //Execute the command CommandResult r = logic.execute(inputCommand); //Confirm the result contains the right data assertEquals(expectedMessage, r.feedbackToUser); - assertEquals(r.getRelevantPersons().isPresent(), isRelevantPersonsExpected); - if(isRelevantPersonsExpected){ - assertEquals(lastShownList, r.getRelevantPersons().get()); + assertEquals(r.getRelevantMember().isPresent(), isRelevantMemberExpected); + if (isRelevantMemberExpected) { + assertEquals(lastShownList, r.getRelevantMember().get()); } //Confirm the state of data is as expected - assertEquals(expectedAddressBook, addressBook); - assertEquals(lastShownList, logic.getLastShownList()); - assertEquals(addressBook, saveFile.load()); + assertEquals(expectedRms, rms); + assertEquals(lastShownList, logic.getLastShownMemberList()); + assertEquals(rms, saveFile.load()); } + //@@author px1099 + /** + * Executes the Order command and confirms that the result message is correct. + * Both the 'rms' and the 'last shown list' are expected to be empty. + * @see #assertOrderCommandBehavior(String, String, Rms, boolean, List) + */ + private void assertOrderCommandBehavior(String inputCommand, String expectedMessage) throws Exception { + assertOrderCommandBehavior(inputCommand, expectedMessage, Rms.empty(), false, Collections.emptyList()); + } + + /** + * Executes the command and confirms that the result message is correct and + * also confirms that the following three parts of the Logic object's state are as expected:
+ * - the internal rms data are same as those in the {@code expectedRms}
+ * - the internal 'last shown list' matches the {@code expectedLastList}
+ * - the storage file content matches data in {@code expectedRms}
+ */ + private void assertOrderCommandBehavior(String inputCommand, + String expectedMessage, + Rms expectedRms, + boolean isRelevantOrdersExpected, + List lastShownList) throws Exception { + + //Execute the command + CommandResult r = logic.execute(inputCommand); + + //Confirm the result contains the right data + assertEquals(expectedMessage, r.feedbackToUser); + assertEquals(r.getRelevantOrders().isPresent(), isRelevantOrdersExpected); + if (isRelevantOrdersExpected) { + assertEquals(lastShownList, r.getRelevantOrders().get()); + } + + //Confirm the state of data is as expected + assertEquals(expectedRms, rms); + assertEquals(lastShownList, logic.getLastShownOrderList()); + assertEquals(rms, saveFile.load()); + } + + //@@author kangmingtay + /** + * Confirms the 'invalid argument index number behaviour' for the given command + * targeting a single member in the last shown list, using visible index. + * @param commandWord to test assuming it targets a single employee in the last shown list based on visible index. + */ + private void assertInvalidIndexBehaviorForMemberCommand(String commandWord) throws Exception { + String expectedMessage = Messages.MESSAGE_INVALID_MEMBER_DISPLAYED_INDEX; + TestDataHelper helper = new TestDataHelper(); + + Member m1 = helper.generateMember(1); + Member m2 = helper.generateMember(2); + List lastShownList = helper.generateMemberList(m1, m2); + + logic.setLastShownMemberList(lastShownList); + + assertMemberCommandBehavior(commandWord + " -1", expectedMessage, Rms.empty(), false, lastShownList); + assertMemberCommandBehavior(commandWord + " 0", expectedMessage, Rms.empty(), false, lastShownList); + assertMemberCommandBehavior(commandWord + " 3", expectedMessage, Rms.empty(), false, lastShownList); + } + + //@@author SalsabilTasnia + /** + * Executes the menu command and confirms that the result message is correct. + * Both the 'Rms' and the 'last shown menu list' are expected to be empty. + * @see #assertMenuCommandBehavior(String, String, Rms, boolean, List) + */ + private void assertMenuCommandBehavior(String inputCommand, String expectedMessage) throws Exception { + assertMenuCommandBehavior(inputCommand, expectedMessage, Rms.empty(), false, Collections.emptyList()); + } + /** + * Executes the menu command and confirms that the result message is correct and + * also confirms that the following three parts of the Logic object's state are as expected:
+ * - the internal Rms data are same as those in the {@code expectedRms}
+ * - the internal 'last shown menu list' matches the {@code expectedLastList}
+ * - the storage file content matches data in {@code expectedRms}
+ */ + private void assertMenuCommandBehavior(String inputCommand, + String expectedMessage, + Rms expectedRms, + boolean isRelevantMenuItemsExpected, + List lastShownMenuList) throws Exception { + + //Execute the command + CommandResult r = logic.execute(inputCommand); + + //Confirm the result contains the right data + assertEquals(expectedMessage, r.feedbackToUser); + assertEquals(r.getRelevantMenus().isPresent(), isRelevantMenuItemsExpected); + if (isRelevantMenuItemsExpected) { + assertEquals(lastShownMenuList, r.getRelevantMenus().get()); + } + + + //Confirm the state of data is as expected + assertEquals(expectedRms, rms); + assertEquals(lastShownMenuList, logic.getLastShownMenuList()); + assertEquals(rms, saveFile.load()); + } + + //@@author @Test public void execute_unknownCommandWord() throws Exception { String unknownCommand = "uicfhmowqewca"; @@ -109,107 +368,948 @@ public void execute_exit() throws Exception { assertCommandBehavior("exit", ExitCommand.MESSAGE_EXIT_ACKNOWEDGEMENT); } + //@@author kianhong95 @Test - public void execute_clear() throws Exception { + public void execute_addemp_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + EmployeeAddCommand.MESSAGE_USAGE); + assertEmployeeCommandBehavior("addemp wrong args wrong args", expectedMessage); + assertEmployeeCommandBehavior( + "addemp Valid Name 12345 e/valid@email.butNoPhonePrefix a/valid, address pos/validPos", + expectedMessage); + assertEmployeeCommandBehavior( + "addemp Valid Name p/12345 valid@email.butNoPrefix a/valid, address pos/validPos", + expectedMessage); + assertEmployeeCommandBehavior( + "addemp Valid Name p/12345 e/valid@email.butNoAddressPrefix valid, address pos/validPos", + expectedMessage); + assertEmployeeCommandBehavior( + "addemp Valid Name p/12345 e/valid@email a/butNoAddressPrefix valid, address butNoPositionPrefix", + expectedMessage); + } + + @Test + public void execute_addemp_invalidPersonData() throws Exception { + assertEmployeeCommandBehavior( + "addemp []\\[;] p/81234567 e/valid@e.mail a/valid, address pos/validPos", + EmployeeName.MESSAGE_NAME_CONSTRAINTS); + assertEmployeeCommandBehavior( + "addemp Valid Name p/not_numbers e/valid@e.mail a/valid, address pos/validPos", + EmployeePhone.MESSAGE_PHONE_CONSTRAINTS); + assertEmployeeCommandBehavior( + "addemp Valid Name p/81234567 e/notAnEmail a/valid, address pos/validPos", + EmployeeEmail.MESSAGE_EMAIL_CONSTRAINTS); + assertEmployeeCommandBehavior( + "addemp Valid Name p/81234567 e/valid@e.mail a/valid, address pos/@#%&%", + EmployeePosition.MESSAGE_POSITION_CONSTRAINTS); + + } + + @Test + public void execute_addemp_successful() throws Exception { + // setup expectations TestDataHelper helper = new TestDataHelper(); - addressBook.addPerson(helper.generatePerson(1, true)); - addressBook.addPerson(helper.generatePerson(2, true)); - addressBook.addPerson(helper.generatePerson(3, true)); + Employee toBeAdded = helper.peter(); + Attendance toBeAddedAttendance = new Attendance(toBeAdded.getName().toString()); + Rms expectedRms = new Rms(); + expectedRms.addEmployee(toBeAdded); + expectedRms.addAttendance(toBeAddedAttendance); + + // execute command and verify result + assertEmployeeCommandBehavior(helper.generateAddEmpCommand(toBeAdded), + String.format(EmployeeAddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedRms, + false, + Collections.emptyList()); - assertCommandBehavior("clear", ClearCommand.MESSAGE_SUCCESS, AddressBook.empty(), false, Collections.emptyList()); } + //@@author AngWM @Test - public void execute_add_invalidArgsFormat() throws Exception { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); - assertCommandBehavior( - "add wrong args wrong args", expectedMessage); - assertCommandBehavior( - "add Valid Name 12345 e/valid@email.butNoPhonePrefix a/valid, address", expectedMessage); - assertCommandBehavior( - "add Valid Name p/12345 valid@email.butNoPrefix a/valid, address", expectedMessage); - assertCommandBehavior( - "add Valid Name p/12345 e/valid@email.butNoAddressPrefix valid, address", expectedMessage); + public void execute_statsmenu_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, StatsMenuCommand.MESSAGE_USAGE); + assertMenuCommandBehavior( + "statsmenu InvalidDate", expectedMessage); + assertMenuCommandBehavior( + "statsmenu f/00192048 t/99022018", expectedMessage); + assertMenuCommandBehavior( + "statsmenu f/062017 t/2018", expectedMessage); } + //@@author kianhong95 @Test - public void execute_add_invalidPersonData() throws Exception { - assertCommandBehavior( - "add []\\[;] p/12345 e/valid@e.mail a/valid, address", Name.MESSAGE_NAME_CONSTRAINTS); - assertCommandBehavior( - "add Valid Name p/not_numbers e/valid@e.mail a/valid, address", Phone.MESSAGE_PHONE_CONSTRAINTS); - assertCommandBehavior( - "add Valid Name p/12345 e/notAnEmail a/valid, address", Email.MESSAGE_EMAIL_CONSTRAINTS); - assertCommandBehavior( - "add Valid Name p/12345 e/valid@e.mail a/valid, address t/invalid_-[.tag", Tag.MESSAGE_TAG_CONSTRAINTS); + public void execute_addempDuplicate_notAllowed() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Employee toBeAdded = helper.peter(); + Attendance toBeAddedAttendace = new Attendance(toBeAdded.getName().toString()); + Rms expectedRms = new Rms(); + expectedRms.addEmployee(toBeAdded); + expectedRms.addAttendance(toBeAddedAttendace); + + // setup starting state + logic.execute(helper.generateAddEmpCommand(toBeAdded)); //employee already in Rms + // execute command and verify result + assertEmployeeCommandBehavior( + helper.generateAddEmpCommand(toBeAdded), + EmployeeAddCommand.MESSAGE_DUPLICATE_EMPLOYEE, + expectedRms, + false, + Collections.emptyList()); + } + + @Test + public void execute_listemp_successful() throws Exception { + // prepare expectations + TestDataHelper helper = new TestDataHelper(); + + Employee e1 = helper.generateEmployee(1); + Employee e2 = helper.generateEmployee(2); + List lastShownList = helper.generateEmployeeList(e1, e2); + + Rms expectedRms = helper.generateRmsEmployees(lastShownList); + List expectedList = expectedRms.getAllEmployees().immutableListView(); + + // prepare Rms state + helper.addEmployeesToRms(rms, lastShownList); + + assertEmployeeCommandBehavior("listemp", + Command.getMessageForEmployeeListShownSummary(expectedList), + expectedRms, + true, + expectedList); + } + + /** + * Confirms the 'invalid argument index number behaviour' for the given command + * targeting a single employee in the last shown list, using visible index. + * @param commandWord to test assuming it targets a single employee in the last shown list based on visible index. + */ + private void assertInvalidIndexBehaviorForEmployeeDeleteCommand(String commandWord) throws Exception { + String expectedMessage = Messages.MESSAGE_INVALID_EMPLOYEE_DISPLAYED_INDEX; + TestDataHelper helper = new TestDataHelper(); + + Employee e1 = helper.generateEmployee(1); + Employee e2 = helper.generateEmployee(2); + List lastShownList = helper.generateEmployeeList(e1, e2); + + logic.setLastShownEmployeeList(lastShownList); + + assertEmployeeCommandBehavior(commandWord + " -1", expectedMessage, Rms.empty(), false, lastShownList); + assertEmployeeCommandBehavior(commandWord + " 0", expectedMessage, Rms.empty(), false, lastShownList); + assertEmployeeCommandBehavior(commandWord + " 3", expectedMessage, Rms.empty(), false, lastShownList); + } + + /** + * Confirms the 'invalid argument index number behaviour' for the given command + * targeting a single employee in the last shown list, using visible index. + * @param commandWord to test assuming it targets a single employee in the last shown list based on visible index. + */ + private void assertInvalidIndexBehaviorForEmployeeEditCommand(String commandWord) throws Exception { + String invalidFormat = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + EmployeeEditCommand.MESSAGE_USAGE); + String invalidIndexMessage = Messages.MESSAGE_INVALID_EMPLOYEE_DISPLAYED_INDEX; + TestDataHelper helper = new TestDataHelper(); + + Employee e1 = helper.generateEmployee(1); + Employee e2 = helper.generateEmployee(2); + List lastShownList = helper.generateEmployeeList(e1, e2); + String arbitraryParameter = "p/98765432"; + + logic.setLastShownEmployeeList(lastShownList); + + assertEmployeeCommandBehavior(commandWord + " -1 " + arbitraryParameter, invalidFormat, + Rms.empty(), false, lastShownList); + assertEmployeeCommandBehavior(commandWord + " 0 " + arbitraryParameter, invalidIndexMessage, + Rms.empty(), false, lastShownList); + assertEmployeeCommandBehavior(commandWord + " 3 " + arbitraryParameter, invalidIndexMessage, + Rms.empty(), false, lastShownList); } @Test - public void execute_add_successful() throws Exception { + public void execute_delemp_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + EmployeeDeleteCommand.MESSAGE_USAGE); + assertEmployeeCommandBehavior("delemp ", expectedMessage); + assertEmployeeCommandBehavior("delemp arg not number", expectedMessage); + } + + @Test + public void execute_delemp_invalidIndex() throws Exception { + assertInvalidIndexBehaviorForEmployeeDeleteCommand("delemp"); + } + + @Test + public void execute_delemp_removesCorrectEmployee() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Employee e1 = helper.generateEmployee(1); + Employee e2 = helper.generateEmployee(2); + Employee e3 = helper.generateEmployee(3); + Attendance a1 = helper.generateAttendance(1); + Attendance a2 = helper.generateAttendance(2); + Attendance a3 = helper.generateAttendance(3); + + List lastShownEmployeeList = helper.generateEmployeeList(e1, e2, e3); + List lastShownAttendanceList = helper.generateAttendanceList(a1, a2, a3); + + Rms expectedRms = helper.generateRmsEmployeesAndAttendances(lastShownEmployeeList, lastShownAttendanceList); + expectedRms.removeEmployee(e2); + expectedRms.removeAttendance(a2); + + helper.addEmployeesToRms(rms, lastShownEmployeeList); + helper.addAttendancesToRms(rms, lastShownAttendanceList); + logic.setLastShownEmployeeList(lastShownEmployeeList); + logic.setLastShownAttendanceList(lastShownAttendanceList); + + assertEmployeeAttendanceCommandBehavior("delemp 2", + String.format(EmployeeDeleteCommand.MESSAGE_DELETE_EMPLOYEE_SUCCESS, e2), + expectedRms, + false, + false, + lastShownEmployeeList, + lastShownAttendanceList); + } + + @Test + public void execute_delemp_missingInRms() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Employee e1 = helper.generateEmployee(1); + Employee e2 = helper.generateEmployee(2); + Employee e3 = helper.generateEmployee(3); + + List threeEmployees = helper.generateEmployeeList(e1, e2, e3); + + Rms expectedRms = helper.generateRmsEmployees(threeEmployees); + expectedRms.removeEmployee(e2); + + + helper.addEmployeesToRms(rms, threeEmployees); + rms.removeEmployee(e2); + logic.setLastShownEmployeeList(threeEmployees); + + assertEmployeeCommandBehavior("delemp 2", + Messages.MESSAGE_EMPLOYEE_NOT_IN_RMS, + expectedRms, + false, + threeEmployees); + } + + + @Test + public void execute_clockinEmployee_success() throws Exception { + SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy"); + SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm"); + Date date = new Date(); + String currentTime = timeFormatter.format(date); + String currentDate = dateFormatter.format(date); + + TestDataHelper helper = new TestDataHelper(); + Employee e1 = helper.generateEmployee(1); + Employee e2 = helper.generateEmployee(2); + Employee e3 = helper.generateEmployee(3); + Attendance a1 = helper.generateAttendance(1); + Attendance a2 = helper.generateAttendance(2); + Attendance a3 = helper.generateAttendance(3); + + Set expectedTimings = new LinkedHashSet<>(); + Timing currentTiming = new Timing(currentTime, currentDate, true); + expectedTimings.add(currentTiming); + Attendance expectedAttendance = helper.generateAttendanceWithTime(3, true, expectedTimings); + + List lastShownEmployeeList = helper.generateEmployeeList(e1, e2, e3); + List lastShownAttendanceList = helper.generateAttendanceList(a1, a2, a3); + + Rms expectedRms = helper.generateRmsEmployeesAndAttendances(lastShownEmployeeList, lastShownAttendanceList); + expectedRms.removeAttendance(a3); + expectedRms.addAttendance(expectedAttendance); + + helper.addEmployeesToRms(rms, lastShownEmployeeList); + helper.addAttendancesToRms(rms, lastShownAttendanceList); + logic.setLastShownEmployeeList(lastShownEmployeeList); + logic.setLastShownAttendanceList(lastShownAttendanceList); + + assertEmployeeAttendanceCommandBehavior("clockin Employee 3", + String.format(EmployeeClockInCommand.MESSAGE_SUCCESS, e3.getName(), currentDate, currentTime), + expectedRms, + false, + false, + lastShownEmployeeList, + lastShownAttendanceList); + } + + + @Test + public void execute_clockinEmployee_invalidEmployee() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Employee e1 = helper.generateEmployee(1); + Employee e2 = helper.generateEmployee(2); + Employee e3 = helper.generateEmployee(3); + Attendance a1 = helper.generateAttendance(1); + Attendance a2 = helper.generateAttendance(2); + Attendance a3 = helper.generateAttendance(3); + + List lastShownEmployeeList = helper.generateEmployeeList(e1, e2, e3); + List lastShownAttendanceList = helper.generateAttendanceList(a1, a2, a3); + + Rms expectedRms = helper.generateRmsEmployeesAndAttendances(lastShownEmployeeList, lastShownAttendanceList); + helper.addEmployeesToRms(rms, lastShownEmployeeList); + helper.addAttendancesToRms(rms, lastShownAttendanceList); + logic.setLastShownEmployeeList(lastShownEmployeeList); + logic.setLastShownAttendanceList(lastShownAttendanceList); + + assertEmployeeAttendanceCommandBehavior("clockin Employee 5", + Messages.MESSAGE_EMPLOYEE_NOT_IN_RMS, + expectedRms, + false, + false, + lastShownEmployeeList, + lastShownAttendanceList); + } + + + @Test + public void execute_clockoutEmployee_success() throws Exception { + SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy"); + SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm"); + Date date = new Date(); + String currentTime = timeFormatter.format(date); + String currentDate = dateFormatter.format(date); + + TestDataHelper helper = new TestDataHelper(); + Employee e1 = helper.generateEmployee(1); + Employee e2 = helper.generateEmployee(2); + Employee e3 = helper.generateEmployee(3); + Attendance a1 = helper.generateAttendance(1); + Attendance a2 = helper.generateAttendance(2); + Attendance a3 = helper.generateAttendance(3); + + Set expectedTimings = new LinkedHashSet<>(); + Timing clockinTiming = new Timing(currentTime, currentDate, true); + Timing clockoutTiming = new Timing(currentTime, currentDate, false); + expectedTimings.add(clockinTiming); + expectedTimings.add(clockoutTiming); + Attendance expectedAttendance = helper.generateAttendanceWithTime(3, false, expectedTimings); + + List lastShownEmployeeList = helper.generateEmployeeList(e1, e2, e3); + List lastShownAttendanceList = helper.generateAttendanceList(a1, a2, a3); + + Rms expectedRms = helper.generateRmsEmployeesAndAttendances(lastShownEmployeeList, lastShownAttendanceList); + expectedRms.removeAttendance(a3); + expectedRms.addAttendance(expectedAttendance); + + + helper.addEmployeesToRms(rms, lastShownEmployeeList); + helper.addAttendancesToRms(rms, lastShownAttendanceList); + logic.setLastShownEmployeeList(lastShownEmployeeList); + logic.setLastShownAttendanceList(lastShownAttendanceList); + logic.execute("clockin Employee 3"); + + assertEmployeeAttendanceCommandBehavior("clockout Employee 3", + String.format(EmployeeClockOutCommand.MESSAGE_SUCCESS, e3.getName(), currentDate, currentTime), + expectedRms, + false, + false, + lastShownEmployeeList, + lastShownAttendanceList); + } + + + @Test + public void execute_clockoutEmployee_invalidEmployee() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Employee e1 = helper.generateEmployee(1); + Employee e2 = helper.generateEmployee(2); + Employee e3 = helper.generateEmployee(3); + Attendance a1 = helper.generateAttendance(1); + Attendance a2 = helper.generateAttendance(2); + Attendance a3 = helper.generateAttendance(3); + + List lastShownEmployeeList = helper.generateEmployeeList(e1, e2, e3); + List lastShownAttendanceList = helper.generateAttendanceList(a1, a2, a3); + + Rms expectedRms = helper.generateRmsEmployeesAndAttendances(lastShownEmployeeList, lastShownAttendanceList); + + helper.addEmployeesToRms(rms, lastShownEmployeeList); + helper.addAttendancesToRms(rms, lastShownAttendanceList); + logic.setLastShownEmployeeList(lastShownEmployeeList); + logic.setLastShownAttendanceList(lastShownAttendanceList); + + assertEmployeeAttendanceCommandBehavior("clockout Employee 5", + Messages.MESSAGE_EMPLOYEE_NOT_IN_RMS, + expectedRms, + false, + false, + lastShownEmployeeList, + lastShownAttendanceList); + } + + @Test + public void execute_editemp_successful() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Employee e1 = helper.generateEmployee(1); + Employee e2 = helper.generateEmployee(2); + Employee e3 = helper.generateEmployee(3); + Employee editedEmployee = helper.generateEditEmployee(e2, "phone", "91234567"); + + List lastShownEmployeeList = helper.generateEmployeeList(e1, e2, e3); + + Rms expectedRms = helper.generateRmsEmployees(lastShownEmployeeList); + expectedRms.editEmployee(e2, editedEmployee); + + + helper.addEmployeesToRms(rms, lastShownEmployeeList); + logic.setLastShownEmployeeList(lastShownEmployeeList); + + + assertEmployeeCommandBehavior(helper.generateEditEmpCommand("2", "phone", "91234567"), + String.format(EmployeeEditCommand.MESSAGE_EDIT_EMPLOYEE_SUCCESS, editedEmployee), + expectedRms, + false, + lastShownEmployeeList); + + } + + @Test + public void execute_editemp_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + EmployeeEditCommand.MESSAGE_USAGE); + assertEmployeeCommandBehavior("editemp ", expectedMessage); + assertEmployeeCommandBehavior("editemp arg not number", expectedMessage); + } + + @Test + public void execute_editemp_noArgs() throws Exception { + TestDataHelper helper = new TestDataHelper(); + + Employee e1 = helper.generateEmployee(1); + Employee e2 = helper.generateEmployee(2); + Employee e3 = helper.generateEmployee(3); + List lastShownList = helper.generateEmployeeList(e1, e2, e3); + + logic.setLastShownEmployeeList(lastShownList); + + assertEmployeeCommandBehavior(helper.generateEditEmpCommand("2", null, null), + String.format(EmployeeEditCommand.MESSAGE_NOARGS, EmployeeEditCommand.MESSAGE_USAGE), + Rms.empty(), + false, + lastShownList); + } + + @Test + public void execute_editemp_invalidIndex() throws Exception { + assertInvalidIndexBehaviorForEmployeeEditCommand("editemp"); + } + + //@@author kangmingtay + @Test + public void execute_addmember_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, MemberAddCommand.MESSAGE_USAGE); + assertMemberCommandBehavior( + "addmember Valid Name p/", expectedMessage); + } + + @Test + public void execute_addmember_invalidMemberData() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, MemberAddCommand.MESSAGE_USAGE); + assertMemberCommandBehavior( + "addmember []\\[;]", expectedMessage); + } + + @Test + public void execute_addmember_invalidNameData() throws Exception { + String expectedMessage = MemberName.MESSAGE_NAME_CONSTRAINTS; + assertMemberCommandBehavior( + "addmember []; e/valid@email", expectedMessage); + } + + @Test + public void execute_addmember_invalidEmailData() throws Exception { + String expectedMessage = MemberEmail.MESSAGE_EMAIL_CONSTRAINTS; + assertMemberCommandBehavior( + "addmember Valid Name e/invalid email", expectedMessage); + } + + @Test + public void execute_addmember_successful() throws Exception { // setup expectations TestDataHelper helper = new TestDataHelper(); - Person toBeAdded = helper.adam(); - AddressBook expectedAB = new AddressBook(); - expectedAB.addPerson(toBeAdded); + Member toAdd = helper.eve(); + Rms expectedRms = new Rms(); + expectedRms.addMember(toAdd); // execute command and verify result - assertCommandBehavior(helper.generateAddCommand(toBeAdded), - String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), - expectedAB, - false, - Collections.emptyList()); + assertMemberCommandBehavior(helper.generateAddMemberCommand(toAdd), + String.format(MemberAddCommand.MESSAGE_SUCCESS, toAdd), + expectedRms, + false, + Collections.emptyList()); } @Test - public void execute_addDuplicate_notAllowed() throws Exception { + public void execute_addmemberDuplicate_notAllowed() throws Exception { // setup expectations TestDataHelper helper = new TestDataHelper(); - Person toBeAdded = helper.adam(); - AddressBook expectedAB = new AddressBook(); - expectedAB.addPerson(toBeAdded); + Member toBeAdded = helper.eve(); + Rms expectedRms = new Rms(); + expectedRms.addMember(toBeAdded); // setup starting state - addressBook.addPerson(toBeAdded); // person already in internal address book + logic.execute(helper.generateAddMemberCommand(toBeAdded)); //member already in Rms // execute command and verify result - assertCommandBehavior( - helper.generateAddCommand(toBeAdded), - AddCommand.MESSAGE_DUPLICATE_PERSON, - expectedAB, + assertMemberCommandBehavior( + helper.generateAddMemberCommand(toBeAdded), + MemberAddCommand.MESSAGE_DUPLICATE_MEMBER, + expectedRms, false, Collections.emptyList()); + } + @Test + public void execute_listmember_successful() throws Exception { + // prepare expectations + TestDataHelper helper = new TestDataHelper(); + + Member m1 = helper.generateMember(1); + Member m2 = helper.generateMember(2); + List lastShownList = helper.generateMemberList(m1, m2); + + Rms expectedRms = helper.generateRmsMember(lastShownList); + List expectedList = expectedRms.getAllMembers().immutableListView(); + + // prepare Rms state + helper.addMembersToRms(rms, lastShownList); + + assertMemberCommandBehavior("listmember", + Command.getMessageForMemberListShownSummary(expectedList), + expectedRms, + true, + expectedList); + } + + @Test + public void execute_delmember_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + MemberDeleteCommand.MESSAGE_USAGE); + assertMemberCommandBehavior("delmember ", expectedMessage); + assertMemberCommandBehavior("delmember arg not number", expectedMessage); + } + + @Test + public void execute_delmember_invalidIndex() throws Exception { + assertInvalidIndexBehaviorForMemberCommand("delmember"); + } + + @Test + public void execute_delmember_removesCorrectMember() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Member m1 = helper.generateMember(1); + Member m2 = helper.generateMember(2); + Member m3 = helper.generateMember(3); + + List threeMembers = helper.generateMemberList(m1, m2, m3); + + Rms expectedRms = helper.generateRmsMember(threeMembers); + expectedRms.removeMember(m2); + + + helper.addMembersToRms(rms, threeMembers); + logic.setLastShownMemberList(threeMembers); + + assertMemberCommandBehavior("delmember 2", + String.format(MemberDeleteCommand.MESSAGE_DELETE_MEMBER_SUCCESS, m2), + expectedRms, + false, + threeMembers); + } + + @Test + public void execute_delmember_missingInRms() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Member m1 = helper.generateMember(1); + Member m2 = helper.generateMember(2); + Member m3 = helper.generateMember(3); + + List threeMembers = helper.generateMemberList(m1, m2, m3); + + Rms expectedRms = helper.generateRmsMember(threeMembers); + expectedRms.removeMember(m2); + + + helper.addMembersToRms(rms, threeMembers); + rms.removeMember(m2); + logic.setLastShownMemberList(threeMembers); + + assertMemberCommandBehavior("delmember 2", + Messages.MESSAGE_MEMBER_NOT_IN_RMS, + expectedRms, + false, + threeMembers); } @Test - public void execute_list_showsAllPersons() throws Exception { + public void updateMemberPoints() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Points expectedPoints = new Points(500); + + Member m1 = helper.eve(); + m1.updatePoints(50, 0); + Points actualPoints = m1.getCurrentPoints(); + + assertEquals(expectedPoints.getCurrentPoints(), actualPoints.getCurrentPoints()); + + m1.updatePoints(200000000, 0); + actualPoints = m1.getCurrentPoints(); + assertEquals(expectedPoints.MAX_CURRENT_POINTS, actualPoints.getCurrentPoints()); + } + + /** + * Test to check if the member tier is being updated correctly + * @throws Exception + */ + @Test + public void testUpdatePointsAndTier() throws Exception { + TestDataHelper test = new TestDataHelper(); + Member testMember = test.eve(); + MemberTier testTier = testMember.getMemberTier(); + + testMember.updatePointsAndTier(0, 0); + String expectedTier1 = "Bronze"; + assertEquals(expectedTier1, testTier.toString()); + + testMember.updatePointsAndTier(21, 0); + String expectedTier2 = "Silver"; + assertEquals(expectedTier2, testTier.toString()); + + testMember.updatePointsAndTier(41, 0); + String expectedTier3 = "Gold"; + assertEquals(expectedTier3, testTier.toString()); + } + + //@@author SalsabilTasnia + @Test + public void execute_addmenu_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, MenuAddCommand.MESSAGE_USAGE); + assertMenuCommandBehavior( + "addmenu wrong args wrong args", expectedMessage); + assertMenuCommandBehavior( + "addmenu Valid Name $12345", expectedMessage); + assertMenuCommandBehavior( + "addmenu Valid Name p/$12345 butNoTypePrefix", expectedMessage); + } + + @Test + public void execute_addmenu_invalidMenuData() throws Exception { + assertMenuCommandBehavior( + "addmenu []\\[;] p/$12345 type/valid, type", MenuName.MESSAGE_NAME_CONSTRAINTS); + assertMenuCommandBehavior( + "addmenu Valid Name p/not_numbers type/valid, type", Price.MESSAGE_PRICE_CONSTRAINTS); + assertMenuCommandBehavior( + "addmenu Valid Name p/.99, type/main", Price.MESSAGE_PRICE_CONSTRAINTS); + assertMenuCommandBehavior( + "addmenu Valid Name p/00, type/main", Price.MESSAGE_PRICE_CONSTRAINTS); + assertMenuCommandBehavior( + "addmenu Valid Name p/$123 type/@#%&", Type.MESSAGE_TYPE_CONSTRAINTS); + assertMenuCommandBehavior( + "addmenu Valid Name p/$12345 type/valid, type t/invalid_-[.tag", Tag.MESSAGE_TAG_CONSTRAINTS); + + } + + @Test + public void execute_addmenu_successful() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Menu toBeAdded = helper.burger(); + Rms expectedRms = new Rms(); + expectedRms.addMenu(toBeAdded); + + // execute command and verify result + assertMenuCommandBehavior(helper.generateMenuAddCommand(toBeAdded), + String.format(MenuAddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedRms, + false, + Collections.emptyList()); + + } + + @Test + public void execute_addmenuDuplicate_notAllowed() throws Exception { + // setup expectations + TestDataHelper helper = new TestDataHelper(); + Menu toBeAdded = helper.burger(); + Rms expectedRms = new Rms(); + expectedRms.addMenu(toBeAdded); + + // setup starting state + rms.addMenu(toBeAdded); // menu already in internal RMS + + // execute command and verify result + assertMenuCommandBehavior( + helper.generateMenuAddCommand(toBeAdded), + MenuAddCommand.MESSAGE_DUPLICATE_MENU_ITEM, + expectedRms, + false, + Collections.emptyList()); + + } + + //test for MenuListCommand + @Test + public void execute_listmenu_showsAllMenuItems() throws Exception { // prepare expectations + Rms expectedRms = new Rms(); + List expectedMenuList = expectedRms.getAllMenus().immutableListView(); + + + assertMenuCommandBehavior("listmenu", + Command.getMessageForMenuListShownSummary(expectedMenuList), + expectedRms, + true, + expectedMenuList); + } + + @Test + public void execute_menulistByTpe_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + MenuListByTypeCommand.MESSAGE_USAGE); + assertMenuCommandBehavior("listmenutype ", expectedMessage); + } + + @Test + public void execute_menulistByType_successfulMatchesTheSpecifiedCategory() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Menu mTarget1 = helper.generateMenuWithGivenNameAndType("Cheese Burger", "main"); + Menu mTarget2 = helper.generateMenuWithGivenNameAndType("Chicken Burger", "main"); + Menu m1 = helper.generateMenuWithGivenNameAndType("Salad", "sides"); + Menu m2 = helper.generateMenuWithGivenNameAndType("Sprite", "beverage"); + + List fourMenus = helper.generateMenuList(m1, mTarget1, m2, mTarget2); + Rms expectedRms = helper.generateRmsMenu(fourMenus); + List expectedList = helper.generateMenuList(mTarget1, mTarget2); + helper.addToRmsMenu(rms, fourMenus); + assertMenuCommandBehavior("listmenutype main", + Command.getMessageForMenuListShownSummary(expectedList), + expectedRms, + true, + expectedList); + } + + @Test + public void execute_menulistByType_moreThanOneTypeSearchNotAllowed() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Menu mTarget1 = helper.generateMenuWithGivenNameAndType("Cheese Burger", "main"); + Menu mTarget2 = helper.generateMenuWithGivenNameAndType("Chicken Burger", "main"); + Menu m1 = helper.generateMenuWithGivenNameAndType("Salad", "sides"); + Menu m2 = helper.generateMenuWithGivenNameAndType("Sprite", "beverage"); + + List fourMenus = helper.generateMenuList(m1, mTarget1, m2, mTarget2); + Rms expectedRms = helper.generateRmsMenu(fourMenus); + List expectedList = helper.generateMenuList(); + helper.addToRmsMenu(rms, fourMenus); + assertMenuCommandBehavior("listmenutype main sides", + MenuListByTypeCommand.MESSAGE_ERROR, + expectedRms, + false, + expectedList); + } + + /** + * Test case to check if the argument entered is one of the following or not: + * main, sides, beverage, dessert, others, set meals + * If the arguments are not one of the following, then the argument is Invalid + */ + @Test + public void execute_menulistByType_invalidArgs() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Menu mTarget1 = helper.generateMenuWithGivenNameAndType("Cheese Burger", "main"); + Menu mTarget2 = helper.generateMenuWithGivenNameAndType("Chicken Burger", "main"); + Menu m1 = helper.generateMenuWithGivenNameAndType("Salad", "sides"); + Menu m2 = helper.generateMenuWithGivenNameAndType("Sprite", "beverage"); + + List fourMenus = helper.generateMenuList(m1, mTarget1, m2, mTarget2); + Rms expectedRms = helper.generateRmsMenu(fourMenus); + List expectedList = helper.generateMenuList(); + helper.addToRmsMenu(rms, fourMenus); + assertMenuCommandBehavior("listmenutype burger", + MenuListByTypeCommand.MESSAGE_ERROR, + expectedRms, + false, + expectedList); + } + + /** + * Confirms the 'invalid argument index number behaviour' for the given command + * targeting a single menu item in the last shown menu list, using visible index. + * @param commandWord to test + * assuming it targets a single menu item in the last shown menu list based on visible index. + */ + private void assertInvalidIndexBehaviorForMenuCommand(String commandWord) throws Exception { + String expectedMessage = Messages.MESSAGE_INVALID_MENU_ITEM_DISPLAYED_INDEX; + TestDataHelper helper = new TestDataHelper(); + + Menu e1 = helper.generateMenuItem(1); + Menu e2 = helper.generateMenuItem(2); + List lastShownMenuList = helper.generateMenuList(e1, e2); + + logic.setLastShownMenuList(lastShownMenuList); + + assertMenuCommandBehavior(commandWord + " -1", expectedMessage, Rms.empty(), false, lastShownMenuList); + assertMenuCommandBehavior(commandWord + " 0", expectedMessage, Rms.empty(), false, lastShownMenuList); + assertMenuCommandBehavior(commandWord + " 3", expectedMessage, Rms.empty(), false, lastShownMenuList); + + } + + @Test + public void execute_deletemenu_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + MenuDeleteCommand.MESSAGE_USAGE); + assertMenuCommandBehavior("delmenu ", expectedMessage); + assertMenuCommandBehavior("delmenu arg not number", expectedMessage); + } + + @Test + public void execute_deletemenu_invalidIndex() throws Exception { + assertInvalidIndexBehaviorForMenuCommand("delmenu"); + } + + @Test + public void execute_deletemenu_removesCorrectMenu() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Menu m1 = helper.generateMenuItem(1); + Menu m2 = helper.generateMenuItem(2); + Menu m3 = helper.generateMenuItem(3); + List threeMenus = helper.generateMenuList(m1, m2, m3); + Rms expectedRms = helper.generateRmsMenu(threeMenus); + expectedRms.removeMenuItem(m2); + helper.addToRmsMenu(rms, threeMenus); + logic.setLastShownMenuList(threeMenus); + assertMenuCommandBehavior("delmenu 2", + String.format(MenuDeleteCommand.MESSAGE_DELETE_MENU_ITEM_SUCCESS, m2), + expectedRms, + false, + threeMenus); + } + + @Test + public void execute_deletemenu_missingInRms() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Menu m1 = helper.generateMenuItem(1); + Menu m2 = helper.generateMenuItem(2); + Menu m3 = helper.generateMenuItem(3); + List threeMenus = helper.generateMenuList(m1, m2, m3); + Rms expectedRms = helper.generateRmsMenu(threeMenus); + expectedRms.removeMenuItem(m2); + helper.addToRmsMenu(rms, threeMenus); + rms.removeMenuItem(m2); + logic.setLastShownMenuList(threeMenus); + assertMenuCommandBehavior("delmenu 2", + Messages.MESSAGE_MENU_ITEM_NOT_IN_ADDRESSBOOK, + expectedRms, + false, + threeMenus); + } + + @Test + public void execute_findmenu_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, MenuFindCommand.MESSAGE_USAGE); + assertMenuCommandBehavior("findmenu ", expectedMessage); + } + + @Test + public void execute_findmenu_onlyMatchesFullWordsInMenuItems() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Menu mTarget1 = helper.generateMenuWithName("Double Cheese Burger"); + Menu mTarget2 = helper.generateMenuWithName("Mac and Cheese"); + Menu m1 = helper.generateMenuWithName("cheeeeseeeeeee"); + Menu m2 = helper.generateMenuWithName("che ese"); + List fourMenus = helper.generateMenuList(m1, mTarget1, m2, mTarget2); + Rms expectedRms = helper.generateRmsMenu(fourMenus); + List expectedList = helper.generateMenuList(mTarget1, mTarget2); + helper.addToRmsMenu(rms, fourMenus); + assertMenuCommandBehavior("findmenu Cheese", + Command.getMessageForMenuListShownSummary(expectedList), + expectedRms, + true, + expectedList); + } + + @Test + public void execute_findmenu_isInCaseSensitive() throws Exception { TestDataHelper helper = new TestDataHelper(); - AddressBook expectedAB = helper.generateAddressBook(false, true); - List expectedList = expectedAB.getAllPersons().immutableListView(); + Menu mTarget1 = helper.generateMenuWithName("bla bla KEY bla"); + Menu mTarget2 = helper.generateMenuWithName("bla KeY bla bceofeia"); + Menu mTarget3 = helper.generateMenuWithName("key key"); + Menu m2 = helper.generateMenuWithName("sduauo"); + + List fourMenus = helper.generateMenuList(mTarget1, m2, mTarget2, mTarget3); + Rms expectedRms = helper.generateRmsMenu(fourMenus); + List expectedList = helper.generateMenuList(mTarget1, mTarget2, mTarget3); + helper.addToRmsMenu(rms, fourMenus); + + assertMenuCommandBehavior("findmenu KEY", + Command.getMessageForMenuListShownSummary(expectedList), + expectedRms, + true, + expectedList); + } - // prepare address book state - helper.addToAddressBook(addressBook, false, true); + @Test + public void execute_findmenu_matchesIfAnyKeywordPresent() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Menu mTarget1 = helper.generateMenuWithName("Cheese Taco"); + Menu mTarget2 = helper.generateMenuWithName("Cheese Burger"); + Menu m1 = helper.generateMenuWithName("Cheessse wrap"); + Menu m2 = helper.generateMenuWithName("Grilled cheeeese sandwiches"); + List fourMenus = helper.generateMenuList(m1, mTarget1, m2, mTarget2); + Rms expectedRms = helper.generateRmsMenu(fourMenus); + List expectedList = helper.generateMenuList(mTarget1, mTarget2); + helper.addToRmsMenu(rms, fourMenus); + assertMenuCommandBehavior("findmenu Cheese Taco", + Command.getMessageForMenuListShownSummary(expectedList), + expectedRms, + true, + expectedList); + } - assertCommandBehavior("list", - Command.getMessageForPersonListShownSummary(expectedList), - expectedAB, - true, - expectedList); + @Test + public void execute_showMainMenu_validArgsFormat() throws Exception { + String expectedMessage = MenuShowMainMenuCommand.MAIN_MENU_DISPLAY; + assertMenuCommandBehavior("showmainmenu", expectedMessage); } + //@@author px1099 @Test - public void execute_view_invalidArgsFormat() throws Exception { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE); - assertCommandBehavior("view ", expectedMessage); - assertCommandBehavior("view arg not number", expectedMessage); + public void execute_clearorder() throws Exception { + TestDataHelper helper = new TestDataHelper(); + rms.addOrder(helper.generateOrder(1)); + rms.addOrder(helper.generateOrder(2)); + rms.addOrder(helper.generateOrder(3)); + + assertOrderCommandBehavior( + "clearorder", + OrderClearCommand.MESSAGE_SUCCESS, + Rms.empty(), + false, + Collections.emptyList()); } @Test - public void execute_view_invalidIndex() throws Exception { - assertInvalidIndexBehaviorForCommand("view"); + public void execute_listorder_showsAllOrders() throws Exception { + // prepare expectations + TestDataHelper helper = new TestDataHelper(); + Rms expectedRms = helper.generateRmsOrder(1, 2, 3, 4); + List expectedList = expectedRms.getAllOrders().immutableListView(); + + // prepare Rms state + helper.addOrdersToRms(rms, 1, 2, 3, 4); + + assertOrderCommandBehavior("listorder", + Command.getMessageForOrderListShownSummary(expectedList), + expectedRms, + true, + expectedList); } /** @@ -217,374 +1317,487 @@ public void execute_view_invalidIndex() throws Exception { * targeting a single person in the last shown list, using visible index. * @param commandWord to test assuming it targets a single person in the last shown list based on visible index. */ - private void assertInvalidIndexBehaviorForCommand(String commandWord) throws Exception { - String expectedMessage = Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; + private void assertInvalidIndexBehaviorForOrderCommand(String commandWord) throws Exception { + String expectedMessage = Messages.MESSAGE_INVALID_ORDER_DISPLAYED_INDEX; TestDataHelper helper = new TestDataHelper(); - List lastShownList = helper.generatePersonList(false, true); + List lastShownList = helper.generateOrderList(1, 2); + + logic.setLastShownOrderList(lastShownList); + + assertOrderCommandBehavior(commandWord + " -1", expectedMessage, Rms.empty(), false, lastShownList); + assertOrderCommandBehavior(commandWord + " 0", expectedMessage, Rms.empty(), false, lastShownList); + assertOrderCommandBehavior(commandWord + " 3", expectedMessage, Rms.empty(), false, lastShownList); - logic.setLastShownList(lastShownList); + } - assertCommandBehavior(commandWord + " -1", expectedMessage, AddressBook.empty(), false, lastShownList); - assertCommandBehavior(commandWord + " 0", expectedMessage, AddressBook.empty(), false, lastShownList); - assertCommandBehavior(commandWord + " 3", expectedMessage, AddressBook.empty(), false, lastShownList); + private String generateDraftOrderAsString(ReadOnlyOrder draft) { + return String.format(Messages.MESSAGE_DRAFT_ORDER_DETAILS, draft.getDraftDetailsAsText()); + } + @Test + public void execute_deleteorder_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + OrderDeleteCommand.MESSAGE_USAGE); + assertOrderCommandBehavior("deleteorder ", expectedMessage); + assertOrderCommandBehavior("deleteorder arg not number", expectedMessage); } @Test - public void execute_view_onlyShowsNonPrivate() throws Exception { + public void execute_deleteorder_invalidIndex() throws Exception { + assertInvalidIndexBehaviorForOrderCommand("deleteorder"); + } + @Test + public void execute_deleteorder_removesCorrectOrder() throws Exception { TestDataHelper helper = new TestDataHelper(); - Person p1 = helper.generatePerson(1, true); - Person p2 = helper.generatePerson(2, false); - List lastShownList = helper.generatePersonList(p1, p2); - AddressBook expectedAB = helper.generateAddressBook(lastShownList); - helper.addToAddressBook(addressBook, lastShownList); + Order o1 = helper.generateOrder(1); + Order o2 = helper.generateOrder(2); + Order o3 = helper.generateOrder(3); + + List threeOrders = helper.generateOrderList(o1, o2, o3); - logic.setLastShownList(lastShownList); + Rms expectedRms = helper.generateRmsOrder(threeOrders); + expectedRms.removeOrder(o2); - assertCommandBehavior("view 1", - String.format(ViewCommand.MESSAGE_VIEW_PERSON_DETAILS, p1.getAsTextHidePrivate()), - expectedAB, - false, - lastShownList); - assertCommandBehavior("view 2", - String.format(ViewCommand.MESSAGE_VIEW_PERSON_DETAILS, p2.getAsTextHidePrivate()), - expectedAB, - false, - lastShownList); + helper.addOrdersToRms(rms, threeOrders); + logic.setLastShownOrderList(threeOrders); + + assertOrderCommandBehavior("deleteorder 2", + String.format(OrderDeleteCommand.MESSAGE_DELETE_ORDER_SUCCESS, o2), + expectedRms, + false, + threeOrders); } @Test - public void execute_tryToViewMissingPerson_errorMessage() throws Exception { + public void execute_deleteorder_missingInRms() throws Exception { TestDataHelper helper = new TestDataHelper(); - Person p1 = helper.generatePerson(1, false); - Person p2 = helper.generatePerson(2, false); - List lastShownList = helper.generatePersonList(p1, p2); + Order o1 = helper.generateOrder(1); + Order o2 = helper.generateOrder(2); + Order o3 = helper.generateOrder(3); - AddressBook expectedAB = new AddressBook(); - expectedAB.addPerson(p2); + List threeOrders = helper.generateOrderList(o1, o2, o3); - addressBook.addPerson(p2); - logic.setLastShownList(lastShownList); + Rms expectedRms = helper.generateRmsOrder(threeOrders); + expectedRms.removeOrder(o2); - assertCommandBehavior("view 1", - Messages.MESSAGE_PERSON_NOT_IN_ADDRESSBOOK, - expectedAB, - false, - lastShownList); + helper.addOrdersToRms(rms, threeOrders); + rms.removeOrder(o2); + logic.setLastShownOrderList(threeOrders); + + assertOrderCommandBehavior("deleteorder 2", + Messages.MESSAGE_ORDER_NOT_IN_ORDER_LIST, + expectedRms, + false, + threeOrders); } @Test - public void execute_viewAll_invalidArgsFormat() throws Exception { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewAllCommand.MESSAGE_USAGE); - assertCommandBehavior("viewall ", expectedMessage); - assertCommandBehavior("viewall arg not number", expectedMessage); + public void execute_draftcustomer_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + DraftOrderEditCustomerCommand.MESSAGE_USAGE); + assertOrderCommandBehavior("draftcustomer ", expectedMessage); + assertOrderCommandBehavior("draftcustomer arg not number", expectedMessage); } @Test - public void execute_viewAll_invalidIndex() throws Exception { - assertInvalidIndexBehaviorForCommand("viewall"); + public void execute_draftcustomer_invalidIndex() throws Exception { + assertInvalidIndexBehaviorForMemberCommand("draftcustomer"); } @Test - public void execute_viewAll_alsoShowsPrivate() throws Exception { + public void execute_draftcustomer_retrievesCorrectMember() throws Exception { TestDataHelper helper = new TestDataHelper(); - Person p1 = helper.generatePerson(1, true); - Person p2 = helper.generatePerson(2, false); - List lastShownList = helper.generatePersonList(p1, p2); - AddressBook expectedAB = helper.generateAddressBook(lastShownList); - helper.addToAddressBook(addressBook, lastShownList); + Member m1 = helper.generateMember(1); + Member m2 = helper.eve(); + Member m3 = helper.generateMember(3); + + List threeMembers = helper.generateMemberList(m1, m2, m3); + + Rms expectedRms = helper.generateRmsMember(threeMembers); + Order expectedDraftOrder = helper.foodOrderWithoutDishes(); - logic.setLastShownList(lastShownList); + helper.addMembersToRms(rms, threeMembers); + logic.setLastShownMemberList(threeMembers); - assertCommandBehavior("viewall 1", - String.format(ViewCommand.MESSAGE_VIEW_PERSON_DETAILS, p1.getAsTextShowAll()), - expectedAB, - false, - lastShownList); + String expectedMessage = String.format(DraftOrderEditCustomerCommand.MESSAGE_SUCCESS, + generateDraftOrderAsString(expectedDraftOrder)); - assertCommandBehavior("viewall 2", - String.format(ViewCommand.MESSAGE_VIEW_PERSON_DETAILS, p2.getAsTextShowAll()), - expectedAB, - false, - lastShownList); + assertMemberCommandBehavior("draftcustomer 2", + expectedMessage, + expectedRms, + false, + threeMembers); } @Test - public void execute_tryToViewAllPersonMissingInAddressBook_errorMessage() throws Exception { + public void execute_draftcustomer_missingInRms() throws Exception { TestDataHelper helper = new TestDataHelper(); - Person p1 = helper.generatePerson(1, false); - Person p2 = helper.generatePerson(2, false); - List lastShownList = helper.generatePersonList(p1, p2); + Member m1 = helper.generateMember(1); + Member m2 = helper.generateMember(2); + Member m3 = helper.generateMember(3); + + List threeMembers = helper.generateMemberList(m1, m2, m3); - AddressBook expectedAB = new AddressBook(); - expectedAB.addPerson(p1); + Rms expectedRms = helper.generateRmsMember(threeMembers); + expectedRms.removeMember(m2); - addressBook.addPerson(p1); - logic.setLastShownList(lastShownList); + helper.addMembersToRms(rms, threeMembers); + rms.removeMember(m2); + logic.setLastShownMemberList(threeMembers); - assertCommandBehavior("viewall 2", - Messages.MESSAGE_PERSON_NOT_IN_ADDRESSBOOK, - expectedAB, - false, - lastShownList); + assertMemberCommandBehavior("draftcustomer 2", + Messages.MESSAGE_MEMBER_NOT_IN_RMS, + expectedRms, + false, + threeMembers); } @Test - public void execute_delete_invalidArgsFormat() throws Exception { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE); - assertCommandBehavior("delete ", expectedMessage); - assertCommandBehavior("delete arg not number", expectedMessage); + public void execute_draftdish_invalidArgsFormat() throws Exception { + String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + DraftOrderEditDishCommand.MESSAGE_INVALID_FORMAT); + assertOrderCommandBehavior("draftdish", expectedMessage); + assertOrderCommandBehavior("draftdish ", expectedMessage); + assertOrderCommandBehavior("draftdish wrong args wrong args", expectedMessage); + assertOrderCommandBehavior("draftdish 1", expectedMessage); + assertOrderCommandBehavior("draftdish 1 2", expectedMessage); + assertOrderCommandBehavior("draftdish 1 q/", expectedMessage); + assertOrderCommandBehavior("draftdish q/2", expectedMessage); + assertOrderCommandBehavior("draftdish a q/2", expectedMessage); + assertOrderCommandBehavior("draftdish 1 q/b", expectedMessage); + assertOrderCommandBehavior("draftdish -1 q/2", expectedMessage); + assertOrderCommandBehavior("draftdish 1 q/-2", expectedMessage); + assertOrderCommandBehavior("draftdish 1 q/1000", expectedMessage); } @Test - public void execute_delete_invalidIndex() throws Exception { - assertInvalidIndexBehaviorForCommand("delete"); + public void execute_draftdish_invalidIndex() throws Exception { + String expectedMessage = Messages.MESSAGE_INVALID_MENU_ITEM_DISPLAYED_INDEX; + TestDataHelper helper = new TestDataHelper(); + Menu m1 = helper.generateMenuItem(1); + Menu m2 = helper.generateMenuItem(2); + List lastShownMenuList = helper.generateMenuList(m1, m2); + + logic.setLastShownMenuList(lastShownMenuList); + + assertMenuCommandBehavior("draftdish" + " 0 " + "q/1", expectedMessage, Rms.empty(), false, lastShownMenuList); + assertMenuCommandBehavior("draftdish" + " 3 " + "q/1", expectedMessage, Rms.empty(), false, lastShownMenuList); } @Test - public void execute_delete_removesCorrectPerson() throws Exception { + public void execute_draftdish_retrievesCorrectMenuItem() throws Exception { TestDataHelper helper = new TestDataHelper(); - Person p1 = helper.generatePerson(1, false); - Person p2 = helper.generatePerson(2, true); - Person p3 = helper.generatePerson(3, true); + Menu m1 = helper.generateMenuItem(1); + Menu m2 = helper.burger(); + Menu m3 = helper.generateMenuItem(3); - List threePersons = helper.generatePersonList(p1, p2, p3); + List threeMenus = helper.generateMenuList(m1, m2, m3); - AddressBook expectedAB = helper.generateAddressBook(threePersons); - expectedAB.removePerson(p2); + Rms expectedRms = helper.generateRmsMenu(threeMenus); + Order expectedDraftOrder = helper.foodOrderWithoutCustomer(); + helper.addToRmsMenu(rms, threeMenus); + logic.setLastShownMenuList(threeMenus); - helper.addToAddressBook(addressBook, threePersons); - logic.setLastShownList(threePersons); + String expectedMessage = String.format(DraftOrderEditDishCommand.MESSAGE_SUCCESS, + generateDraftOrderAsString(expectedDraftOrder)); - assertCommandBehavior("delete 2", - String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, p2), - expectedAB, - false, - threePersons); + assertMenuCommandBehavior("draftdish 2 q/" + TestDataHelper.FOOD_QUANTITY, + expectedMessage, + expectedRms, + false, + threeMenus); } @Test - public void execute_delete_missingInAddressBook() throws Exception { + public void execute_draftdish_missingInRms() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Menu m1 = helper.generateMenuItem(1); + Menu m2 = helper.generateMenuItem(2); + Menu m3 = helper.generateMenuItem(3); + + List threeMenus = helper.generateMenuList(m1, m2, m3); + + Rms expectedRms = helper.generateRmsMenu(threeMenus); + expectedRms.removeMenuItem(m2); + + helper.addToRmsMenu(rms, threeMenus); + rms.removeMenuItem(m2); + logic.setLastShownMenuList(threeMenus); + + assertMenuCommandBehavior("draftdish 2 q/1", + Messages.MESSAGE_MENU_ITEM_NOT_IN_ADDRESSBOOK, + expectedRms, + false, + threeMenus); + } + @Test + public void execute_cleardraft() throws Exception { TestDataHelper helper = new TestDataHelper(); - Person p1 = helper.generatePerson(1, false); - Person p2 = helper.generatePerson(2, true); - Person p3 = helper.generatePerson(3, true); - List threePersons = helper.generatePersonList(p1, p2, p3); + Order expectedDraftOrder = new Order(); + rms.editDraftOrderCustomer(helper.eve()); + rms.editDraftOrderDishItem(helper.burger(), TestDataHelper.FOOD_QUANTITY); + + String expectedMessage = String.format(DraftOrderClearCommand.MESSAGE_SUCCESS, + generateDraftOrderAsString(expectedDraftOrder)); - AddressBook expectedAB = helper.generateAddressBook(threePersons); - expectedAB.removePerson(p2); + assertOrderCommandBehavior("cleardraft", expectedMessage); + } + + @Test + public void execute_addorder() throws Exception { + TestDataHelper helper = new TestDataHelper(); - helper.addToAddressBook(addressBook, threePersons); - addressBook.removePerson(p2); - logic.setLastShownList(threePersons); + Order expectedDraftOrder = helper.foodOrder(); + rms.editDraftOrderCustomer(helper.eve()); + rms.editDraftOrderDishItem(helper.burger(), TestDataHelper.FOOD_QUANTITY); - assertCommandBehavior("delete 2", - Messages.MESSAGE_PERSON_NOT_IN_ADDRESSBOOK, - expectedAB, - false, - threePersons); + String expectedMessage = generateDraftOrderAsString(expectedDraftOrder) + + "\n\n" + OrderAddCommand.MESSAGE_ADD_ORDER_INSTRUCTION + + "\n\n" + OrderAddCommand.MESSAGE_ALL_ORDER_DRAFT_COMMANDS + + "\n\n" + OrderAddCommand.MESSAGE_ALL_ORDER_DRAFT_COMMANDS_USAGES; + + assertOrderCommandBehavior("addorder", expectedMessage); } @Test - public void execute_find_invalidArgsFormat() throws Exception { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE); - assertCommandBehavior("find ", expectedMessage); + public void execute_addorder_missingCustomer() throws Exception { + TestDataHelper helper = new TestDataHelper(); + + Order expectedDraftOrder = helper.foodOrderWithoutCustomer(); + rms.editDraftOrderDishItem(helper.burger(), TestDataHelper.FOOD_QUANTITY); + + String expectedMessage = generateDraftOrderAsString(expectedDraftOrder) + + "\n\n" + OrderAddCommand.MESSAGE_ADD_ORDER_INSTRUCTION + + "\n\n" + OrderAddCommand.MESSAGE_ALL_ORDER_DRAFT_COMMANDS + + "\n\n" + OrderAddCommand.MESSAGE_ALL_ORDER_DRAFT_COMMANDS_USAGES; + + assertOrderCommandBehavior("addorder", expectedMessage); } @Test - public void execute_find_onlyMatchesFullWordsInNames() throws Exception { + public void execute_addorder_missingDishes() throws Exception { TestDataHelper helper = new TestDataHelper(); - Person pTarget1 = helper.generatePersonWithName("bla bla KEY bla"); - Person pTarget2 = helper.generatePersonWithName("bla KEY bla bceofeia"); - Person p1 = helper.generatePersonWithName("KE Y"); - Person p2 = helper.generatePersonWithName("KEYKEYKEY sduauo"); - List fourPersons = helper.generatePersonList(p1, pTarget1, p2, pTarget2); - AddressBook expectedAB = helper.generateAddressBook(fourPersons); - List expectedList = helper.generatePersonList(pTarget1, pTarget2); - helper.addToAddressBook(addressBook, fourPersons); + Order expectedDraftOrder = helper.foodOrderWithoutDishes(); + rms.editDraftOrderCustomer(helper.eve()); + + String expectedMessage = generateDraftOrderAsString(expectedDraftOrder) + + "\n\n" + OrderAddCommand.MESSAGE_ADD_ORDER_INSTRUCTION + + "\n\n" + OrderAddCommand.MESSAGE_ALL_ORDER_DRAFT_COMMANDS + + "\n\n" + OrderAddCommand.MESSAGE_ALL_ORDER_DRAFT_COMMANDS_USAGES; - assertCommandBehavior("find KEY", - Command.getMessageForPersonListShownSummary(expectedList), - expectedAB, - true, - expectedList); + assertOrderCommandBehavior("addorder", expectedMessage); } @Test - public void execute_find_isCaseSensitive() throws Exception { + public void execute_addorder_missingCustomerAndDishes() throws Exception { + Order expectedDraftOrder = new Order(); + + String expectedMessage = generateDraftOrderAsString(expectedDraftOrder) + + "\n\n" + OrderAddCommand.MESSAGE_ADD_ORDER_INSTRUCTION + + "\n\n" + OrderAddCommand.MESSAGE_ALL_ORDER_DRAFT_COMMANDS + + "\n\n" + OrderAddCommand.MESSAGE_ALL_ORDER_DRAFT_COMMANDS_USAGES; + + assertOrderCommandBehavior("addorder", expectedMessage); + } + + @Test + public void execute_confirmorder_missingDishes() throws Exception { TestDataHelper helper = new TestDataHelper(); - Person pTarget1 = helper.generatePersonWithName("bla bla KEY bla"); - Person pTarget2 = helper.generatePersonWithName("bla KEY bla bceofeia"); - Person p1 = helper.generatePersonWithName("key key"); - Person p2 = helper.generatePersonWithName("KEy sduauo"); - List fourPersons = helper.generatePersonList(p1, pTarget1, p2, pTarget2); - AddressBook expectedAB = helper.generateAddressBook(fourPersons); - List expectedList = helper.generatePersonList(pTarget1, pTarget2); - helper.addToAddressBook(addressBook, fourPersons); + Order expectedDraftOrder = helper.foodOrderWithoutDishes(); + rms.editDraftOrderCustomer(helper.eve()); + + String expectedMessage = String.format(DraftOrderConfirmCommand.MESSAGE_DRAFT_INCOMPLETE, + generateDraftOrderAsString(expectedDraftOrder)); - assertCommandBehavior("find KEY", - Command.getMessageForPersonListShownSummary(expectedList), - expectedAB, - true, - expectedList); + assertOrderCommandBehavior("confirmdraft", expectedMessage); } @Test - public void execute_find_matchesIfAnyKeywordPresent() throws Exception { + public void execute_draftpoints_success() throws Exception { TestDataHelper helper = new TestDataHelper(); - Person pTarget1 = helper.generatePersonWithName("bla bla KEY bla"); - Person pTarget2 = helper.generatePersonWithName("bla rAnDoM bla bceofeia"); - Person p1 = helper.generatePersonWithName("key key"); - Person p2 = helper.generatePersonWithName("KEy sduauo"); - List fourPersons = helper.generatePersonList(p1, pTarget1, p2, pTarget2); - AddressBook expectedAB = helper.generateAddressBook(fourPersons); - List expectedList = helper.generatePersonList(pTarget1, pTarget2); - helper.addToAddressBook(addressBook, fourPersons); + Order expectedDraftOrder = helper.foodOrderWithReturningCustomer(); + rms.editDraftOrderCustomer(helper.david()); + rms.editDraftOrderDishItem(helper.burger(), helper.FOOD_QUANTITY); + rms.editDraftOrderPoints(helper.pointsToRedeem()); - assertCommandBehavior("find KEY rAnDoM", - Command.getMessageForPersonListShownSummary(expectedList), - expectedAB, - true, - expectedList); + String expectedMessage = String.format(DraftOrderEditPointsCommand.MESSAGE_SUCCESS, + generateDraftOrderAsString(expectedDraftOrder)); + + assertOrderCommandBehavior("draftpoints 0", expectedMessage); } - /** - * A utility class to generate test data. - */ - class TestDataHelper{ - - Person adam() throws Exception { - Name name = new Name("Adam Brown"); - Phone privatePhone = new Phone("111111", true); - Email email = new Email("adam@gmail.com", false); - Address privateAddress = new Address("111, alpha street", true); - Tag tag1 = new Tag("tag1"); - Tag tag2 = new Tag("tag2"); - Set tags = new HashSet<>(Arrays.asList(tag1, tag2)); - return new Person(name, privatePhone, email, privateAddress, tags); - } + @Test + public void execute_draftpoints_missingCustomer() throws Exception { + TestDataHelper helper = new TestDataHelper(); - /** - * Generates a valid person using the given seed. - * Running this function with the same parameter values guarantees the returned person will have the same state. - * Each unique seed will generate a unique Person object. - * - * @param seed used to generate the person data field values - * @param isAllFieldsPrivate determines if private-able fields (phone, email, address) will be private - */ - Person generatePerson(int seed, boolean isAllFieldsPrivate) throws Exception { - return new Person( - new Name("Person " + seed), - new Phone("" + Math.abs(seed), isAllFieldsPrivate), - new Email(seed + "@email", isAllFieldsPrivate), - new Address("House of " + seed, isAllFieldsPrivate), - new HashSet<>(Arrays.asList(new Tag("tag" + Math.abs(seed)), new Tag("tag" + Math.abs(seed + 1)))) - ); - } + Order expectedDraftOrder = helper.foodOrderWithoutCustomer(); + rms.editDraftOrderDishItem(helper.burger(), helper.FOOD_QUANTITY); - /** Generates the correct add command based on the person given */ - String generateAddCommand(Person p) { - StringJoiner cmd = new StringJoiner(" "); + String expectedMessage = String.format(DraftOrderEditPointsCommand.MESSAGE_EMPTY_CUSTOMER_FIELD, + generateDraftOrderAsString(expectedDraftOrder)); - cmd.add("add"); + assertOrderCommandBehavior("draftpoints 50", expectedMessage); + } - cmd.add(p.getName().toString()); - cmd.add((p.getPhone().isPrivate() ? "pp/" : "p/") + p.getPhone()); - cmd.add((p.getEmail().isPrivate() ? "pe/" : "e/") + p.getEmail()); - cmd.add((p.getAddress().isPrivate() ? "pa/" : "a/") + p.getAddress()); + @Test + public void execute_draftpoints_missingDishes() throws Exception { + TestDataHelper helper = new TestDataHelper(); - Set tags = p.getTags(); - for(Tag t: tags){ - cmd.add("t/" + t.tagName); - } + Order expectedDraftOrder = helper.foodOrderWithoutDishes(); + rms.editDraftOrderCustomer(helper.eve()); - return cmd.toString(); - } + String expectedMessage = String.format(DraftOrderEditPointsCommand.MESSAGE_EMPTY_DISH_FIELD, + generateDraftOrderAsString(expectedDraftOrder)); - /** - * Generates an AddressBook with auto-generated persons. - * @param isPrivateStatuses flags to indicate if all contact details of respective persons should be set to - * private. - */ - AddressBook generateAddressBook(Boolean... isPrivateStatuses) throws Exception{ - AddressBook addressBook = new AddressBook(); - addToAddressBook(addressBook, isPrivateStatuses); - return addressBook; - } + assertOrderCommandBehavior("draftpoints 50", expectedMessage); + } - /** - * Generates an AddressBook based on the list of Persons given. - */ - AddressBook generateAddressBook(List persons) throws Exception{ - AddressBook addressBook = new AddressBook(); - addToAddressBook(addressBook, persons); - return addressBook; - } + @Test + public void execute_draftpoints_invalidPoints() throws Exception { + TestDataHelper helper = new TestDataHelper(); - /** - * Adds auto-generated Person objects to the given AddressBook - * @param addressBook The AddressBook to which the Persons will be added - * @param isPrivateStatuses flags to indicate if all contact details of generated persons should be set to - * private. - */ - void addToAddressBook(AddressBook addressBook, Boolean... isPrivateStatuses) throws Exception{ - addToAddressBook(addressBook, generatePersonList(isPrivateStatuses)); - } + Order expectedDraftOrder = helper.foodOrder(); + rms.editDraftOrderCustomer(helper.eve()); + rms.editDraftOrderDishItem(helper.burger(), helper.FOOD_QUANTITY); - /** - * Adds the given list of Persons to the given AddressBook - */ - void addToAddressBook(AddressBook addressBook, List personsToAdd) throws Exception{ - for(Person p: personsToAdd){ - addressBook.addPerson(p); - } - } + String expectedMessage = String.format(DraftOrderEditPointsCommand.MESSAGE_NO_REDEEMABLE_POINTS, + generateDraftOrderAsString(expectedDraftOrder)); - /** - * Creates a list of Persons based on the give Person objects. - */ - List generatePersonList(Person... persons) throws Exception{ - List personList = new ArrayList<>(); - for(Person p: persons){ - personList.add(p); - } - return personList; - } + assertOrderCommandBehavior("draftpoints 50", expectedMessage); + } - /** - * Generates a list of Persons based on the flags. - * @param isPrivateStatuses flags to indicate if all contact details of respective persons should be set to - * private. - */ - List generatePersonList(Boolean... isPrivateStatuses) throws Exception{ - List persons = new ArrayList<>(); - int i = 1; - for(Boolean p: isPrivateStatuses){ - persons.add(generatePerson(i++, p)); - } - return persons; - } + @Test + public void execute_draftpoints_isNegative() throws Exception { + TestDataHelper helper = new TestDataHelper(); + + Order expectedDraftOrder = helper.foodOrderWithReturningCustomer(); + rms.editDraftOrderCustomer(helper.david()); + rms.editDraftOrderDishItem(helper.burger(), helper.FOOD_QUANTITY); + + String expectedMessage = String.format(DraftOrderEditPointsCommand.MESSAGE_NEGATIVE_POINTS, + generateDraftOrderAsString(expectedDraftOrder)); + + assertOrderCommandBehavior("draftpoints -50", expectedMessage); + } + + //@@author + + /* + @Test + public void execute_confirmorder_missingCustomer() throws Exception { + TestDataHelper helper = new TestDataHelper(); + + Rms expectedRms = helper.generateRms(); + Order expectedDraftOrder = helper.foodOrderWithoutCustomer(); + expectedRms.addOrder(expectedDraftOrder); + + rms.editDraftOrderDishItem(helper.burger(), helper.FOOD_QUANTITY); + + String expectedMessage = DraftOrderConfirmCommand.MESSAGE_SUCCESS + + "\n" + Command.getMessageForOrderListShownSummary(expectedRms.getAllOrders().immutableListView()); - /** - * Generates a Person object with given name. Other fields will have some dummy values. - */ - Person generatePersonWithName(String name) throws Exception { - return new Person( - new Name(name), - new Phone("1", false), - new Email("1@email", false), - new Address("House of 1", false), - Collections.singleton(new Tag("tag")) - ); + assertOrderCommandBehavior("confirmdraft", + expectedMessage, + expectedRms, + true, + threeOrders); + } + */ + + //@@author AngWM + /** + * Executes the command and confirms that the result message is correct + */ + private void assertStatisticsCommandBehavior(String inputCommand, + String expectedMessage, boolean isEquals) throws Exception { + + //Execute the command + CommandResult r = logic.execute(inputCommand); + + //Confirm the result contains the right data + if (isEquals) { + assertEquals(expectedMessage, r.feedbackToUser); + } else { + org.junit.Assert.assertNotEquals(expectedMessage, r.feedbackToUser); } + + } + + @Test + public void test_statistics_employee() throws Exception { + TestDataHelper helper = new TestDataHelper(); + + // Test employee statistics when there are no employees + assertStatisticsCommandBehavior(helper.generateStatsEmpCommand(), + StatsEmployeeCommand.MESSAGE_NO_EMPLOYEE, true); + + // Test employee statistics when there are employees + Employee toBeAdded = helper.peter(); + Attendance toBeAddedAttendace = new Attendance(toBeAdded.getName().toString()); + rms.addEmployee(toBeAdded); + rms.addAttendance(toBeAddedAttendace); + assertStatisticsCommandBehavior(helper.generateStatsEmpCommand(), + StatsEmployeeCommand.MESSAGE_NO_EMPLOYEE, false); + } + + @Test + public void test_statistics_member() throws Exception { + TestDataHelper helper = new TestDataHelper(); + + // Test member statistics when there are no members + assertStatisticsCommandBehavior(helper.generateStatsMemberCommand(), + StatsMemberCommand.MESSAGE_NO_MEMBERS, true); + + // Test member statistics when there are members + Member toBeAdded = helper.generateMember(1); + rms.addMember(toBeAdded); + assertStatisticsCommandBehavior(helper.generateStatsMemberCommand(), + StatsMemberCommand.MESSAGE_NO_MEMBERS, false); + } + + @Test + public void test_statistics_menu() throws Exception { + TestDataHelper helper = new TestDataHelper(); + + // Test menu statistics when there are no orders + assertStatisticsCommandBehavior(helper.generateStatsMenuCommand(null, null), + StatsMenuCommand.MESSAGE_NO_ORDER, true); + + // Test menu statistics when there are orders + Order toBeAddedOrder = helper.generateOrder(1); + rms.addOrder(toBeAddedOrder); + toBeAddedOrder = helper.generateOrder(2); + rms.addOrder(toBeAddedOrder); + Menu toBeAddedMenu = helper.generateMenuItem(1); + rms.addMenu(toBeAddedMenu); + assertStatisticsCommandBehavior(helper.generateStatsMenuCommand(null, null), + StatsMenuCommand.MESSAGE_NO_ORDER, false); + } + + @Test + public void test_statistics_order() throws Exception { + TestDataHelper helper = new TestDataHelper(); + + // Test order statistics when there are no orders + assertStatisticsCommandBehavior(helper.generateStatsOrderCommand(), + StatsOrderCommand.MESSAGE_NO_ORDER, true); + + // Test order statistics when there are orders + Order toBeAdded = helper.generateOrder(1); + rms.addOrder(toBeAdded); + toBeAdded = helper.generateOrder(2); + rms.addOrder(toBeAdded); + assertStatisticsCommandBehavior(helper.generateStatsOrderCommand(), + StatsOrderCommand.MESSAGE_NO_ORDER, false); } + //@@author } diff --git a/test/java/seedu/addressbook/logic/TestDataHelper.java b/test/java/seedu/addressbook/logic/TestDataHelper.java new file mode 100644 index 000000000..f38235ddb --- /dev/null +++ b/test/java/seedu/addressbook/logic/TestDataHelper.java @@ -0,0 +1,646 @@ +package seedu.addressbook.logic; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.StringJoiner; + +import seedu.addressbook.data.Rms; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.Employee; +import seedu.addressbook.data.employee.EmployeeAddress; +import seedu.addressbook.data.employee.EmployeeEmail; +import seedu.addressbook.data.employee.EmployeeName; +import seedu.addressbook.data.employee.EmployeePhone; +import seedu.addressbook.data.employee.EmployeePosition; +import seedu.addressbook.data.employee.Timing; +import seedu.addressbook.data.member.Member; +import seedu.addressbook.data.member.MemberEmail; +import seedu.addressbook.data.member.MemberName; +import seedu.addressbook.data.member.Points; +import seedu.addressbook.data.menu.Menu; +import seedu.addressbook.data.menu.MenuName; +import seedu.addressbook.data.menu.Price; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.menu.Type; +import seedu.addressbook.data.order.Order; +import seedu.addressbook.data.tag.Tag; + +/** + * A utility class to generate test data. + */ +class TestDataHelper { + + public static final int FOOD_QUANTITY = 1; + + //@@author kianhong95 + /** + * Generate an employee for testing purpose + */ + public Employee peter() throws Exception { + EmployeeName name = new EmployeeName("Peter Lee"); + EmployeePhone phone = new EmployeePhone("91234567"); + EmployeeEmail email = new EmployeeEmail("PeterLee89@rms.com"); + EmployeeAddress address = new EmployeeAddress("Clementi Ave 2, Blk 543 #13-12"); + EmployeePosition position = new EmployeePosition("Cashier"); + return new Employee(name, phone, email, address, position); + } + + //@@author kangmingtay + /** + * Generate a member for testing purpose + */ + public Member eve() throws Exception { + MemberName name = new MemberName("Eve"); + MemberEmail email = new MemberEmail("eve@gmail.com"); + return new Member(name, email); + } + + //@@author kangmingtay + /** + * Generate a member with existing points for testing purpose + */ + public Member david() throws Exception { + MemberName name = new MemberName("David"); + MemberEmail email = new MemberEmail("David@gmail.com"); + Member david = new Member(name, email); + david.setPoints(100); + return david; + } + + //@@author SalsabilTasnia + /** + * Generate a menu item for testing purpose + */ + public Menu burger() throws Exception { + MenuName name = new MenuName("Cheese Burger"); + Price price = new Price("$5.00"); + Type type = new Type("main"); + Tag tag1 = new Tag("tag1"); + Tag tag2 = new Tag("tag2"); + Set tags = new HashSet<>(Arrays.asList(tag1, tag2)); + return new Menu(name, price, type, tags); + } + + //@@author px1099 + /** + * Generate a map of dish items for testing purpose + */ + public Map foodItems() throws Exception { + Map foods = new HashMap<>(); + foods.put(burger(), FOOD_QUANTITY); + return foods; + } + + //@@author kangmingtay + /** + * Generate empty points to redeem for testing purpose + */ + public int pointsToRedeem() { + return new Points().getCurrentPoints(); + } + + /** + * Generate empty points to redeem for testing purpose + */ + int pointsToRedeemLimit() throws Exception { + final int points = Integer.MAX_VALUE; + return new Points(points).getCurrentPoints(); + } + + //@@author px1099 + /** + * Generate an order for testing purpose + */ + public Order foodOrder() throws Exception { + long orderingTime = 1000; + Date orderingDate = new Date(orderingTime); + return new Order(eve(), orderingDate, foodItems(), pointsToRedeem()); + } + + /** + * Generate an order with a customer with points for testing purpose + */ + public Order foodOrderWithReturningCustomer() throws Exception { + long orderingTime = 1000; + Date orderingDate = new Date(orderingTime); + return new Order(david(), orderingDate, foodItems(), pointsToRedeem()); + } + + /** + * Generate an order without customer field for testing purpose + */ + public Order foodOrderWithoutCustomer() throws Exception { + long orderingTime = 1000; + Date orderingDate = new Date(orderingTime); + return new Order(new Member(), orderingDate, foodItems(), pointsToRedeem()); + } + + /** + * Generate an order without dishes for testing purpose + */ + public Order foodOrderWithoutDishes() throws Exception { + long orderingTime = 1000; + Date orderingDate = new Date(orderingTime); + return new Order(eve(), orderingDate, new HashMap<>(), pointsToRedeem()); + } + + //@@author kianhong95 + /** + * Generates a valid employee using the given seed. + * Running this function with the same parameter values guarantees the returned employee will have the same state. + * Each unique seed will generate a unique Employee object. + * + * @param seed used to generate the employee data field values + */ + public Employee generateEmployee(int seed) throws Exception { + Random rnd = new Random(); + int randomPhone = 10000000 + rnd.nextInt(90000000); + return new Employee( + new EmployeeName("Employee " + seed), + new EmployeePhone("" + randomPhone), + new EmployeeEmail(seed + "@email"), + new EmployeeAddress("House of " + seed), + new EmployeePosition("Position " + seed) + ); + } + + /** Generates a new employee based on the detail given */ + public Employee generateEditEmployee(Employee e, String editParam, String editDetail) throws Exception { + EmployeeName name = e.getName(); + EmployeePhone phone; + EmployeeEmail email; + EmployeeAddress address; + EmployeePosition position; + + if ("phone".equals(editParam)) { + phone = new EmployeePhone(editDetail); + } else { + phone = e.getPhone(); + } + + if ("email".equals(editParam)) { + email = new EmployeeEmail(editDetail); + } else { + email = e.getEmail(); + } + + if ("address".equals(editParam)) { + address = new EmployeeAddress(editDetail); + } else { + address = e.getAddress(); + } + + if ("position".equals(editParam)) { + position = new EmployeePosition(editDetail); + } else { + position = e.getPosition(); + } + + return new Employee(name, phone, email, address, position); + } + + /** + * Generates a valid attendance using the given seed. + * Running this function with the same parameter values guarantees the returned attendance will have the same state. + * Each unique seed will generate a unique Attendance object. + * + * @param seed used to generate the employee data field value + */ + public Attendance generateAttendance(int seed) { + return new Attendance("Employee " + seed); + } + + /** + * Generates a valid attendance with timing using the given seed and Timing Set. + * Running this function with the same parameter values guarantees the returned attendance will have the same state. + * Each unique seed will generate a unique Attendance object. + * + * @param seed used to generate the employee data field value + * @param isClockedIn set the clock in or out + * @param timings Timing Set to be added to the attendance + */ + public Attendance generateAttendanceWithTime(int seed, boolean isClockedIn, Set timings) { + return new Attendance("Employee " + seed, isClockedIn, timings); + } + + //@@author kangmingtay + /** + * Generates a valid member using the given seed. + * Running this function with the same parameter values guarantees the returned employee will have the same state. + * Each unique seed will generate a unique Member object. + * + * @param seed used to generate the employee data field values + */ + public Member generateMember(int seed) throws Exception { + return new Member( + new MemberName("Member " + seed), + new MemberEmail(seed + "@email") + ); + } + + //@@author SalsabilTasnia + /** + * Generates a valid menu item using the given seed. + * Running this function with the same parameter values guarantees the returned menu item will have the same state. + * Each unique seed will generate a unique Menu object. + * + * @param seed used to generate the menu item data field values + */ + public Menu generateMenuItem(int seed) throws Exception { + return new Menu( + new MenuName("Menu " + seed), + new Price("$" + Math.abs(seed)), + new Type(("main")), + new HashSet<>(Arrays.asList(new Tag("tag" + Math.abs(seed)), new Tag("tag" + Math.abs(seed + 1)))) + ); + } + + //@@author px1099 + public Map generateDishItems(int seed) throws Exception { + Map dishItems = new HashMap<>(); + dishItems.put(generateMenuItem(seed), Math.abs(seed)); + return dishItems; + } + + /** + * Generates a valid order using the given seed. + * Running this function with the same parameter values guarantees the returned menu item will have the same state. + * Each unique seed will generate a unique Order object. + * + * @param seed used to generate the menu item data field values + */ + public Order generateOrder(int seed) throws Exception { + return new Order( + generateMember(seed), + new Date(Math.abs(seed)), + generateDishItems(seed), + new Points().getCurrentPoints() + ); + } + + //@@author kianhong95 + /** Generates the correct add command based on the employee given */ + public String generateAddEmpCommand(Employee e) { + StringJoiner cmd = new StringJoiner(" "); + + cmd.add("addemp"); + + cmd.add(e.getName().toString()); + cmd.add("p/" + e.getPhone().toString()); + cmd.add("e/" + e.getEmail().toString()); + cmd.add("a/" + e.getAddress().toString()); + cmd.add("pos/" + e.getPosition().toString()); + + return cmd.toString(); + } + + /** Generates the correct edit command based on the employee given */ + public String generateEditEmpCommand(String index, String editParam, String editDetail) { + StringJoiner cmd = new StringJoiner(" "); + + cmd.add("editemp"); + + cmd.add(index); + + if (editParam == null || editDetail == null) { + return cmd.toString(); + } else { + if (editParam.equals("phone")) { + cmd.add("p/" + editDetail); + } + + if (editParam.equals("email")) { + cmd.add("e/" + editDetail); + } + + if (editParam.equals("address")) { + cmd.add("a/" + editDetail); + } + + if (editParam.equals("position")) { + cmd.add("pos/" + editDetail); + } + } + + return cmd.toString(); + } + + //@@author kangmingtay + /** + * Generates the correct add member command based on the member given + */ + public String generateAddMemberCommand(Member e) { + StringJoiner cmd = new StringJoiner(" "); + + cmd.add("addmember"); + + cmd.add(e.getName().toString()); + cmd.add(("e/") + e.getEmail()); + + return cmd.toString(); + } + + /** + * Generates the correct add menu command based on the menu item given + */ + public String generateMenuAddCommand(Menu m) { + StringJoiner cmd = new StringJoiner(" "); + + cmd.add("addmenu"); + + cmd.add(m.getName().toString()); + cmd.add(("p/") + m.getPrice()); + cmd.add(("type/") + m.getType()); + + Set tags = m.getTags(); + for (Tag t: tags) { + cmd.add("t/" + t.tagName); + } + + return cmd.toString(); + } + + //@@author px1099 + /** + * Generates the correct edit draft dish command based on the given index number and quantity + */ + public String generateDraftOrderEditDishCommand(int index, int quantity) { + StringJoiner cmd = new StringJoiner(" "); + + cmd.add("draftdish"); + + cmd.add(Integer.toString(index)); + cmd.add("q/" + quantity); + + return cmd.toString(); + } + + //@@author kianhong95 + /** + * Generates an Rms based on the list of Employees given. + */ + public Rms generateRmsEmployees(List employees) throws Exception { + Rms rms = new Rms(); + addEmployeesToRms(rms, employees); + return rms; + } + + /** + * Generates an Rms based on the list of Employees and Attendances given. + */ + public Rms generateRmsEmployeesAndAttendances( + List employees, + List attendances) throws Exception { + Rms rms = new Rms(); + addEmployeesToRms(rms, employees); + addAttendancesToRms(rms, attendances); + return rms; + } + + //@@author SalsabilTasnia + /** + * Generates an Rms based on the list of Menu given. + */ + public Rms generateRmsMenu(List menus) throws Exception { + Rms rms = new Rms(); + addToRmsMenu(rms, menus); + return rms; + } + + //@@author kangmingtay + /** + * Generates an Rms based on the list of Member given. + */ + public Rms generateRmsMember(List members) throws Exception { + Rms rms = new Rms(); + addMembersToRms(rms, members); + return rms; + } + + //@@author px1099 + /** + * Generates an Rms based on the list of Member given. + */ + public Rms generateRmsOrder(List orders) throws Exception { + Rms rms = new Rms(); + addOrdersToRms(rms, orders); + return rms; + } + + public Rms generateRmsOrder(Integer... integers) throws Exception { + Rms rms = new Rms(); + addOrdersToRms(rms, integers); + return rms; + } + + //@@author SalsabilTasnia + /** + * Adds the given list of Menus to the given Rms + */ + public void addToRmsMenu(Rms rms, List menusToAdd) throws Exception { + for (Menu m: menusToAdd) { + rms.addMenu(m); + } + } + + //@@author kianhong95 + /** + * Adds the given list of Employeees to the given Rms. + */ + public void addEmployeesToRms(Rms rms, List employeesToAdd) throws Exception { + for (Employee e : employeesToAdd) { + rms.addEmployee(e); + } + } + + /** + * Adds the given list of Employeees to the given Rms. + */ + public void addAttendancesToRms(Rms rms, List attendancesToAdd) { + for (Attendance a: attendancesToAdd) { + rms.addAttendance(a); + } + } + + //@@author kangmingtay + /** + * Adds the given list of Members to the given Rms + */ + public void addMembersToRms(Rms rms, List membersToAdd) throws Exception { + for (Member member: membersToAdd) { + rms.addMember(member); + } + } + + //@@author px1099 + /** + * Adds the given list of Orders to the given Rms + */ + public void addOrdersToRms(Rms rms, List ordersToAdd) throws Exception { + for (Order order: ordersToAdd) { + rms.addOrder(order); + } + } + + /** + * Adds auto-generated Order objects to the given Rms + * @param rms The Rms to which the Orders will be added + * @param integers the seeds used to create the Orders + */ + public void addOrdersToRms(Rms rms, Integer... integers) throws Exception { + addOrdersToRms(rms, generateOrderList(integers)); + } + + //@@author kianhong95 + /** + * Creates a list of Employees based on the give Employee objects. + */ + public List generateEmployeeList(Employee... employees) { + List employeeList = new ArrayList<>(); + Collections.addAll(employeeList, employees); + return employeeList; + } + + /** + * Creates a list of Attendances based on the give Attendance objects. + */ + public List generateAttendanceList(Attendance... attendances) { + List attendanceList = new ArrayList<>(); + Collections.addAll(attendanceList, attendances); + return attendanceList; + } + + //@@author kangmingtay + /** + * Creates a list of Members based on the give Member objects. + */ + public List generateMemberList(Member... members) { + List memberList = new ArrayList<>(); + Collections.addAll(memberList, members); + return memberList; + } + + //@@author SalsabilTasnia + /** + * Creates a list of Menu Items based on the give Menu objects. + */ + public List generateMenuList(Menu... menus) { + List menuList = new ArrayList<>(); + Collections.addAll(menuList, menus); + return menuList; + } + + //@@author px1099 + /** + * Creates a list of Orders based on the given Order objects. + */ + public List generateOrderList(Order... orders) { + List orderList = new ArrayList<>(); + Collections.addAll(orderList, orders); + return orderList; + } + + /** + * Creates a list of Orders based on the given integers. + */ + public List generateOrderList(Integer... integers) throws Exception { + List orderList = new ArrayList<>(); + for (Integer n: integers) { + orderList.add(generateOrder(n)); + } + return orderList; + } + + //@@author kangmingtay + /** + * Generates a Member object with given name. Other fields will have some dummy values. + */ + public Member generateMemberWithName(String name) throws Exception { + return new Member( + new MemberName(name), + new MemberEmail(name + "@email") + ); + } + + //@@author SalsabilTasnia + /** + * Generates a Menu object with given name. Other fields will have some dummy values. + */ + public Menu generateMenuWithName(String name) throws Exception { + return new Menu( + new MenuName(name), + new Price("$5.00"), + new Type("main"), + Collections.singleton(new Tag("tag")) + ); + } + + /** + * Generates a Menu object with given name. Other fields will have some dummy values. + */ + public Menu generateMenuWithGivenNameAndType(String name, String type) throws Exception { + return new Menu( + new MenuName(name), + new Price("$5.00"), + new Type(type), + Collections.singleton(new Tag("tag")) + ); + } + + //@@author AngWM + /** Generates the correct stats employee command */ + public String generateStatsEmpCommand() { + StringJoiner cmd = new StringJoiner(" "); + + cmd.add("statsemp"); + + return cmd.toString(); + } + + /** Generates the correct stats member command */ + public String generateStatsMemberCommand() { + StringJoiner cmd = new StringJoiner(" "); + + cmd.add("statsmember"); + + return cmd.toString(); + } + + /** Generates the correct stats menu command based on the to and from dates given */ + public String generateStatsMenuCommand(Date from, Date to) { + SimpleDateFormat dateFormat = new SimpleDateFormat("ddMMYYYY"); + + StringJoiner cmd = new StringJoiner(" "); + + cmd.add("statsmenu"); + + if (from != null) { + cmd.add("f/" + dateFormat.format(from)); + } + if (to != null) { + cmd.add("t/" + dateFormat.format(to)); + } + + return cmd.toString(); + } + + /** Generates the correct stats order command */ + public String generateStatsOrderCommand() { + StringJoiner cmd = new StringJoiner(" "); + + cmd.add("statsorder"); + + return cmd.toString(); + } + + //@@author +} diff --git a/test/java/seedu/addressbook/parser/ParserTest.java b/test/java/seedu/addressbook/parser/ParserTest.java index 5b5f5b013..233ddd12a 100644 --- a/test/java/seedu/addressbook/parser/ParserTest.java +++ b/test/java/seedu/addressbook/parser/ParserTest.java @@ -1,18 +1,56 @@ package seedu.addressbook.parser; -import org.junit.Before; -import org.junit.Test; -import seedu.addressbook.commands.*; -import seedu.addressbook.data.exception.IllegalValueException; -import seedu.addressbook.data.tag.Tag; -import seedu.addressbook.data.person.*; +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; -import static org.junit.Assert.*; -import static seedu.addressbook.common.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import org.junit.Before; +import org.junit.Test; + +import seedu.addressbook.commands.Command; +import seedu.addressbook.commands.ExitCommand; +import seedu.addressbook.commands.HelpCommand; +import seedu.addressbook.commands.IncorrectCommand; +import seedu.addressbook.commands.member.MemberAddCommand; +import seedu.addressbook.commands.member.MemberListCommand; +import seedu.addressbook.commands.menu.MenuAddCommand; +import seedu.addressbook.commands.menu.MenuClearCommand; +import seedu.addressbook.commands.menu.MenuDeleteCommand; +import seedu.addressbook.commands.menu.MenuFindCommand; +import seedu.addressbook.commands.menu.MenuListByTypeCommand; +import seedu.addressbook.commands.menu.MenuListCommand; +import seedu.addressbook.commands.menu.MenuRecommendationCommand; +import seedu.addressbook.commands.menu.MenuShowMainMenuCommand; +import seedu.addressbook.commands.order.DraftOrderClearCommand; +import seedu.addressbook.commands.order.DraftOrderConfirmCommand; +import seedu.addressbook.commands.order.DraftOrderEditCustomerCommand; +import seedu.addressbook.commands.order.DraftOrderEditDishCommand; +import seedu.addressbook.commands.order.OrderAddCommand; +import seedu.addressbook.commands.order.OrderClearCommand; +import seedu.addressbook.commands.order.OrderDeleteCommand; +import seedu.addressbook.commands.order.OrderListCommand; +import seedu.addressbook.commands.statistics.StatsEmployeeCommand; +import seedu.addressbook.commands.statistics.StatsHelpCommand; +import seedu.addressbook.commands.statistics.StatsMemberCommand; +import seedu.addressbook.commands.statistics.StatsMenuCommand; +import seedu.addressbook.commands.statistics.StatsOrderCommand; +import seedu.addressbook.common.Messages; +import seedu.addressbook.data.exception.IllegalValueException; +import seedu.addressbook.data.member.Member; +import seedu.addressbook.data.member.MemberEmail; +import seedu.addressbook.data.member.MemberName; +import seedu.addressbook.data.member.ReadOnlyMember; +import seedu.addressbook.data.menu.Menu; +import seedu.addressbook.data.menu.MenuName; +import seedu.addressbook.data.menu.Price; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.menu.Type; +import seedu.addressbook.data.tag.Tag; public class ParserTest { @@ -26,7 +64,7 @@ public void setup() { @Test public void emptyInput_returnsIncorrect() { final String[] emptyInputs = { "", " ", "\n \n" }; - final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE); + final String resultMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, emptyInputs); } @@ -39,25 +77,108 @@ public void unknownCommandWord_returnsHelp() { /** * Test 0-argument commands */ - + @Test public void helpCommand_parsedCorrectly() { final String input = "help"; parseAndAssertCommandType(input, HelpCommand.class); } - + + //@@author SalsabilTasnia + @Test + public void menuClearCommand_parsedCorrectly() { + final String input = "clearmenu"; + parseAndAssertCommandType(input, MenuClearCommand.class); + } + + @Test + public void menuListCommand_parsedCorrectly() { + final String input = "listmenu"; + parseAndAssertCommandType(input, MenuListCommand.class); + } + + @Test + public void menuRecommendationCommand_parsedCorrectly() { + final String input = "recommendations"; + parseAndAssertCommandType(input, MenuRecommendationCommand.class); + } + + @Test + public void menuShowMainMenuCommand_parsedCorrectly() { + final String input = "showmainmenu"; + parseAndAssertCommandType(input, MenuShowMainMenuCommand.class); + } + + //@@author AngWM + @Test + public void statsEmployeeCommand_parsedCorrectly() { + final String input = "statsemp"; + parseAndAssertCommandType(input, StatsEmployeeCommand.class); + } + + @Test + public void statsMemberCommand_parsedCorrectly() { + final String input = "statsmember"; + parseAndAssertCommandType(input, StatsMemberCommand.class); + } + + @Test + public void statsMenuCommand_parsedCorrectly() { + final String input = "statsmenu"; + parseAndAssertCommandType(input, StatsMenuCommand.class); + } + + @Test + public void statsOrderCommand_parsedCorrectly() { + final String input = "statsorder"; + parseAndAssertCommandType(input, StatsOrderCommand.class); + } + + @Test + public void statsHelpCommand_parsedCorrectly() { + final String input = "statistics"; + parseAndAssertCommandType(input, StatsHelpCommand.class); + } + + //@@author kangmingtay + @Test + public void memberListCommand_parsedCorrectly() { + final String input = "listmember"; + parseAndAssertCommandType(input, MemberListCommand.class); + } + + //@@author px1099 @Test - public void clearCommand_parsedCorrectly() { - final String input = "clear"; - parseAndAssertCommandType(input, ClearCommand.class); + public void draftOrderClearCommand_parsedCorrectly() { + final String input = "cleardraft"; + parseAndAssertCommandType(input, DraftOrderClearCommand.class); } @Test - public void listCommand_parsedCorrectly() { - final String input = "list"; - parseAndAssertCommandType(input, ListCommand.class); + public void draftOrderConfirmCommand_parsedCorrectly() { + final String input = "confirmdraft"; + parseAndAssertCommandType(input, DraftOrderConfirmCommand.class); } + @Test + public void orderAddCommand_parsedCorrectly() { + final String input = "addorder"; + parseAndAssertCommandType(input, OrderAddCommand.class); + } + + @Test + public void orderClearCommand_parsedCorrectly() { + final String input = "clearorder"; + parseAndAssertCommandType(input, OrderClearCommand.class); + } + + @Test + public void orderListCommand_parsedCorrectly() { + final String input = "listorder"; + parseAndAssertCommandType(input, OrderListCommand.class); + } + + //@@author @Test public void exitCommand_parsedCorrectly() { final String input = "exit"; @@ -65,212 +186,440 @@ public void exitCommand_parsedCorrectly() { } /** - * Test ingle index argument commands + * Test single index argument commands */ - + + //@@author SalsabilTasnia @Test - public void deleteCommand_noArgs() { - final String[] inputs = { "delete", "delete " }; - final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE); + public void menuDeleteCommand_noArgs() { + final String[] inputs = { "delmenu", "delmenu " }; + final String resultMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + MenuDeleteCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test - public void deleteCommand_argsIsNotSingleNumber() { - final String[] inputs = { "delete notAnumber ", "delete 8*wh12", "delete 1 2 3 4 5" }; - final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE); + public void menuDeleteCommand_argsIsNotSingleNumber() { + final String[] inputs = { "delmenu notAnumber ", "delmenu 8*wh12", "delmenu 1 2 3 4 5" }; + final String resultMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + MenuDeleteCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } - + @Test - public void deleteCommand_numericArg_indexParsedCorrectly() { + public void menuDeleteCommand_numericArg_indexParsedCorrectly() { final int testIndex = 1; - final String input = "delete " + testIndex; - final DeleteCommand result = parseAndAssertCommandType(input, DeleteCommand.class); + final String input = "delmenu " + testIndex; + final MenuDeleteCommand result = parseAndAssertCommandType(input, MenuDeleteCommand.class); assertEquals(result.getTargetIndex(), testIndex); } + //@@author px1099 @Test - public void viewCommand_noArgs() { - final String[] inputs = { "view", "view " }; - final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE); + public void draftOrderEditCustomerCommand_noArgs() { + final String[] inputs = { "draftcustomer", "draftcustomer " }; + final String resultMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + DraftOrderEditCustomerCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test - public void viewCommand_argsIsNotSingleNumber() { - final String[] inputs = { "view notAnumber ", "view 8*wh12", "view 1 2 3 4 5" }; - final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE); + public void draftOrderEditCustomerCommand_argsIsNotSingleNumber() { + final String[] inputs = { "draftcustomer notAnumber ", "draftcustomer 8*wh12", "draftcustomer 1 2 3 4 5" }; + final String resultMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + DraftOrderEditCustomerCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } - + @Test - public void viewCommand_numericArg_indexParsedCorrectly() { - final int testIndex = 2; - final String input = "view " + testIndex; - final ViewCommand result = parseAndAssertCommandType(input, ViewCommand.class); + public void draftOrderEditCustomerCommand_numericArg_indexParsedCorrectly() { + final int testIndex = 1; + final String input = "draftcustomer " + testIndex; + final DraftOrderEditCustomerCommand result = parseAndAssertCommandType(input, + DraftOrderEditCustomerCommand.class); assertEquals(result.getTargetIndex(), testIndex); } @Test - public void viewAllCommand_noArgs() { - final String[] inputs = { "viewall", "viewall " }; - final String resultMessage = - String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewAllCommand.MESSAGE_USAGE); + public void orderDeleteCommand_noArgs() { + final String[] inputs = { "deleteorder", "deleteorder " }; + final String resultMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + OrderDeleteCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test - public void viewAllCommand_argsIsNotSingleNumber() { - final String[] inputs = { "viewall notAnumber ", "viewall 8*wh12", "viewall 1 2 3 4 5" }; - final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewAllCommand.MESSAGE_USAGE); + public void orderDeleteCommand_argsIsNotSingleNumber() { + final String[] inputs = { "deleteorder notAnumber ", "deleteorder 8*wh12", "deleteorder 1 2 3 4 5" }; + final String resultMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + OrderDeleteCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test - public void viewAllCommand_numericArg_indexParsedCorrectly() { - final int testIndex = 3; - final String input = "viewall " + testIndex; - final ViewAllCommand result = parseAndAssertCommandType(input, ViewAllCommand.class); + public void orderDeleteCommand_numericArg_indexParsedCorrectly() { + final int testIndex = 1; + final String input = "deleteorder " + testIndex; + final OrderDeleteCommand result = parseAndAssertCommandType(input, OrderDeleteCommand.class); assertEquals(result.getTargetIndex(), testIndex); } + //@@author SalsabilTasnia /** - * Test find persons by keyword in name command + * Test find menu items by keyword in name command */ @Test - public void findCommand_invalidArgs() { + public void menuFindCommand_invalidArgs() { // no keywords final String[] inputs = { - "find", - "find " + "findmenu", + "findmenu " }; final String resultMessage = - String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE); + String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, MenuFindCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test - public void findCommand_validArgs_parsedCorrectly() { + public void menuFindCommand_validArgs_parsedCorrectly() { final String[] keywords = { "key1", "key2", "key3" }; final Set keySet = new HashSet<>(Arrays.asList(keywords)); - final String input = "find " + String.join(" ", keySet); - final FindCommand result = - parseAndAssertCommandType(input, FindCommand.class); + final String input = "findmenu " + String.join(" ", keySet); + final MenuFindCommand result = + parseAndAssertCommandType(input, MenuFindCommand.class); assertEquals(keySet, result.getKeywords()); } @Test - public void findCommand_duplicateKeys_parsedCorrectly() { + public void menuFindCommand_duplicateKeys_parsedCorrectly() { final String[] keywords = { "key1", "key2", "key3" }; final Set keySet = new HashSet<>(Arrays.asList(keywords)); // duplicate every keyword - final String input = "find " + String.join(" ", keySet) + " " + String.join(" ", keySet); - final FindCommand result = - parseAndAssertCommandType(input, FindCommand.class); + final String input = "findmenu " + String.join(" ", keySet) + " " + String.join(" ", keySet); + final MenuFindCommand result = + parseAndAssertCommandType(input, MenuFindCommand.class); assertEquals(keySet, result.getKeywords()); } + + /** + * Test listing menu items according to their category types command + */ + + @Test + public void menuListByTypeCommand_noArgs() { + // no keywords + final String inputs = "listmenutype"; + final String resultMessage = + String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, MenuListByTypeCommand.MESSAGE_USAGE); + + parseAndAssertIncorrectWithMessage(resultMessage, inputs); + } + + @Test + public void menuListByTypeCommand_validArgs_parsedCorrectly() { + final String type = "main"; + //final Set keySet = new HashSet<>(Arrays.asList(keywords)); + final String input = "listmenutype " + type; + final MenuListByTypeCommand result = + parseAndAssertCommandType(input, MenuListByTypeCommand.class); + assertEquals(type, result.getItemword()); + } + + /** - * Test add person command + * Test add menu item command */ - + @Test - public void addCommand_invalidArgs() { + public void menuAddCommand_invalidArgs() { final String[] inputs = { - "add", - "add ", - "add wrong args format", - // no phone prefix - String.format("add $s $s e/$s a/$s", Name.EXAMPLE, Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE), - // no email prefix - String.format("add $s p/$s $s a/$s", Name.EXAMPLE, Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE), - // no address prefix - String.format("add $s p/$s e/$s $s", Name.EXAMPLE, Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE) + "addmenu", + "addmenu ", + "addmenu wrong args format", + // no price prefix + String.format("addmenu %1$s %2$s type/%3$s", MenuName.EXAMPLE, Price.EXAMPLE, Type.EXAMPLE), + // no type prefix + String.format("addmenu %1$s p/%2$s %3$s", MenuName.EXAMPLE, Price.EXAMPLE, Type.EXAMPLE) }; - final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); + final String resultMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + MenuAddCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test - public void addCommand_invalidPersonDataInArgs() { - final String invalidName = "[]\\[;]"; - final String validName = Name.EXAMPLE; - final String invalidPhoneArg = "p/not__numbers"; - final String validPhoneArg = "p/" + Phone.EXAMPLE; - final String invalidEmailArg = "e/notAnEmail123"; - final String validEmailArg = "e/" + Email.EXAMPLE; + public void menuAddCommand_invalidMenuDataInArgs() { + final String invalidMenuName = "[]\\[;]"; + final String validMenuName = MenuName.EXAMPLE; + final String invalidPriceArg = "p/not__numbers"; + final String validPriceArg = "p/" + Price.EXAMPLE; + final String invalidTypeArg = "type/notType"; + final String validTypeArg = "type/" + Type.EXAMPLE; final String invalidTagArg = "t/invalid_-[.tag"; - // address can be any string, so no invalid address - final String addCommandFormatString = "add $s $s $s a/" + Address.EXAMPLE; + final String addMenuCommandFormatString = "addmenu %1$s %2$s %3$s"; // test each incorrect person data field argument individually final String[] inputs = { - // invalid name - String.format(addCommandFormatString, invalidName, validPhoneArg, validEmailArg), - // invalid phone - String.format(addCommandFormatString, validName, invalidPhoneArg, validEmailArg), - // invalid email - String.format(addCommandFormatString, validName, validPhoneArg, invalidEmailArg), - // invalid tag - String.format(addCommandFormatString, validName, validPhoneArg, validEmailArg) + " " + invalidTagArg + // invalid menu name + String.format(addMenuCommandFormatString, invalidMenuName, validPriceArg, validTypeArg), + // invalid price + String.format(addMenuCommandFormatString, validMenuName, invalidPriceArg, validTypeArg), + // invalid type + String.format(addMenuCommandFormatString, validMenuName, validPriceArg, invalidTypeArg), + // invalid tag + String.format(addMenuCommandFormatString, validMenuName, validPriceArg, validTypeArg) + " " + invalidTagArg }; for (String input : inputs) { parseAndAssertCommandType(input, IncorrectCommand.class); } } + //Testing for invalid Food Item Data Argument + + + //Testing for valid Food Item Data parsed correctly + @Test - public void addCommand_validPersonData_parsedCorrectly() { - final Person testPerson = generateTestPerson(); - final String input = convertPersonToAddCommandString(testPerson); - final AddCommand result = parseAndAssertCommandType(input, AddCommand.class); - assertEquals(result.getPerson(), testPerson); + public void menuAddCommand_validFoodItemData_parsedCorrectly() { + final Menu testMenu = generateTestMenu(); + final String input = convertMenuToAddCommandString(testMenu); + final MenuAddCommand result = parseAndAssertCommandType(input, MenuAddCommand.class); + assertEquals(result.getMenu(), testMenu); } @Test - public void addCommand_duplicateTags_merged() throws IllegalValueException { - final Person testPerson = generateTestPerson(); - String input = convertPersonToAddCommandString(testPerson); - for (Tag tag : testPerson.getTags()) { + public void menuAddCommand_duplicateTags_merged() { + final Menu testMenu = generateTestMenu(); + StringBuilder sb = new StringBuilder(); + sb.append(convertMenuToAddCommandString(testMenu)); + for (Tag tag : testMenu.getTags()) { // create duplicates by doubling each tag - input += " t/" + tag.tagName; + sb.append(" t/").append(tag.tagName); } - - final AddCommand result = parseAndAssertCommandType(input, AddCommand.class); - assertEquals(result.getPerson(), testPerson); + String input = sb.toString(); + final MenuAddCommand result = parseAndAssertCommandType(input, MenuAddCommand.class); + assertEquals(result.getMenu(), testMenu); } - private static Person generateTestPerson() { + /** + * Generate a menu item for testing + */ + private static Menu generateTestMenu() { try { - return new Person( - new Name(Name.EXAMPLE), - new Phone(Phone.EXAMPLE, true), - new Email(Email.EXAMPLE, false), - new Address(Address.EXAMPLE, true), - new HashSet<>(Arrays.asList(new Tag("tag1"), new Tag("tag2"), new Tag("tag3"))) + return new Menu( + new MenuName(MenuName.EXAMPLE), + new Price(Price.EXAMPLE), + new Type(Type.EXAMPLE), + new HashSet<>(Arrays.asList(new Tag("tag1"), new Tag("tag2"), new Tag("tag3"))) ); } catch (IllegalValueException ive) { - throw new RuntimeException("test person data should be valid by definition"); + throw new RuntimeException("test menu data should be valid by definition"); } } - private static String convertPersonToAddCommandString(ReadOnlyPerson person) { - String addCommand = "add " - + person.getName().fullName - + (person.getPhone().isPrivate() ? " pp/" : " p/") + person.getPhone().value - + (person.getEmail().isPrivate() ? " pe/" : " e/") + person.getEmail().value - + (person.getAddress().isPrivate() ? " pa/" : " a/") + person.getAddress().value; - for (Tag tag : person.getTags()) { - addCommand += " t/" + tag.tagName; + /** + * Return the command line used to add the given Menu item to the menu list + */ + private static String convertMenuToAddCommandString(ReadOnlyMenus menu) { + StringBuilder sb = new StringBuilder(); + sb.append("addmenu ").append(menu.getName().fullName) + .append(" p/").append(menu.getPrice().value) + .append(" type/").append(menu.getType().value); + for (Tag tag : menu.getTags()) { + sb.append(" t/").append(tag.tagName); + } + return sb.toString(); + } + + //@@author kangmingtay + /** + * Test add member command + */ + + @Test + public void memberAddCommand_invalidMemberDataInArgs() { + final String invalidName = "[]\\[;]"; + + // address can be any string, so no invalid address + final String memberAddCommandFormatString = "addmember p/%1$s"; + + // test each incorrect person data field argument individually + // add subsequent tests below when more fields are added... + final String[] inputs = { + // invalid name + String.format(memberAddCommandFormatString, invalidName) + }; + for (String input : inputs) { + parseAndAssertCommandType(input, IncorrectCommand.class); + } + } + + @Test + public void memberAddCommand_validEmptyMemberData_parsedCorrectly() { + final Member testMember = generateTestEmptyMember(); + final String input = convertMemberToAddCommandString(testMember); + final MemberAddCommand result = parseAndAssertCommandType(input, MemberAddCommand.class); + assertEquals(result.getMember(), testMember); + + } + + @Test + public void memberAddCommand_validMemberData_parsedCorrectly() { + final Member testMember = generateTestMember(); + final String input = convertMemberToAddCommandString(testMember); + final MemberAddCommand result = parseAndAssertCommandType(input, MemberAddCommand.class); + assertEquals(result.getMember(), testMember); + } + + + /** + * Generate an empty Member object for testing + */ + private static Member generateTestEmptyMember() { + try { + return new Member(); + } catch (Exception ive) { + throw new RuntimeException("test empty member data should be valid by definition"); + } + } + + /** + * Generate a Member for testing + */ + private static Member generateTestMember() { + try { + return new Member(new MemberName(MemberName.EXAMPLE), new MemberEmail(MemberEmail.EXAMPLE)); + } catch (IllegalValueException ie) { + throw new RuntimeException("test member data should be valid by definition"); } + } + + /** + * Return the command line used to add the given Member to the member list + */ + private static String convertMemberToAddCommandString(ReadOnlyMember member) { + String addCommand = "addmember " + + member.getName().fullName + + " e/" + member.getEmail().value; + return addCommand; } + //@@author AngWM + /** + * Test statsmenu with arg command + */ + + @Test + public void statsMenuCommand_validArgs_parsedCorrectly() { + + final String[] inputs = { + "statsmenu f/01022018", + "statsmenu t/04112018", + "statsmenu f/01102017 t/04112018" + }; + for (String input: inputs) { + parseAndAssertCommandType(input, StatsMenuCommand.class); + } + } + + @Test + public void statsMenuCommand_invalidArgs() { + final String[] inputs = { + // No from prefix + "statsmenu 0102018", + // Invalid date + "statsmenu t/00012018", + // No to prefix + "statsmenu f/01102017 /04112018", + // Duplicate prefix + "statsmenu f/01102017 f/04112018" + }; + final String resultMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + StatsMenuCommand.MESSAGE_USAGE); + parseAndAssertIncorrectWithMessage(resultMessage, inputs); + } + + //@@author px1099 + /** + * Test draft dish command + */ + + @Test + public void draftOrderEditDishCommand_invalidArgs() { + final String[] inputs = { + "draftdish", + "draftdish ", + "draftdish wrong args format", + "draftdish 1 q/2 wrong args format", + // no index + "draftdish q/15", + "draftdish 1 q/2 q/15", + "draftdish q/1 3 q/2", + // index is not a single number + "draftdish a q/15", + "draftdish * q/15", + "draftdish 1 2 3 4 q/15", + // no quantity + "draftdish 1 q/", + "draftdish 1 q/ ", + "draftdish 1 q/ 2 q/3", + // quantity is not a single number + "draftdish 1 q/a", + "draftdish 1 q/*", + "draftdish 1 q/1 2 3 4", + // no quantity prefix + "draftdish 1", + "draftdish 1 2", + // quantity is not a 1-3 digits integer + "draftdish 1 q/1000" + }; + final String resultMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + DraftOrderEditDishCommand.MESSAGE_INVALID_FORMAT); + parseAndAssertIncorrectWithMessage(resultMessage, inputs); + } + + @Test + public void draftOrderEditDishCommand_duplicateIndexes() { + final Map indexQuantityPairs = new HashMap<>(); + indexQuantityPairs.put(1, 0); + indexQuantityPairs.put(2, 1); + String input = generateDraftDishCommand(indexQuantityPairs) + " 1 q/999"; + final IncorrectCommand result = parseAndAssertCommandType(input, IncorrectCommand.class); + String expectedFeedback = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + DraftOrderEditDishCommand.MESSAGE_DUPLICATE_INDEX); + assertEquals(result.feedbackToUser, expectedFeedback); + } + + @Test + public void draftOrderEditDishCommand_validArgs_parsedCorrectly() { + final Map indexQuantityPairs = new HashMap<>(); + indexQuantityPairs.put(1, 0); + indexQuantityPairs.put(2, 1); + indexQuantityPairs.put(3, 999); + String input = generateDraftDishCommand(indexQuantityPairs); + final DraftOrderEditDishCommand result = parseAndAssertCommandType(input, DraftOrderEditDishCommand.class); + assertEquals(result.getIndexQuantityPairs(), indexQuantityPairs); + } + + /** + * Generate a draftdish command based on the given pairs of index and quantity. + * @param indexQuantityPairs the map containing the pairs of index and quantity + * @return + */ + public String generateDraftDishCommand(Map indexQuantityPairs) { + String input = "draftdish"; + for (Map.Entry entry: indexQuantityPairs.entrySet()) { + input += " " + entry.getKey() + " q/" + entry.getValue(); + } + return input; + } + + //@@author /** * Utility methods */ diff --git a/test/java/seedu/addressbook/storage/StorageFileTest.java b/test/java/seedu/addressbook/storage/StorageFileTest.java index f7d9721db..adffcbaf7 100644 --- a/test/java/seedu/addressbook/storage/StorageFileTest.java +++ b/test/java/seedu/addressbook/storage/StorageFileTest.java @@ -1,111 +1,209 @@ -package seedu.addressbook.storage; - -import static org.junit.Assert.assertEquals; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; - -import seedu.addressbook.data.AddressBook; -import seedu.addressbook.data.exception.IllegalValueException; -import seedu.addressbook.data.person.Address; -import seedu.addressbook.data.person.Email; -import seedu.addressbook.data.person.Name; -import seedu.addressbook.data.person.Person; -import seedu.addressbook.data.person.Phone; -import seedu.addressbook.data.tag.Tag; -import seedu.addressbook.storage.StorageFile.StorageOperationException; -import static seedu.addressbook.util.TestUtil.assertTextFilesEqual; - -public class StorageFileTest { - private static final String TEST_DATA_FOLDER = "test/data/StorageFileTest"; - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Rule - public TemporaryFolder testFolder = new TemporaryFolder(); - - @Test - public void constructor_nullFilePath_exceptionThrown() throws Exception { - thrown.expect(NullPointerException.class); - new StorageFile(null); - } - - @Test - public void constructor_noTxtExtension_exceptionThrown() throws Exception { - thrown.expect(IllegalValueException.class); - new StorageFile(TEST_DATA_FOLDER + "/" + "InvalidfileName"); - } - - @Test - public void load_invalidFormat_exceptionThrown() throws Exception { - // The file contains valid xml data, but does not match the AddressBook class - StorageFile storage = getStorage("InvalidData.txt"); - thrown.expect(StorageOperationException.class); - storage.load(); - } - - @Test - public void load_validFormat() throws Exception { - AddressBook actualAB = getStorage("ValidData.txt").load(); - AddressBook expectedAB = getTestAddressBook(); - - // ensure loaded AddressBook is properly constructed with test data - // TODO: overwrite equals method in AddressBook class and replace with equals method below - assertEquals(actualAB.getAllPersons(), expectedAB.getAllPersons()); - } - - @Test - public void save_nullAddressBook_exceptionThrown() throws Exception { - StorageFile storage = getTempStorage(); - thrown.expect(NullPointerException.class); - storage.save(null); - } - - @Test - public void save_validAddressBook() throws Exception { - AddressBook ab = getTestAddressBook(); - StorageFile storage = getTempStorage(); - storage.save(ab); - - assertStorageFilesEqual(storage, getStorage("ValidData.txt")); - } - - // getPath() method in StorageFile class is trivial so it is not tested - - /** - * Asserts that the contents of two storage files are the same. - */ - private void assertStorageFilesEqual(StorageFile sf1, StorageFile sf2) throws Exception { - assertTextFilesEqual(Paths.get(sf1.getPath()), Paths.get(sf2.getPath())); - } - - private StorageFile getStorage(String fileName) throws Exception { - return new StorageFile(TEST_DATA_FOLDER + "/" + fileName); - } - - private StorageFile getTempStorage() throws Exception { - return new StorageFile(testFolder.getRoot().getPath() + "/" + "temp.txt"); - } - - private AddressBook getTestAddressBook() throws Exception { - AddressBook ab = new AddressBook(); - ab.addPerson(new Person(new Name("John Doe"), - new Phone("98765432", false), - new Email("johnd@gmail.com", false), - new Address("John street, block 123, #01-01", false), - Collections.emptySet())); - ab.addPerson(new Person(new Name("Betsy Crowe"), - new Phone("1234567", true), - new Email("betsycrowe@gmail.com", false), - new Address("Newgate Prison", true), - new HashSet<>(Arrays.asList(new Tag("friend"), new Tag("criminal"))))); - return ab; - } -} +package seedu.addressbook.storage; + +import static org.junit.Assert.assertEquals; +import static seedu.addressbook.util.TestUtil.assertTextFilesEqual; + +import java.nio.file.Paths; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import seedu.addressbook.data.Rms; +import seedu.addressbook.data.employee.Attendance; +import seedu.addressbook.data.employee.Employee; +import seedu.addressbook.data.employee.EmployeeAddress; +import seedu.addressbook.data.employee.EmployeeEmail; +import seedu.addressbook.data.employee.EmployeeName; +import seedu.addressbook.data.employee.EmployeePhone; +import seedu.addressbook.data.employee.EmployeePosition; +import seedu.addressbook.data.employee.Timing; +import seedu.addressbook.data.exception.IllegalValueException; +import seedu.addressbook.data.member.Member; +import seedu.addressbook.data.member.MemberEmail; +import seedu.addressbook.data.member.MemberName; +import seedu.addressbook.data.member.MemberTier; +import seedu.addressbook.data.member.Points; +import seedu.addressbook.data.menu.Menu; +import seedu.addressbook.data.menu.MenuName; +import seedu.addressbook.data.menu.Price; +import seedu.addressbook.data.menu.ReadOnlyMenus; +import seedu.addressbook.data.menu.Type; +import seedu.addressbook.data.order.Order; +import seedu.addressbook.data.tag.Tag; +import seedu.addressbook.storage.StorageFile.StorageOperationException; + +public class StorageFileTest { + private static final String TEST_DATA_FOLDER = "test/data/StorageFileTest"; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + @Test + public void constructor_nullFilePath_exceptionThrown() throws Exception { + thrown.expect(NullPointerException.class); + new StorageFile(null); + } + + @Test + public void constructor_noTxtExtension_exceptionThrown() throws Exception { + thrown.expect(IllegalValueException.class); + new StorageFile(TEST_DATA_FOLDER + "/" + "InvalidfileName"); + } + + @Test + public void load_invalidFormat_exceptionThrown() throws Exception { + // The file contains valid xml data, but does not match the Rms class + StorageFile storage = getStorage("InvalidData.txt"); + thrown.expect(StorageOperationException.class); + storage.load(); + } + + //@@author px1099 + @Test + public void load_validFormat() throws Exception { + Rms actualRms = getStorage("ValidData.txt").load(); + Rms expectedRms = getTestRms(); + + // ensure loaded Rms is properly constructed with test data + // overwrite equals method in Rms class and replace with equals method below + assertEquals(actualRms.getAllAttendance(), expectedRms.getAllAttendance()); + assertEquals(actualRms.getAllEmployees(), expectedRms.getAllEmployees()); + assertEquals(actualRms.getAllMembers(), expectedRms.getAllMembers()); + assertEquals(actualRms.getAllMenus(), expectedRms.getAllMenus()); + assertEquals(actualRms.getAllOrders(), expectedRms.getAllOrders()); + + } + //@@author + + @Test + public void save_nullRms_exceptionThrown() throws Exception { + StorageFile storage = getTempStorage(); + thrown.expect(NullPointerException.class); + storage.save(null); + } + + @Test + public void save_validRms() throws Exception { + Rms rms = getTestRms(); + StorageFile storage = getTempStorage(); + storage.save(rms); + + assertStorageFilesEqual(storage, getStorage("ValidData.txt")); + } + + // getPath() method in StorageFile class is trivial so it is not tested + + /** + * Asserts that the contents of two storage files are the same. + */ + private void assertStorageFilesEqual(StorageFile sf1, StorageFile sf2) throws Exception { + assertTextFilesEqual(Paths.get(sf1.getPath()), Paths.get(sf2.getPath())); + } + + private StorageFile getStorage(String fileName) throws Exception { + return new StorageFile(TEST_DATA_FOLDER + "/" + fileName); + } + + private StorageFile getTempStorage() throws Exception { + return new StorageFile(testFolder.getRoot().getPath() + "/" + "temp.txt"); + } + + private Rms getTestRms() throws Exception { + Rms rms = new Rms(); + generateEmployeeAndAttendanceList(rms); + generateMenuAndMemberAndOrderList(rms); + return rms; + } + + //@@author px1099 + /** + * Add a list of employee, attendance to the specified rms for testing + */ + private void generateEmployeeAndAttendanceList(Rms rms) throws Exception { + String emp1Name = "Tay"; + String emp2Name = "Lim"; + Employee emp1 = new Employee( + new EmployeeName(emp1Name), + new EmployeePhone("11111111"), + new EmployeeEmail("11111111@gmail.com"), + new EmployeeAddress("11111111 Street"), + new EmployeePosition("Cashier")); + Employee emp2 = new Employee( + new EmployeeName(emp2Name), + new EmployeePhone("22222222"), + new EmployeeEmail("22222222@gmail.com"), + new EmployeeAddress("22222222 Street"), + new EmployeePosition("Cashier")); + + rms.addEmployee(emp1); + rms.addEmployee(emp2); + + Set timings = new LinkedHashSet<>(); + timings.add(new Timing("00:00", "11/08/2018", true)); + Attendance atd1 = new Attendance(emp1Name, true, timings); + Attendance atd2 = new Attendance(emp2Name, true, timings); + + rms.addAttendance(atd1); + rms.addAttendance(atd2); + } + + /** + * Add a list of menus, members, orders to the specified rms for testing + */ + private void generateMenuAndMemberAndOrderList(Rms rms) throws Exception { + Set foodTags = new HashSet<>(); + foodTags.add(new Tag("best")); + Menu menu1 = new Menu( + new MenuName("Burger"), + new Price("$5.00"), + new Type("main"), + foodTags); + Menu menu2 = new Menu( + new MenuName("Fries"), + new Price("$2.00"), + new Type("sides"), + foodTags); + + rms.addMenu(menu1); + rms.addMenu(menu2); + + Member member1 = new Member( + new MemberName("Ang"), + new MemberEmail("Ang@gmail.com"), + new Points(), + new Date(1000), + new MemberTier("Bronze")); + Member member2 = new Member( + new MemberName("Salsabil"), + new MemberEmail("Salsabil@gmail.com"), + new Points(10000), + new Date(1000), + new MemberTier("Gold")); + + rms.addMember(member1); + rms.addMember(member2); + + Map foodItems = new HashMap<>(); + foodItems.put(menu1, 1); + foodItems.put(menu2, 2); + + Order order1 = new Order(member1, new Date(1000), 9.0, foodItems, 0); + Order order2 = new Order(member2, new Date(1000), 8.0, foodItems, 100); + + rms.addOrder(order1); + rms.addOrder(order2); + } + + //@@author +} diff --git a/test/java/seedu/addressbook/util/TestUtil.java b/test/java/seedu/addressbook/util/TestUtil.java index 18fbfba7a..03db1ca30 100644 --- a/test/java/seedu/addressbook/util/TestUtil.java +++ b/test/java/seedu/addressbook/util/TestUtil.java @@ -1,13 +1,18 @@ package seedu.addressbook.util; import static org.junit.Assert.assertEquals; + import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +/** + * Utilities class used for testing + */ public class TestUtil { + /** * Asserts whether the text in the two given files are the same. Ignores any * differences in line endings @@ -16,5 +21,6 @@ public static void assertTextFilesEqual(Path path1, Path path2) throws IOExcepti List list1 = Files.readAllLines(path1, Charset.defaultCharset()); List list2 = Files.readAllLines(path2, Charset.defaultCharset()); assertEquals(String.join("\n", list1), String.join("\n", list2)); + } }