By: Team W09-3 Since: Feb 2019 Licence: MIT

1. Introduction

RestOrRant (ROR) is a restaurant management application for restaurant managers, waiters and cashiers. ROR is designed for them to perform front-end operations more conveniently and efficiently. It allows them to manage tables, orders, menu and statistical data using the Command Line Interface (CLI).

This Developer Guide allows you as a developer to understand the implementation and design principles used in ROR. This guide serves as a reference for you to set up and further contribute to ROR’s development.

This Developer Guide consists of the following sections:

  • Setting Up - Provides instructions to set up ROR in your computer

  • Design - Provides an overview of the architecture and components of ROR

  • Implementation - Provides implementation and design considerations of ROR’s core features

  • Documentation - Provides instructions to edit and publish documentation

  • Testing - Provides methods to run and troubleshoot tests

  • Dev Ops - Provides tools to help build, test and release ROR

To help you follow this Developer Guide better, here is the legend of the markups and formatting used throughout this document:

code

Command that can be typed

name

Reference to the codebase (such as component, class and method names)

Tips and tricks that might be useful

Additional information that is good to know

Important pointers to take note

2. Setting Up

This section will guide you along the process of setting up ROR in your computer.

2.1. Prerequisites

Before you proceed, there are two prerequisites you will need to fulfill:

  1. JDK 9 or later

    JDK 10 on Windows will fail to run tests in headless mode due to a JavaFX bug. Windows developers are highly recommended to use JDK 9.
  2. IntelliJ IDE

    IntelliJ by default has Gradle and JavaFx plugins installed.
    Do not disable them. If you have disabled them, go to File  Settings  Plugins to re-enable them.

2.2. Setting up ROR in your computer

Having satisfied the prerequisites, you can now set up ROR in your computer by following the steps below:

  1. Fork this repo, and clone the fork to your computer

  2. Open IntelliJ (if you are not in the welcome screen, click File  Close Project to close the existing project dialog first)

  3. Set up the correct JDK version for Gradle

    1. Click Configure  Project Defaults  Project Structure

    2. Click New…​ and find the directory of the JDK

  4. Click Import Project

  5. Locate the build.gradle file and select it. Click OK

  6. Click Open as Project

  7. Click OK to accept the default settings

  8. 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.

  9. Open MainWindow.java and check for any code errors

    1. Due to an ongoing issue with some of the newer versions of IntelliJ, code errors may be detected even if the project can be built and run successfully

    2. To resolve this, place your cursor over any of the code section highlighted in red. Press ALT+ENTER, and select Add '--add-modules=…​' to module compiler options for each error

  10. Repeat this for the test folder as well (e.g. check HelpWindowTest.java for code errors, and if so, resolve it the same way)

2.3. Verifying the setup

To make sure that your ROR is properly set up:

  1. Run the seedu.address.MainApp and try a few commands

  2. Run the tests to ensure they all pass.

2.4. Configurations to do before writing code

You should ensure that the following configurations are done to be compatible with ROR.

2.4.1. Configuring the coding style

This project follows oss-generic coding standards. IntelliJ’s default style is mostly compliant with ours but it uses a different import order from ours. To rectify,

  1. Go to File  Settings…​ (Windows/Linux), or IntelliJ IDEA  Preferences…​ (macOS)

  2. Select Editor  Code Style  Java

  3. 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 blank line between each import

Optionally, you can follow the UsingCheckstyle.adoc document to configure Intellij to check style-compliance as you write code.

2.4.2. 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:

  1. Configure the site-wide documentation settings in build.gradle, such as the site-name, to suit your own project.

  2. Replace the URL in the attribute repoURL in DeveloperGuide.adoc and UserGuide.adoc with the URL of your fork.

2.4.3. Setting up CI

Set up Travis to perform Continuous Integration (CI) for your fork. See UsingTravis.adoc to learn how to set it up.

After setting up Travis, you can optionally set up coverage reporting for your team fork (see UsingCoveralls.adoc).

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 UsingAppVeyor.adoc).

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)

2.4.4. Getting started with coding

When you are ready to start coding, get some sense of the overall design by reading Section 3.1, “Architecture”.

3. Design

Now that you have successfully set up ROR, you are advised to read this section in order to better understand the architecture and its components in ROR. Knowing the structure of ROR and the interactions between its components will allow you to better navigate and modify the code, giving you a headstart in contributing to ROR.

3.1. Architecture

The Architecture Diagram given below explains the high-level design of the ROR App.

Architecture
Figure 1. Architecture Diagram
The .pptx files used to create diagrams in this document can be found in the diagrams folder. To update a diagram, modify the diagram in the pptx file, select the objects of the diagram, and choose Save as picture and save your new diagram into the images folder.

Given below is a quick overview of each component.

Main has only one class called MainApp. It has two functions:

  • 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(s) where necessary.

Commons represents a collection of classes used by multiple other components. The following class plays an important role at the architecture level:

  • LogsCenter : Used by many classes to write log messages to the App’s log file.

The rest of the App consists of four components.

  • UI : The UI of the App.

  • Logic : The command executor.

  • Model : Holds the data of the App in-memory.

  • Storage : Reads data from, and writes data to, the hard disk.

Each of the four components

  • Defines its API in an interface with the same name as the Component.

  • Exposes its functionality using a {Component Name}Manager class.

For example, the Logic component (see the class diagram given below) defines its API in the Logic.java interface and exposes its functionality using the LogicManager.java class.

LogicComponentClassDiagram
Figure 2. Class Diagram of the Logic Component

How the architecture components interact with each other

The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command addTable 2.

SDforAddTable
Figure 3. Component interactions for addTable 2 command

The sections below give more details of each component.

3.2. UI component

The UI component (Figure 4) handles the User Interface of ROR.

UiClassDiagram
Figure 4. Structure of the UI Component

API : Ui.java

The UI consists of a MainWindow that is made up of parts such as CommandBox, ResultDisplay and StatusBarFooter. All these, including the MainWindow, inherit from the abstract UiPart class. Not all the parts are displayed to the user at the same time (eg. TableFlowPanel is not shown together with StatisticsFlowPanel). The parts that are shown depends on the mode.

The UI component uses JavaFx UI framework. The layout of these UI parts are defined in matching .fxml files that are in the /blob/master/src/main/resources/view folder. For example, the layout of the MainWindow is specified in MainWindow.fxml.

The UI component:

  • Executes user commands using the Logic component.

  • Listens for changes to Model data so that the UI can be updated with the modified data.

3.3. Logic component

The Logic component (Figure 5) deals with the logic behind the execution of commands.

LogicComponentClassDiagram
Figure 5. Structure of the Logic Component

API : Logic.java

  • Logic uses the RestOrRantParser class to parse the user command.

  • This results in a Command object which is executed by the LogicManager.

  • The command execution can affect the Model (e.g. adding a table).

  • The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui.

  • In addition, the CommandResult object can also instruct the Ui to perform certain actions, such as displaying help to the user or changing the mode displayed.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("addTable 2") API call.

AddTableSdforLogic
Figure 6. Interactions Inside the Logic Component for the addTable 2 Command

3.4. Model component

The Model component (Figure 7) keeps a model of the current state of ROR in memory.

ModelClassDiagram
Figure 7. Structure of the Model Component

API : Model.java

The Model :

  • stores a UserPref object that represents the user’s preferences.

  • stores the RestOrRant data.

  • exposes an unmodifiable ObservableList<Table>, ObservableList<OrderItem>, ObservableList<MenuItem> and ObservableList<Revenue> that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.

  • does not depend on any of the other three components.

3.5. Storage component

The Storage component (Figure 8) manages the storing and retrieving of data on local files.

StorageClassDiagram
Figure 8. Structure of the Storage Component

API : Storage.java

The Storage component:

  • saves UserPref objects in json format and reads it back.

  • LogicManager calls for each of Tables, Orders, Menu and Statistics to be saved when they are modified.

  • saves the Tables, Orders, Menu and Statistics data in json format and reads it back.

3.6. Common classes

Classes used by multiple components are in the seedu.address.commons package.

4. Implementation

This section describes some noteworthy details on how our features are implemented.

4.1. Mode System

4.1.1. Modes

As ROR contains many features, users may find it complicated to handle all the features at one go with so many commands to use. Furthermore, users may be overwhelmed if all the restaurant’s information were to be displayed together in one screen.

As such, we have decided to implement a mode system for ROR to organise the available commands into their respective features. ROR provides 4 different modes for the users, each displaying a different set of panels that are relevant to the mode:

To use the different features, we have also implemented commands for users to switch between the 4 user modes.

There is also a Bill Mode available for your use as a developer, although the existence of this mode is not shared with the users. The Bill Mode displays the receipt and is only used after the bill command is executed in RestOrRant.

Although the bill command is used in Table Mode, it is considered a Statistics feature rather than an Orders feature as it involves calculation for the revenue.

4.1.2. Current Implementation

Changing modes in general

The modes of the application are defined using a Mode enum and the current mode of the application is stored as a private attribute mode in LogicManager. Users can switch between modes in ROR using the {XXX}Mode command, such as menuMode and tableMode. When the mode of the application is changed, we need to update the:

  • Logic component so that the mode attribute in LogicManager reflects the new current mode, since this is used to check if a command can be executed,

  • UI component so that the panels display the information that is relevant to the mode.

For all these commands, these updates are done using a new toMode attribute added to the CommandResult object. As an example, the figure below shows the sequence diagram for when a user executes the menuMode command:

MenuModeSequenceDiagram
Figure 9. Component interactions for menuMode command

Upon execution of the menuMode command, MenuModeCommand#generateCommandResult() will generate a CommandResult whose toMode attribute is set to MENU_MODE and return it to the LogicManager. Now, the updates can be done for the respective components:

  • Logic component: LogicManager calls CommandResult#newModeStatus() to retrieve the mode to be changed to and updates the current mode stored in its mode attribute by calling LogicManager#changeMode(). The CommandResult is passed to MainWindow in UI.

  • UI component: MainWindow calls CommandResult#newModeStatus() to retrieve the mode to be changed to and updates the display panels by calling MainWindow#handleChangeMode().

For commands that do not involve a change in mode, CommandResult#newModeStatus() will return null and no updates to the current mode or display will be done.
Changing to Table Mode for a specific table

All the commands that change modes take no parameters except for Table Mode. When the user wants to perform order-related operations (eg. add an item to the table’s order), the user will first have to switch to Table Mode by typing the tableMode command (or its shortcut TM) followed by the table number of the table that the user wants to perform actions on.

The following sequence diagram shows the relevant parts of what happens when a user executes the tableMode 3 command:

TableModeSequenceDiagram
Figure 10. Component interactions for tableMode 3 command

The Logic and UI component interactions work in a similar way to the other commands that change modes and the implementation is mentioned above. The only difference is the parsing of the argument 3 into a TableNumber object that indicates the table number of the target table to switch to. This TableNumber object is stored as an attribute in TableModeCommand.

Let’s focus on what happens to the Model component when TableModeCommand#execute() is called. The relevant parts of the sequence diagram has been extracted out and shown in the diagram below.

TableModeModelSequenceDiagram
Figure 11. Interaction between Logic and Model components for tableMode 3 command

It first retrieves the Table in RestOrRant that corresponds to its TableNumber attribute. The list of tables are stored within the Tables object in RestOrRant. The read only versions of these objects can be obtained from Model#getRestOrRant() and then ReadOnlyRestOrRant#getTables(). The desired Table object can then be retrieved by passing TableNumber into ReadOnlyTables#getTableFromNumber().

The Table is then set as the selected table in Model using Model#setSelectedTable(). This is to keep track of the current table so that the Table Mode commands like addToOrder and bill can easily perform their operations by getting the table from Model#getSelectedTable().

After which, Model#updateFilteredOrderItemList() is called to update the filtered list such that it only contains order items ordered by the chosen table. This will then update the item list panel in the UI to display only the order items that were ordered by the chosen table.

TableModeCommand#execute() also checks that the application is not already in Table Mode of the same table number, the table number specified is valid, and the desired table is occupied.

4.1.3. Design Considerations

Aspect Alternative 1 Alternative 2

Defining of modes

Modes are defined using a Mode enum.
- Pros: Easily identify incorrect modes since the set of valid modes is restricted to the enum values.
- Cons: Need to create a new class in Logic, which increases coupling between Logic and UI components.

We decided to choose this option as the enum is an abstraction over the possible modes. Apart from being less prone to errors, it also makes the code more readable as the enum values explicitly state the name of the mode (eg. TABLE_MODE, MENU_MODE) and are stored in Mode variables, making it clear that it refers to a mode and not just any string.

Modes can be defined with strings (eg. "TableMode") or integers.
- Pros: Easy to implement.
- Cons: May be prone to errors (eg. mistyping a string) that causes bugs in the code.

Storing of current mode

Mode stored in the Logic component (LogicManager).
- Pros: Easy to access the current mode to check whether a command is valid to execute in a particular mode.
- Cons: Slightly breaks abstraction since mode is not purely related to command execution.

We decided to store the current mode in Logic because we note that most of the work that requires access to the current mode is done here, so providing a quick access will be more efficient. The Model component does not need to know what the current mode is at all, so it would be better to keep the coupling low to facilitate integration.

Mode stored in the Model component (ModelManager).
- Pros: Maintains abstraction as the mode is part of the current state of the application, which is maintained by Model.
- Cons: Increases coupling among the Model, Logic and UI components.

Specifying table number for Table Mode commands

Table number specified only once when switching to Table Mode, then retrieved from Model#getSelectedTable() for future commands.
- Pros: Convenient to retrieve table number without having to explicitly specify it in every command.
- Cons: Commands that change modes are not standardized since the others do not have any parameters.

We decided to go ahead with this implementation as we feel that from a user’s point of view, it would be more convenient to be able to just specify the table number once and then perform a series of operations afterwards. Furthermore, having to parse the table number for every command will also do more work.

Table number specified for every command (eg. addToOrder 3 W09 1 to add 1x W09 to table 3).
- Pros: All commands that change modes are simple and similar.
- Cons: Makes the commands longer to type.

4.2. Tables feature

The Table feature allows the users to add, edit and clear tables using the commands addTable, editPax, editSeats and clearTables to ensure that RestOrRant reflects their physical restaurant in terms of the number of tables and each table’s occupancy. Also, the Tables feature provides a quick way to look for an available table that can accommodate the specified number of customers using the spaceFor command. All of the table details are displayed in our UI for easy reference.

4.2.1. Classes for Tables feature in Model

The Tables feature was implemented with the introduction of a new set of classes to Model. A new Tables class encapsulates all the methods and classes related to this feature. It also contains a listener that is used to update the storage when any modification is made to the tables. The Tables object is stored in RestOrRant and it exposes a read-only interface ReadOnlyTables to allow the other components to retrieve table-related information while maintaining data integrity.

Tables contains a UniqueTableList, which contains a list of all the tables. Each Table object consists of 2 objects:

  • TableNumber indicates the table number of the table

  • TableStatus indicates the occupancy of the table

Table objects are uniquely identified by TableNumber as all tables in a restaurant are uniquely labelled by their table numbers.

The Tables Class Diagram below shows the relationship between the classes.

TablesModelClassDiagram
Figure 12. Tables Class Diagram in the Model component

4.2.2. Adding a table to ROR

The interaction between the components for this function is similar to adding to menu. A key difference in function is that addToMenu only adds 1 menu item each time, but addTable allows multiple tables to be added in a single command.

Tables are stored in a list. When addTable 4 is keyed in, the command is parsed by AddTableCommandParser to check if the arguments are in valid format. They are then converted into a list of String table statuses, which are stored in the AddTableCommand object returned by the parser.

When AddTableCommand#execute() is called, the table statuses in the String list are processed one by one. For each table status, a Table with the appropriate TableStatus and TableNumber is created and stored in the Model.

The TableNumber runs sequentially and is kept track of by the Tables class. This means that there users are not allowed specify the table number for their tables and hence will not in any case add duplicate tables to ROR.

4.2.3. Edit pax at a table

Current Implementation

The edit pax mechanism is facilitated by UniqueTableList. It stores all tables currently in ROR and ensures that there are no duplicated tables. Additionally, it implements UniqueTableList#setTable(targetTable, editedTable), which replaces the target table in the list with the new table with the updated TableStatus.

This operation is exposed in the Model interface as Model#setTable(targetTable, editedTable).

Given below is an example usage scenario and how the edit pax mechanism behaves at each step.

Step 1. Suppose the user launches the application for the first time, the sample data has 35 tables and are stored in the UniqueTableList as shown below. Focus your attention on the Table with the orange markup as this will be our target table in this example.

EditPaxStateListDiagram Before

Step 2. Suppose the user decides to have 2 customers sit at table 2 and executes editPax 2 2 command to update Table 2 with 2 customers. The editPax command calls Model#setTable(targeTable, editedTable), causing the ROR to create a new table with the updated TableStatus and replace the current target table in the UniqueTableList. As shown below, the index of the replaced table now points to a new Table which is highlighted orange.

EditPaxStateListDiagram After

The Storage detects the above change in the UniqueTableList and also updates itself.

The following sequence diagram shows how the edit pax operation works:

EditPaxSequenceDiagram
If the table to be edited does not exist in the UniqueTableList, the application returns an error to the user rather than attempting to replace the non existent table.

The following activity diagram summarises what happens when a user executes a new command:

EditPaxActivityDiagram
Design Considerations
Aspect Alternative 1 Alternative 2

Editing the tables in UniqueTableList

Replace the entire table with an updated table.
- Pros: Maintains abstraction of Table and TableStatus and reduce errors from abusing the editability of TableStatus in Table.
- Cons: May have efficiency issues when creating a whole new object whenever part of it needs to be changed.

We decided to choose this option because we realise that if we were to edit the tables directly, we would need a listener for each Table in the UniqueTableList to detect changes, which is not an efficient design. Furthermore, the tables can only be edited one at a time. Hence, the space efficiency issue would not be significant.

Directly edit the TableStatus of the tables.
- Pros: Potentially more efficient as there is no need to create new object and replace items in a list.
- Cons: Breaks abstraction to a certain extent and allows future developers to abuse the object.

4.2.4. Edit seats at a table

The interaction between the components for this function is similar to editing pax at a table. A key difference in function is that editPax changes the occupancy of the Table by changing the number of customers seated at the table while editSeats changes the number of seats at the table.

One thing to note is that the editSeats command can be called on not just unoccupied tables but also occupied tables. Hence, there is a different set of checks in place to ensure that the integrity of tables is intact.

An example would be, suppose editSeats is called on an occupied table, we do not allow the user to change the number of seats at the table to less than the current number of customers seated at the same table. Hence, if Table 1 already has 4 customers, editSeats 1 2 would return an error.

4.2.5. Clear all tables in ROR

The interaction between the components for this function is similar to clearing an order. The key difference is that clearTables requires ROR to be completely empty before the command can be successfully executed.

This means that all of the tables in ROR have to be unoccupied and this is checked in the ClearTablesCommand#execute() method as shown in the code snippet below.

public CommandResult execute(Mode mode, Model model, CommandHistory history) throws CommandException {
        requireNonNull(model);
        StringBuilder sbFinalOutput = new StringBuilder(MESSAGE_SUCCESS);
        if (model.getRestOrRant().getTables().getTableList().isEmpty()) { (1)
            throw new CommandException(MESSAGE_FAILURE);
        }
        if (!model.isRestaurantEmpty()) { (2)
            throw new CommandException(INVALID_RESTAURANT_STATE);
        }

        model.setTables(new ArrayList<>()); (3)

        return new CommandResult(sbFinalOutput.toString());
}

In the method above, Model#isRestaurantEmpty() loops through all the tables in the UniqueTableList and checks if they are occupied. Let’s walk through this method to see how the main functionality of clearTables is implemented:

1 It first checks if ROR already has no tables. If it does not, an error is returned as we cannot clear an empty ROR.
2 Then, as mentioned above, it checks if ROR is completely unoccupied by using the Model#isRestaurantEmpty() method.
3 Lastly, it replaces the current list of tables in UniqueTableList with an empty List which effectively removes all record of any tables from ROR.

4.2.6. Getting an available table for customers

Current Implementation

This command’s interaction with the components is very similar to editing pax at a table. The key difference is that it searches for the best fit table automatically instead of having the table specified by the user.

This functionality uses the spaceFor command and is facilitated by the Model#isOccupied(Table) method. This method checks if the specified table has any customers seated at the table. The spaceFor command works like a request and takes in 1 argument specifying the number of customers to be seated in ROR.

SpaceForCommand#execute() is then called which searches through the UniqueTableList for the best fit table. In this case, the best fit Table is the smallest available one that can accommodate the specified number of customers. If there is more than 1 Table that fits those criterions, the Table with the smallest TableNumber is chosen.

public CommandResult execute(Mode mode, Model model, CommandHistory history) throws CommandException {
        requireNonNull(model);
        Table bestFitTable = null;
        int smallestSize = Integer.MAX_VALUE;

        for (Table table : model.getRestOrRant().getTables().getTableList()) {
            int numberOfSeats = Integer.parseInt(table.getTableStatus().getNumberOfSeats());
            if (!table.isOccupied() && numberOfSeats >= size && numberOfSeats < smallestSize) { (1)
                bestFitTable = table;
                smallestSize = numberOfSeats;
            }
            if (smallestSize == size) { (2)
                break;
            }
        }

        if (bestFitTable == null) { (3)
            throw new CommandException(String.format(MESSAGE_NO_AVAILABLE_TABLE, String.valueOf(size)));
        }
        model.setTable(bestFitTable, new Table(bestFitTable.getTableNumber(),
                new TableStatus(String.valueOf(size) + "/" + bestFitTable.getTableStatus().getNumberOfSeats()))); (4)

        return new CommandResult(String.format(MESSAGE_SUCCESS, String.valueOf(size), bestFitTable.getTableNumber()));
}

In the method above, we loop through the tables in the UniqueTableList while checking for the following:

1 It first checks if the table is occupied. If the table is occupied, the if-statement is short circuited. If the table is not occupied, we check if the table is able to accommodate the specified number of customers.
2 This second if statement checks if we have already found the best fit table. If we have, we can break out of the for loop to make this operation more efficient.
3 The last if-statement checks if an available table can be found in the UniqueTableList. If the table is not found, an error message is thrown for the user.

The smallestSize attribute is meant to facilitate the search for the best fit table.

Design Considerations
Aspect Alternative 1 Alternative 2

Choosing the table to allocate to the customers.

Search for the best fit table (table with the least number of seats that can accommodate the customers)
- Pros: Allows the user to allocate tables in the restaurant more efficiently.
- Cons: May have efficiency issues when as it is more likely to go through the all the tables in the UniqueTableList to search for the table.

We decided to choose this option because we realise getting the best fit table would be something that our users prefer as it allows them to run their business more efficiently.

Select the first table that is able to accommodate the specified customers.
- Pros: Potentially more efficient as it is less likely to have to go through the entire UniqueTableList
- Cons: Brings about inefficiencies in the users' front-end operations which is what our application is designed to improve. This makes them less likely to use the command.

4.3. Orders feature

The Orders feature allows restaurant front-end staff to take orders from customers using the addToOrder command, as well as remove incorrectly inputted orders using the deleteFromOrder or clearOrder commands. They can track the current list of items ordered for each table (in Table Mode) or across all tables (in Restaurant Mode) and update their serving statuses with the serve command.

4.3.1. Classes for Orders feature in Model

The Orders feature was implemented with the introduction of a new set of classes to Model. A new Orders class encapsulates all the methods and classes related to this feature. It also contains a listener that is used to update the storage when any modification is made to the orders. The Orders object is stored in RestOrRant and it exposes a read-only interface ReadOnlyOrders to allow the other components to retrieve order-related information while maintaining data integrity.

Orders contains a UniqueOrderItemList, which contains a list of all the items ordered across all the tables in the restaurant, represented by OrderItem objects. Each OrderItem object consists of 4 objects:

  • TableNumber indicates the table number of the table that ordered the item

  • OrderItemStatus tracks the quantity ordered and the quantity that has yet to be served

  • Code indicates the item code and is used to identify the item ordered

  • Name indicates the item name and is used for display in the UI

OrderItem objects are uniquely identified by TableNumber, Code and Name as each table can only have one order entry for each item.

The Orders Class Diagram below shows the relationship between the classes.

OrdersModelClassDiagram
Figure 13. Orders Class Diagram in the Model component

4.3.2. Getting the table number

The Orders feature allows users to perform order-related operations for a specific table. The table number of that table is specified when switching over to Table Mode.

The table number is stored so that users do not need to keep specifying it when typing the commands.

As a result, all the commands in the Orders feature will have to retrieve the table number from Model first before they can perform their functions. The current table is stored as a Table object in Model, which is retrieved by calling Model#getSelectedTable(). The table number, which is represented by a TableNumber object, can then be retrieved from that Table. The sequence diagram for this operation is shown below.

CurrentTableSequenceDiagram
Figure 14. Sequence diagram for retrieving the table number

4.3.3. Adding to an order

The user may want to add new order items to a specific table’s order and this can be done with the addToOrder command.

The interaction between the components for this function is similar to adding to menu. A key difference in function is that addToMenu only adds 1 menu item each time, but addToOrder allows multiple and duplicate items to be added in a single command.

Suppose the user wants to add 3 "W09 Chicken Wings" and 1 "W12 French Fries" to the order. When addToOrder W09 3 W12 1 is keyed in, the command is parsed by AddToOrderCommandParser to check if the arguments are in valid format. They are then converted into a list of Code item codes and a list of Integer quantities, which are stored in the AddToOrderCommand object returned by the parser.

When AddToOrderCommand#execute() is called, the Code objects in the list are processed one by one. For each Code:

  1. It attempts to retrieve a MenuItem object that contains the given Code from Model to check if the item exists in the menu. If it does not exist, an error is returned and the subsequent item codes in the list are not processed.

  2. It thens attempt to find an OrderItem object with the given Code and the current TableNumber to see if that item has already been ordered by the table. If it already exists, the quantity will be updated in the UniqueOrderItemList using Model#setOrderItem(). A new OrderItem with a new OrderItemStatus will be created to replace the existing one. Otherwise, a new OrderItem will be added to the UniqueOrderItemList using Model#addOrderItem().

4.3.4. Deleting from an order

The user may want to remove an order item from a specific table’s order and this can be done with the deleteFromOrder command.

The interaction between the components for this function is similar to deleting from menu.

Suppose the user wants to delete "W09 Chicken Wings" from the order. When deleteFromOrder W09 is keyed in, the command is parsed by DeleteFromOrderCommandParser to check if the item code is in valid format. It is then converted into a Code object which is stored in the DeleteFromOrderCommand object returned by the parser.

When DeleteFromOrderCommand#execute() is called, it will use the Code to check whether the item exists in the menu and whether the current table has ordered the item. If the OrderItem is found, it will be deleted from the UniqueOrderItemList using Model#deleteOrderItem().

4.3.5. Clearing an order

The user may want to remove all the order items from a specific table’s order and this can be done with the clearOrder command.

Current Implementation

When the user enters the clearOrder command, the command is parsed and a ClearOrderCommand object is created.

ClearOrderCommand#execute() is then called, which in turn calls Model#clearOrderItemsFrom() with the current TableNumber. Model acts as a facade and forwards the call to Orders#clearOrderItemsFrom(), and that method’s source code is replicated below.

public void clearOrderItemsFrom(TableNumber tableNumber) {
        ArrayList<OrderItem> itemsToDelete = new ArrayList<>();
        for (OrderItem orderItem : orderItems) { (1)
            if (orderItem.getTableNumber().equals(tableNumber)) {
                itemsToDelete.add(orderItem);
            }
        }
        for (OrderItem item : itemsToDelete) { (2)
            orderItems.remove(item);
        }
        indicateModified(); (3)
}

In the method above, orderItems is a UniqueOrderItemList that contains all the order items from all the tables. Let’s walk through this method to see how the main functionality of clearOrder is implemented:

1 It first loops through all the items in the UniqueOrderItemList and stores the OrderItem objects with the current TableNumber into a new list.
2 It then deletes the items in this list from the UniqueOrderItemList.
3 Finally, it triggers the listener in Orders to indicate that the order item list has been modified. This signals to the LogicManager to update the storage data files by calling Storage#saveOrders().

The sequence diagram for when a user executes the clearOrder command is shown below.

ClearOrderSequenceDiagram
Figure 15. Component interactions for clearOrder command
Design Considerations
Aspect Alternative 1 Alternative 2

Storing of order items

The order items from all the tables in RestOrRant can be stored in a single UniqueOrderItemList.
- Pros: Easier to handle a single list.
- Cons: clearOrder needs to iterate through the entire list to delete the order items from the specific table.

We decided to choose this option because we realise that customers are unlikely to order a large number of items in a single table, so it would not be space efficient to create so many UniqueOrderItemList. Furthermore, it also provides the benefit of arranging the order items across all the tables based on when they were ordered without keeping an extra time attribute. This provides the useful functionality of seeing which orders should be fulfilled first in Restaurant Mode.

Each table has its own UniqueOrderItemList to store the order items for that table.
- Pros: clearOrder will just need to create a new empty UniqueOrderItemList to replace the existing one.
- Cons: Difficult to keep track of so many lists. May be space inefficient to create multiple UniqueOrderItemList if each list only has a few items.

4.3.6. Serving items in an order

The user may want to mark order items from a specific table’s order as served and this can be done with the serve command.

The interaction between the components for this function is similar to editing the occupancy of a table.

Suppose 3 "W09 Chicken Wings" have been ordered by the table and the user wants to mark 2 of them as served. When serve W09 2 is keyed in, the command is parsed by ServeCommandParser to check if the arguments are in valid format. The valid Code item code and Integer quantity are then stored in the ServeCommand object returned by the parser.

When ServeCommand#execute() is called, it will use the Code to check whether the item exists in the menu and whether the current table has ordered the item. If the OrderItem is found and the quantity served is valid, a new OrderItem object will be created with a new OrderItemStatus that reflects the new quantities. Model#setOrderItem() is then used to replace the old OrderItem object in the UniqueOrderItemList with the new one.

The Menu feature allows restaurant managers to take add items to the menu using the addToMenu command, as well as remove menu items as needed using the deleteFromMenu or clearMenu commands. They can track the current list of available items in the menu (with the UI displays in Table Mode and Menu Mode).

This feature works in support of

  • Orders feature: Adding and removing items from the menu would in turn affect items to be ordered.

  • Statistics feature: Popular menu items are listed in descending order of quantities ordered. This is supported by storing the quantities ordered in the json file and internally updating it when orders are confirmed.

The Menu feature was implemented with the introduction of a new set of classes to Model. A new Menu class encapsulates all the methods and classes related to this feature. It also contains a listener that is used to update the storage when any modification is made to the orders. The Menu object is stored in RestOrRant and it exposes a read-only interface ReadOnlyMenu to allow the other components to retrieve menu-related information while maintaining data integrity.

Menu contains a UniqueMenuItemList, which contains a list of all the items in the menu, represented by MenuItem objects. Each MenuItem object consists of 3 objects:

  • Name indicates the item name and is used for display in the UI

  • Code indicates the item code and is used to identify the menu item

  • Price indicates the item price and is used for display in the UI and calculation of bill

MenuItem objects are uniquely identified by Code. As such, each menu item should have distinct item codes.

The Menu Class Diagram below shows the relationship between the classes.

MenuModelClassDiagram
Figure 16. Menu Class Diagram in the Model component

4.4.2. Adding to menu

The user may want to add new menu items to the menu and this can be done with the addToMenu command.

Current Implementation

The menu items in the menu are stored as a list. Adding a new menu item to the menu is done by adding to the existing list.

The command is read as a text string from the command box in the UI and then is executed by calling MainWindow#executeCommand(), which passes this string to the Logic component by calling Logic#execute().

The sequence diagram for interactions between the Logic, Model and Storage components when a user executes the addToMenu command is shown below.

AddToMenuSequenceDiagram
Figure 17. Component interactions for addToMenu command

The Logic#execute() method then creates a command from the text string input by parsing the string to identify the command word (done by the RestOrRantParser#parse() method) and other parameters, in this case the attributes of the MenuItem (done by the AddToMenuCommandParser#parse() method, omitted from the sequence diagram). After parsing, the text string is then converted to Name, Code, and Price and a new MenuItem is created with these attributes and then passed to Model component.

In the Model component, firstly the validity of the Name, Code and Price of the MenuItem is checked. The three attributes must follow the specified format for them to be valid. The MenuItem is only added to the UniqueMenuItemList after ensuring that the same menu item does not already exist in the list. For this to be true, the new menu item should not have the same Code as any other item in the list.

After adding to the list in the Model component, the Menu#indicateModified() method is called, which then triggers the Logic component to save the state of the menu, by calling Storage#saveMenu().

The User Interface also updated by adding the new menu item to the list panel.

This action can only be done in Menu Mode. If not in Menu Mode currently, users can first change mode by switching over to Menu Mode.
Design Considerations
Aspect Alternative 1 Alternative 2

Saving changes from addition of menu items

Save only the menu, using the Menu#indicateModified() and Storage#saveMenu() methods.
- Pros: Uses less memory as any update to the menu will only save the menu.
- Cons: An increase in the number of variables during runtime, as every feature will need a listener and a boolean (for instance, menu feature would have a listener and a boolean menuModified to call Storage#saveMenu() when the boolean evaluates to true).

We decided to choose this option because we realise that there would be bigger performance issues such as lagging in the long run if we were to save every instance of the restaurant for every update to each feature.

Save the whole restaurant instance, by just having two methods (RestOrRant#indicateModified() and Storage#saveRestOrRant()) overall.
- Pros: Fewer variables during runtime, easier to call just one boolean upon any change to restaurant features.
- Cons: There is no need to save another feature if only menu is being updated, may cause lagging instead of improving performance.

4.4.3. Deleting from menu

The user may want to remove a menu item from the menu and this can be done using the deleteFromMenu command.

Current Implementation

As mentioned above, deleting a menu item from the menu is done by removing from the existing list.

The interactions between all four components when a user executes the deleteFromMenu command is very similar to the interactions when adding to menu:

One specific difference is that deleting from menu only requires item Code, whereas adding to menu requires Name, Code and Price. For this reason, we have decided to not use any prefixes (such as c/ to denote item code).

After parsing the input text string by the RestOrRantParser#parse() method, the DeleteFromMenuCommand object is created.

DeleteFromMenuCommand then makes use of the method Menu#getItemFromCode() to retrieve the menu item from the item Code input. The code snippet of the method is shown below:

public Optional<MenuItem> getItemFromCode(Code code) {
    Iterator<MenuItem> iterator = menuItems.iterator();
    while (iterator.hasNext()) { (1)
        MenuItem menuItem = iterator.next();
        if (menuItem.getCode().equals(code)) {
            return Optional.of(menuItem); (2)
        }
    }
    return Optional.empty(); (3)
}

The MenuItem is retrieved from the Code by searching through the UniqueMenuItemList, called menuItems in this snippet. Optional is used as a means of defensive programming so that a null Code would not result in a null pointer exception but would be safely handled by showing an error message to the user for incorrect Code input.

1 The method first loops through all items in the list.
2 If a MenuItem with the same code is found, it is returned as an Optional.
3 If no such MenuItem is found, an Optional.empty() is returned, to safely handle nulls.

The retrieved MenuItem is then passed to Model component to be removed from the UniqueMenuItemList.

Similar to adding to the menu, after removing from the list in the Model component, the Menu#indicateModified() method is called, which then triggers the Logic component to save the state of the menu, by calling Storage#saveMenu().

The User Interface is also updated by removing the menu item on the list panel.

This action can only be done in Menu Mode. If not in Menu Mode currently, users can first change mode by switching over to Menu Mode.
Design Considerations

The above design considerations under Adding to Menu on saving changes to the menu also holds when deleting menu items.

Aspect Alternative 1 Alternative 2

Retrieving the MenuItem from the Code

Handle this operation with the already existing UniqueMenuItemList.
- Pros: Uses less memory as using another data structure would take up twice the space, since UniqueMenuItemList is required for ease of implementations of display in the UI.
- Cons: Takes more time to loop through all menu items in the UniqueMenuItemList, especially if the list is extensive.

We decided to choose this option because we realise that the system would be able to support a typical restaurant’s menu with close to no lagging, and chose to optimise memory over speed.

Create a new HashTable to map item codes to menu items, and search through this HashTable to retrieve the item.
- Pros: Less time taken to search in HashTable than in List.
- Cons: Uses more memory as all menu items will be saved in two data structures. Moreover, there is a need to reflect any changes to the menu in both of the data structures.

4.4.4. Clearing the menu

The user may want to remove all the menu items from the menu and this can be done with the clearMenu command.

The interaction between the components for this function is similar to clearing an order. A small difference is that clearMenu clears all menu items, while clearOrder only clears orders within a specific table.

When clearMenu is keyed in, the command is parsed by RestOrRantParser and a ClearMenuCommand object is returned by the parser.

When ClearMenuCommand#execute() is called, it will check if the restaurant is occupied, and if the menu is empty. CommandException with respective error messages will be thrown if either of the conditions are true. After ensuring that the restaurant is unoccupied and the menu is non-empty, the menu will be cleared by initializing the menu with an empty ArrayList, with the Model#setMenuItems() method.

4.5. Statistics feature

The Statistics feature allows users to perform queries that deal with the restaurant’s statistical data, mainly the revenue earned and the popularity of menu items. With Statistics feature, restaurant front-end staffs can bill the customers' orders and automatically save the revenue in the restaurant’s statistics storage using the bill command. Other commands like yearly, monthly, daily and revenue commands allow the manager to retrieve and study the restaurant’s statistical data trends.

4.5.1. Classes for Statistics feature in Model

The Statistics feature was implemented with the introduction of a new set of classes to Model. A new Statistics class encapsulates all the methods and classes related to this feature. It also contains a listener that is used to update the storage when any modification is made to the statistics. The Statistics object is stored in RestOrRant and it exposes a read-only interface ReadOnlyStatistics to allow the other components to retrieve statistics-related information while maintaining data integrity.

Statistics contains a RevenueList, which contains a list of all the revenue recorded in the restaurant, represented by Revenue objects. Each Revenue object consists of 3 objects:

  • Year indicates the year which the revenue was earned

  • Month indicates the month which the revenue was earned

  • Day indicates the day which the revenue was earned

YearlyRevenue, MonthlyRevenue and DailyRevenue are classes that extends Revenue to inherit its properties. Hence, they are all uniquely identified by Year, Month and Day. Similarly, Date and Bill also consists and are identified by the above 3 objects.

The Statistics Class Diagram below shows the relationship between the classes.

Statistics ModelClassDiagram
Figure 18. Statistics Class diagram in Model component

4.5.2. Design Considerations for Statistics Storage

The data stored in Storage (/data/statistics.json) only encompasses Day, Month, Year and Revenue earned on that specific date. These data are stored when the bill command is called.

Aspect Alternative 1 Alternative 2

Storage of statistical data

Stores daily revenue which comprises of day, month, year and revenue.
- Pros: Less storage space used.
- Cons: No record of bill. Impossible to retrieve receipts.

In a restaurant, it is unlikely that a customer will request for a refund after consuming and paying for the meal. Even if there are complaints about the meal, they will demand for a newly cooked dish before calling for the bill. Hence, we did not see the need to store receipts in the storage and chose this design.

Stores bill which comprises of the day, month, year, bill, table number and receipt.
- Pros: Able to retrieve data of bills.
- Cons: More storage space for lists of bills for each day.

4.5.3. Billing an order

Each Bill is unique to a Table. Hence, the bill mechanism can only be executed under Table Mode. The Table and its TableNumber was specified when switching over to Table Mode. The way TableNumber is specified is explained in Orders feature.

Once the bill command is called, it will internally switch to Bill Mode where the receipt of the specified table is displayed. Concurrently, the bill mechanism accesses functions from Tables, Orders, Menu and Statistics to execute the following:

  • Table: Updates specified table’s occupancy to accommodate new customers.

  • Order: Clears the order list that belongs to the specified table.

  • Menu: Saves the quantity of menu items ordered in the Menu storage (/data/menu.json).

  • Statistics: Saves the revenue in the Statistics Storage (/data/statistics.json).

Current Implementation

The main crux of Bill mechanism is implemented in BillCommand. Just like any other commands, BillCommand#execute() overrides Command#execute() which is called in LogicManager. It takes in the current mode, model and command history which will provide the necessary functions it will need to execute the command properly with the following operations:

  • BillCommand#calculateBill() — Creates a bill with a receipt of all menu items ordered and updates the quantity of menu items ordered in Menu#menuItems.

  • BillCommand#createOrUpdateRevenue() — Either saves the bill as a new DailyRevenue or updates an existing Revenue in the Statistics#revenueList.

  • BillCommand#updateStatusOfTable() — Updates the occupancy of the specified table to indicate that it has zero customers and resets the selected table to null.

  • Model#clearOrderItemsFrom() — Clears the order list to prepare for the next customer.

This sequence diagram provides an overview of how the operations above work together to execute bill command.

Bill SequenceDiagram
Figure 19. Sequence diagram for executing the bill command

To give you a better understanding of the sequence diagram, the following is a usage scenario and how the bill mechanism behaves at each step.

Step 1. Let’s assume that the user is in Table Mode 1. This means Table 1 is the selected table. Before the bill command can be executed successfully, BillCommand#execute() conducts 2 checks.

  1. Checks that current table obtained from Model#getSelectedTable() exists.

  2. Checks that all order items in Model#getFilteredOrderItemList() have been served. The status of the order item is determined via Model#getOrderItemStatus().

Step 2. Given that the command passes all checks, the user can successfully run BillCommand#calculateBill() to assign a new calculated bill to the tableToBill attribute. Let’s refer to the function’s sequence diagram below.

Bill CalculateBill
Figure 20. Sequence diagram for calculating the bill

BillCommand#calculateBill() calls both Model#getFilteredOrderItemList() and ReadOnlyRestOrRant#getMenu() to obtain the table’s list of orders and the restaurant’s menu. After which, it iterates through the list of orders to obtain individual OrderItem and executes the following in each iteration:

  • Gets MenuItem by passing the code of the OrderItem into Menu#getItemFromCode().

  • Retrieves the quantity of OrderItem ordered using OrderItem#getQuantityOrdered().

  • Updates the quantity of menu item ordered using ReadOnlyMenu#updateMenuItemQuantity().

  • Creates a receipt that appends the MenuItem#getCode(), MenuItem#getName(), MenuItem#getPrice() and the quantity retrieved previously.

  • Adds the price of each MenuItem multiplied by the quantity ordered to the total bill.

After iterating through the list of orders, receipt appends the final calculation of the total bill and the function returns a new Bill that contains the updated tableNumber, totalBill and receipt. This new Bill will be assigned to the bill attribute and passed into Model#setRecentBill() to update the Model’s recent bill (This step is needed when changing the user interface to Bill Mode).

Now that bill is updated, it is passed into BillCommand#createOrUpdateRevenue() to store the calculated bill in Statistics#revenueList. Refer to the function’s sequence diagram below.

Bill CreateOrUpdateRevenue
Figure 21. Sequence diagram for creating or updating revenue in the Statistics revenue list

As shown above, BillCommand#createOrUpdateRevenue() creates a DailyRevenue with the bill’s year, month and day. It checks if the newly created DailyRevenue exists in the revenueList via Model#hasRevenue(). If it is true that DailyRevenue exists, the function iterates through the Model#getFilteredRevenueList() and searches for a an existing Revenue that has the same year, month and day as the DailyRevenue. Once the Revenue is found, Revenue#addToRevenue() is used to add the total bill of the DailyRevenue into Revenue. Otherwise, the DailyRevenue is added into the revenueList via Model#addRevenue().

Now, that the necessary data are updated in the internal list of Menu and Statistics, BillCommand#execute() calls BillCommand#updateStatusOfTable() and Model#clearOrderItemsFrom() to make the table available for new customers.

Bill UpdateStatusOfTable
Figure 22. Sequence diagram for updating the status of the table

BillCommand#updateStatusOfTable() updates the table status by creating a new Table with an updated TableStatus and replace the existing Table with the new one. After the table is updated, the program sets the selected table to null to indicate that the table is no longer in use.

Once these essential functions are executed, BillCommand#execute() moves on to indicate that the Tables, Orders, Menu and Statistics data have been modified via Model#updateTables(), Model#updateOrders(), Model#updateMenu(), Model#updateStatistics(). After which it returns the CommandResult to LogicManager#excute() where the it will check if the Tables, Orders, Menu and Statistics data have been modified and updates the storage accordingly. The same CommandResult is returned to MainWindow#executeCommand() where the user interface switches to Bill Mode to display the receipt of the recent Bill.

Internally the program switches to Bill Mode. However, this mode is shown to the user as Table Mode to avoid confusion.
Design Considerations
Aspect Alternative 1 Alternative 2

Design calculateBill()

Iterates through the FilteredOrderItemList once to append the receipt, update quantity ordered of the items in Menu and calculate the bill.
- Pros: Efficient as it only iterates through the list once.
- Cons: The code can be further break into different functions.

We chose this design to prioritize efficiency in this use case. Since bill command is only called when the customer is ready to pay and leave the restaurant, it is better for the payment process to be swift.

Create different functions that specifically calculates the bill, creates a bill receipt and update the quantity of menu items ordered.
- Pros: Adheres to a better abstract design of the code.
- Cons: Inefficient due to multiple iteration of the FilteredOrderItemList. Given a long list of order items, this design will be impractical.

The following implementation applies for yearly, monthly and daily commands. These commands make adjustments to the user interface to display statistical data trends.

Current Implementation

The execution of these commands is similar to changing the mode. The only difference is that isYearly, isMonthly and isDaily boolean variables are used in MainWindow#executeCommand(). These boolean variables are passed into MainWindow#handleChangeMode() where it will determine which display to switch to. You may refer to the activity diagram to understand the cause of actions in the Statistics Mode user interface.

Trend ActivityDiagram
Figure 23. Statistics Mode Display Activity Diagram

Since these commands changes the user interface, a huge portion of their mechanism is dependent on StatisticsFlowPanel. It determines the layout and arrangement of the revenue cards displayed.

Once the revenue observable list and boolean variables are passed into StatisticsFlowPanel, the function checks which boolean variable is true. Lets assume that isDaily is true for daily command. The function enters a while loop that iterates through the revenue list until at most 30 DailyRevenue objects are created and added to a list of DailyRevenue.

The list of DailyRevenue generated is sorted in a reverse chronological order of the date. Hence, even if there are no revenue recorded on a specific date, a DailyRevenue object is still created and added to the list.

After which, the list of DailyRevenue is iterated to generate DailyStatisticsCard for each DailyRevenue. These DailyStatisticsCard are added to the StatisticsFlowPanel#statisticsFlowPane to update the user interface.

Design Considerations
Aspect Alternative 1 Alternative 2

Arrangement of [XXX]StatisticsCard

[XXX]StatisticsCard are arranged in descending order.
- Pros: The most recent card is visible to the user without scrolling.
- Cons: Viewing dates in a descending order might make it difficult to study trends.

[XXX]StatisticsCard are arranged in ascending order.
- Pros: Provides a sense of familiarity as the order is similar to that of a calendar.
- Cons: Users have to scroll to the end to view the latest revenue.

Limit the number of [XXX]StatisticsCard

Maximum [XXX]StatisticsCard shown is 30.
- Pros: Prevents overflow of data. No longer have to scroll through multiple [XXX]StatisticsCard
- Cons: Cannot view revenue from the other dates.

Display all [XXX]StatisticsCard created from stored revenue data.
- Pros: All revenue is available in a single page.
- Cons: Might experience overflow of data in the user interface. User might have to scroll to the revenue

Create additional [XXX]StatisticsCard with $0 revenue

Display all [XXX]StatisticsCard in chronological order.
- Pros: Gives user a bigger picture of the restaurant’s recent earning trends.
- Cons: Takes up space. It might be unnecessary information to the user.

Only display all [XXX]StatisticsCard created from stored revenue data.
- Pros: More revenue figures are presented to the user.
- Cons: Might appear too cluttered making it difficult to study revenue trends.

The current implementation uses the designs in Alternative 1. However, in version 2.0, the application will take the pros of Alternative 2 designs and incorporate them to the current implementation. To help users better study the revenue trends, v2.0 will allow users to call the commands with an argument that indicates the month or year that they want. The display will arrange the DailyStatisticsCard in a calender view with 7 columns for each day. A limit of 31 DailyStatisticsCard will be used to replicate a calendar and prevent overflowing.

4.5.5. Getting the revenue

Current Implementation

When the user enters the revenue command, the command is parsed and a RevenueCommand object is created.

RevenueCommand#execute() is then called, which in turn calls Model#getFilteredRevenueList() to obtain the list of revenue stored in the Storage. To create convenience and efficiency in the execution of the revenue command, 4 different types of argument combinations are provided for the users to obtain the desired revenue. The diagram below illustrates the internal flow of actions for different argument inputs.

Revenue ActivityDiagram
Figure 24. Revenue Internal Flow Diagram

The above diagram summarizes the overall implementation of RevenueCommand. Depending on the user input, the function creates a new Revenue object and iterates through the revenue list to search for revenue of the same year, month or day. If the revenue is the same as the new Revenue, its total revenue is added to the new Revenue. After which, the new Revenue object is used to generate the CommandResult output string where the calculated figure will be returned.

Design Considerations
Aspect Alternative 1 Alternative 2

Validation regex of Month and Day

Allow single and double digits.
- Pros: Users type less, providing convenience.

Since the user input will eventually be parsed as an integer during its validation check, there is no need to restrict the day and month inputs to 2 digits.

Restricts to double digit
- Pros: Easier to store and present date in a standardize format like 01-Jan-2000 rather than 1-Jan-2000.
- Cons: User has to type more.

Validation regex of Year

Only allow four digits that are greater than 2000.
- Pros: Specific restrictions prevents the possibility of making nonsensical typos.
- Cons: Less flexibility.

Since this application is catered to existing restaurants with messy front-end management, there is no need to accept dates that are way before the 2000s.

No restrictions
- Pros: Type less.
- Cons: Users have more freedom to make queries that are not beneficial to them.

Switch case versus Array

Store months in string format in an Array
- Pros: Efficient and fast.
- Cons: Uses more temporary storage.

We chose this design as it not only reduces the lines of code, but also efficiently obtains the needed string.

Switch case
- Pros: Use less temporary storage.
- Cons: Checks through every case if it only passes the last case.

4.6. 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 Section 4.7, “Configuration”)

  • 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

4.7. Configuration

Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: config.json).

5. Documentation

We use asciidoc for writing documentation.

We chose asciidoc over Markdown because asciidoc, although a bit more complex than Markdown, provides more flexibility in formatting.

5.1. Editing Documentation

See UsingGradle.adoc 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.

5.2. Publishing Documentation

See UsingTravis.adoc to learn how to deploy GitHub Pages using Travis.

5.3. Converting Documentation to PDF format

We use 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.

  1. Follow the instructions in UsingGradle.adoc to convert the AsciiDoc files in the docs/ directory to HTML format.

  2. Go to your generated HTML files in the build/docs folder, right click on them and select Open with  Google Chrome.

  3. Within Chrome, click on the Print option in Chrome’s menu.

  4. 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.

chrome save as pdf
Figure 25. Saving documentation as PDF files in Chrome

5.4. Site-wide Documentation Settings

The build.gradle file specifies some project-specific asciidoc attributes which affects how all documentation files within this project are rendered.

Attributes left unset in the build.gradle file will use their default value, if any.
Table 1. 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 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

5.5. Per-file Documentation Settings

Each .adoc file may also specify some file-specific asciidoc attributes which affects how the file is rendered.

Asciidoctor’s built-in attributes may be specified and used as well.

Attributes left unset in .adoc files will use their default value, if any.
Table 2. 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*, AboutUs, ContactUs

* Official SE-EDU projects only

not set

no-site-header

Set this attribute to remove the site navigation bar.

not set

5.6. Site Template

The files in docs/stylesheets are the CSS stylesheets of the site. You can modify them to change some properties of the site’s design.

The files in docs/templates controls the rendering of .adoc files into HTML5. These template files are written in a mixture of Ruby and Slim.

Modifying the template files in 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.

6. Testing

6.1. Running Tests

There are three ways to run tests.

The most reliable way to run tests is the 3rd one. The first two methods might fail some GUI tests due to platform/resolution-specific idiosyncrasies.

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)

See UsingGradle.adoc for more info on how to run tests using Gradle.

Method 3: Using Gradle (headless)

Thanks to the TestFX library we use, our GUI tests can be run in the headless mode. In the headless mode, GUI tests do not show up on the screen. That means the developer can do other things on the Computer while the tests are running.

To run tests in headless mode, open a console and run the command gradlew clean headless allTests (Mac/Linux: ./gradlew clean headless allTests)

6.2. Types of tests

We have two types of tests:

  1. GUI Tests - These are tests involving the GUI. They include,

    1. System Tests that test the entire App by simulating user actions on the GUI. These are in the systemtests package.

    2. Unit tests that test the individual components. These are in seedu.address.ui package.

  2. Non-GUI Tests - These are tests not involving the GUI. They include,

    1. Unit tests targeting the lowest level methods/classes.
      e.g. seedu.address.commons.StringUtilTest

    2. Integration tests that are checking the integration of multiple code units (those code units are assumed to be working).
      e.g. seedu.address.storage.StorageManagerTest

    3. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
      e.g. seedu.address.logic.LogicManagerTest

6.3. Troubleshooting Testing

Problem: HelpWindowTest fails with a NullPointerException.

  • Reason: One of its dependencies, HelpWindow.html in src/main/resources/docs is missing.

  • Solution: Execute Gradle task processResources.

7. Dev Ops

7.1. Build Automation

See UsingGradle.adoc to learn how to use Gradle for build automation.

7.2. Continuous Integration

We use Travis CI and AppVeyor to perform Continuous Integration on our projects. See UsingTravis.adoc and UsingAppVeyor.adoc for more details.

7.3. Coverage Reporting

We use Coveralls to track the code coverage of our projects. See UsingCoveralls.adoc for more details.

7.4. Documentation Previews

When a pull request has changes to asciidoc files, you can use Netlify to see a preview of how the HTML version of those asciidoc files will look like when the pull request is merged. See UsingNetlify.adoc for more details.

7.5. Making a Release

Here are the steps to create a new release.

  1. Update the version number in MainApp.java.

  2. Generate a JAR file using Gradle.

  3. Tag the repo with the version number. e.g. v0.1

  4. Create a new release using GitHub and upload the JAR file you created.

7.6. Managing Dependencies

A project often depends on third-party libraries. For example, RestOrRant depends on the Jackson library for JSON parsing. Managing these dependencies can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than these alternatives:

  1. Include those libraries in the repo (this bloats the repo size)

  2. Require developers to download those libraries manually (this creates extra work for developers)

Appendix A: Product Scope

Target user profile:

  • has a need to manage the front-end operations of a restaurant (customers, orders, menu)

  • still uses outdated front-end restaurant management systems like pen and paper

  • wants to easily keep track of restaurant statistics (revenue, dishes’ popularity)

  • prefer desktop apps over other types

  • can type fast

  • prefers typing over mouse input

  • is reasonably comfortable using CLI apps

Value proposition: RestOrRant manages the customers, orders, menu and restaurant statistics with a speed faster than a typical mouse/GUI driven application. This allows the user to have a clear idea of the current status of the restaurant without the need to physically check out the space. This is especially useful for busy restaurants with unorganised or slow front-end management systems.

Appendix B: User Stories

Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *

Priority As a …​ I want to …​ So that I can…​

* * *

waiter

view the status of the tables

know which tables are free/occupied and which tables are waiting for orders/has received their orders

* * *

waiter

change status of tables

update the availability of tables

* * *

manager

add items to the menu

accurately reflect the available dishes

* * *

manager

delete items from the menu

update changes to the menu

* * *

waiter

add orders to a table

know which orders to serve to each table

* * *

waiter

delete orders from a table

update changes to the orders of each table

* * *

waiter

update wrong orders

conveniently assist to customers demands without keying in wrong/changed orders

* * *

cashier

retrieve the bill for each table

easily process their bill when they are done eating

* * *

manager

obtain specific date’s revenue

easily close accounts for the day and track profit

* * *

manager

view yearly, monthly and daily statistics

study the restaurant’s revenue trends

* *

waiter

add special requests to the order

inform the chef of the customers' specific preferences

* *

manager

view popular dishes via statistics

decide the amount of ingredients to purchase

* *

waiter

view popular dishes via statistics

suggest the best dishes to the customer

*

waiter

reserve tables

see which tables can be provided to other subsequent customers

Appendix C: Use Cases

(For all use cases below, the System is RestOrRant and the Actor is the user, unless specified otherwise)

Use case: UC1 - Switching to Restaurant Mode

MSS

  1. User enters command to switch to Restaurant Mode.

  2. RestOrRant displays a grid of all the tables in the restaurant with their statuses in the main panel, as well as a list of all the orders from all tables in the side panel.

    User case ends.

Extensions

  • 1a. RestOrRant is already in Restaurant Mode.

    • 1a1. RestOrRant returns an error message saying that it is already in the mode.

      Use case ends.

Use case: UC2 - Switching to Menu Mode

MSS

  1. User enters command to switch to Menu Mode.

  2. RestOrRant displays a grid of all the items on the restaurant menu with their code, name and price on the side panel.

    Use case ends.

Extensions

  • 1a. RestOrRant is already in Menu Mode.

    • 1a1. RestOrRant returns an error message saying that it is already in the mode.

      Use case ends.

Use case: UC3 - Switching to Table Mode

MSS

  1. User enters command to switch to Table Mode and specifies the table number of the target table.

  2. RestOrRant displays a grid of all the items on the restaurant menu with their code, name and price on the side panel.

    Use case ends.

Extensions

  • 1a. RestOrRant is already in Table Mode of the same table number.

    • 1a1. RestOrRant returns an error message saying that it is already in the mode.

      Use case ends.

  • 1b. The specified table number is invalid.

    • 1ab. RestOrRant returns an error message saying that the table number is invalid.

      Use case ends.

  • 1c. The table with the specified table number is unoccupied.

    • 1c1. RestOrRant returns an error message saying that the table is unoccupied.

      Use case ends.

Use case: UC4 - Adding new tables

MSS

  1. User enters command to add a table to the restaurant.

  2. RestOrRant Program returns success message that the table is added, providing the table number and table status.

Use case: UC5 - Checking for available tables

MSS

  1. User enters command to list all tables in the restaurant.

  2. RestOrRant Program returns the status (number of customers at the table/number of seats at the table) of all tables in the restaurant.

    Use case ends.

Extensions

  • 2a. The list is empty.

    • 2a1. RestOrRant Program returns an error message that there are no tables in the restaurant.

      Use case ends.

Use case: UC6 - Taking an order

MSS

  1. User switches to Table Mode (UC3).

  2. RestOrRant switches to Table Mode.

  3. User enters a list of item codes and quantities of the items to be added to the table’s order.

  4. RestOrRant updates the list of order items in the side panel to include the newly added order items.

    Use case ends.

Extensions

  • 3a. The item code(s) are not present in the menu.

    • 3a1. RestOrRant displays the first invalid item code in the Results Display and stops adding new items to the order.

      Use case resumes from step 4.

  • 3b. The item code(s) do not follow the standard alphanumeric format.

    • 3b1. RestOrRant returns an error message saying that the item code is in invalid format.

      Use case ends.

  • 3c. The quantity of an item exceeds the limit after the user tries to add the specified quantity.

    • 3c1. RestOrRant displays the item code of that item in the Results Display and stops adding new items to the order.

      Use case resumes from step 4.

  • 3d. The quantities provided are not in valid format or exceed the limit.

    • 3d1. RestOrRant returns an error message saying that the quantity is in invalid format.

      Use case ends.

Use case: UC7 - Adding items to menu

Preconditions: Item to be added cannot exist in the menu.

MSS

  1. User switches to Menu Mode (UC2).

  2. RestOrRant switches to Menu Mode.

  3. User enters the item’s code, name and price.

  4. RestOrRant updates the list of menu items in the side panel to include the added menu item.

    Use case ends.

Extensions

  • 1a. The menu already contains the item to be added (same item code).

    • 1a1. RestOrRant returns an error message saying that duplicate menu items cannot be added.

      Use case ends.

  • 1b. The item code does not follow the specified alphanumeric format.

    • 1b1. RestOrRant returns an error message saying that the item code is in invalid format.

      Use case ends.

  • 1c. The item name does not follow the standard alphanumeric format.

    • 1b1. RestOrRant returns an error message saying that the item name is in invalid format.

      Use case ends.

  • 1d. The item price does not follow the specified format of rational number with two decimal digits.

    • 1d1. RestOrRant returns an error message saying that the item price is in invalid format.

      Use case ends.

Use case: UC8 - Obtaining revenue from statistics

MSS

  1. User enters the revenue command with specific optional arguments (year, month and date).

  2. RestOrRant Program collates data from the stated period, calculates all the revenue and prints out the calculated figure for the user to refer.
    Use case ends.

Extensions

  • 1a. The combination of arguments are invalid.

    • 1a1. RestOrRant prints out "Invalid command format! revenue: Gets the revenue from the specified year, month or day. Parameters: [y/YEAR [m/MONTH] [d/DAY]]] Example: revenue or revenue y/2019 or revenue y/2019 m/12 or revenue y/2019 m/12 d/30"
      Use case ends.

  • 1b. Invalid year.

    • 1b1. RestOrRant prints out "Year should be in the format <four digit integer>, it should not be blank or larger than current year. Years before 2000s are not supported (2000 to current year)."
      Use case ends.

  • 1c. Invalid month.

    • 1c1. RestOrRant prints out "Month should be a single or double double digit integer, and it should not be blank and should be a valid month (1 to 12)."
      Use case ends.

  • 1d. Invalid day.

    • 1d1. RestOrRant prints out "Day should be a single or double double digit integer, it should not be blank and should be a valid day (1 to 31)."
      Use case ends.

  • 1d. Invalid date.

    • 1d1. RestOrRant prints out "Date is invalid. It does not exists."
      Use case ends.

  • 2a. No arguments keyed in.

    • 2a1. RestOrRant returns the revenue of the current day.

      Use case ends.

  • 2b. There is no data recorded, list is empty.

  • 2b1. RestOrRant prints out "Revenue for (stated day, month or year) $ 0.00"

    Use case ends.

Appendix D: Non Functional Requirements

  1. Should work on any mainstream OS as long as it has Java 9 or higher installed.

  2. Should work on both 32-bit and 64-bit machines.

  3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.

  4. Should be able to handle any kind of input, including invalid ones.

  5. Should be able to respond within two seconds, even with a large number of tables and order items.

  6. Number of tables in the restaurant will not be greater than 400.

  7. Quantity of an item ordered by a table will not be greater than 2,000,000,000.

  8. Number of menu items in the menu will not be greater than 200.

  9. Not required to handle the printing of receipts.

Appendix E: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X

Order

The list of items ordered by dining customers at a table

Menu

The list of items available to be ordered by dining customers at the restaurant

Bill

The amount of money that dining customers have to pay based on their order

Revenue

The amount of money earned based on the bills

Appendix F: Instructions for Manual Testing

Given below are instructions to test the app manually.

These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.

F.1. Launch and Shutdown

  1. Initial launch

    1. Download the jar file and copy into an empty folder.

    2. Double-click the jar file.
      Expected: Shows the GUI with sample data. The window size may not be optimum.

  2. Saving window preferences

    1. Resize the window to an optimum size. Move the window to a different location. Close the window.

    2. Re-launch the app by double-clicking the jar file.
      Expected: The most recent window size and location is retained.

  3. Exiting the application

    1. Add some data into RestOrRant (eg. addTable 4 4 2 2).

    2. Exit RestOrRant by clicking the File  Exit button, then re-launch the app.
      Expected: The application closes upon exit. All data remains intact upon re-launch.

    3. Exit RestOrRant by typing the exit command, then re-launch the app.
      Expected: Similar to previous.

    4. Exit RestOrRant by clicking the Close button on the window, then re-launch the app.
      Expected: Similar to previous.

{ more test cases …​ }

F.2. Changing Modes

  1. Restaurant Mode

    1. In Restaurant Mode, switch to Restaurant Mode with restaurantMode or RM.
      Expected: Mode is not changed. Error message is shown in the Results Display.

    2. In any other mode, switch to Restaurant Mode with restaurantMode or RM.
      Expected: Status bar indicates "Restaurant Mode". Success message is shown in the Results Display. Main panel will display a grid of tables with their statuses and side panel will display a list of order items across all tables.

  2. Menu Mode

    1. In Menu Mode, switch to Menu Mode with menuMode or MM.
      Expected: Mode is not changed. Error message is shown in the Results Display.

    2. In any other mode, switch to Menu Mode with menuMode or MM.
      Expected: Status bar indicates "Menu Mode". Success message is shown in the Results Display. Main panel will be empty and side panel will display a list of menu items with their code, name and price.

  3. Table Mode

    1. Pre-requisite: Tables should have been populated with sample data. Table 1 is unoccupied while Tables 2 and 3 are occupied.

    2. In Table Mode for Table 2, switch to Table Mode with tableMode 2 or TM 2.
      Expected: Mode is not changed. Error message is shown in the Results Display.

    3. In Table Mode for Table 2 or in any other mode, switch to Table Mode with tableMode 3 or TM 3.
      Expected: Status bar indicates "Table Mode: Table 3". Success message is shown in the Results Display. Main panel will display a grid of menu items with their code, name and price and side panel will display a list of order items from Table 3.

    4. Test case for unoccupied table: In any mode, switch to Table Mode with tableMode 1 or TM 1.
      Expected: Mode is not changed. Error message is shown in the Results Display.

    5. Test case for invalid table number: In any mode, switch to Table Mode with tableMode 100 or TM 100.
      Expected: Mode is not changed. Error message is shown in the Results Display.

  4. Statistics Mode

    1. In Statistics Mode, switch to Statistics Mode with statisticsMode or SM.
      Expected: Mode is not changed. Error message is shown in the Results Display.

    2. In any other mode, switch to Statistics Mode with statisticsMode or SM.
      Expected: Status bar indicates "Statistics Mode". Success message is shown in the Results Display. Main panel will display a grid of dates with their revenues and side panel will be a list of menu items sorted according to its popularity.

F.3. Tables feature

You can restore the original sample data by deleting the json files in the data folder and restarting the application.

  1. Adding a table

    1. Switch to Restaurant Mode (restaurantMode).

    2. Test case: addTable 1
      Expected: Adds a table with table status of 0/1.

    3. Test case: addTable
      Expected: No tables are added. Error message is shown in the Results Display. Status bar remains the same.

    4. Test case: addTable 4 4 8
      Expected: Adds 3 tables with table statuses 0/4, 0/4 and 0/8 for tables 1, 2 and 3 respectively.

    5. Test case: addTable @
      Expected: No tables are added. Error message is shown in the Results Display.

    6. Test case: addTable 1 2 3 $
      Expected: No tables are added. Error message is shown in the Results Display.

  2. Editing pax in a table

    1. Switch to Restaurant Mode (restaurantMode).

    2. Test case: editPax 2 2
      Expected: Edits Table 2 to have 2 customers.

    3. Test case: editPax @ 2
      Expected: No tables are edited. Error message is shown in the Results Display.

    4. Test case: editPax 2 @
      Expected: No tables are edited. Error message is shown in the Results Display.

  3. Editing seats in a table

    1. Switch to Restaurant Mode (restaurantMode).

    2. Test case: editSeats 2 4
      Expected: Edits Table 2 to have 4 seats.

    3. Test case: editSeats @ 2
      Expected: No tables are edited. Error message is shown in the Results Display.

    4. Test case: editSeats 2 @
      Expected: No tables are edited. Error message is shown in the Results Display.

You should restore all data in between every test for this section. After restoring, use editPax command to remove all customers from all the tables.

  1. Clear tables

    1. Switch to Restaurant Mode (restaurantMode).

    2. Test case: clearTables
      Expected: All tables have been cleared.

    3. Test case: clearTables 2
      Expected: All tables have been cleared.

    4. Test case: clearTables @
      Expected: All tables have been cleared

  2. Space for specified number of customers

    1. Test case: spaceFor 3
      Expected: A table has been allocated for the customers.

    2. Test case: spaceFor @
      Expected: No tables have been allocated. Error message is shown in the Results Display.

You can restore the original sample data by deleting the json files in the data folder and restarting the application.

  1. Adding to the menu

    1. Switch to Menu Mode (menuMode).

    2. For the following test cases, addToMenu can be replaced with the shortcut add.

    3. Test case: addToMenu c/W09 n/Chicken Wings p/4.50
      Expected: Adds a menu item "Chicken Wings" with code W09 and price 4.50 to the menu.

    4. Test case: addToMenu n/French Fries c/W12 p/2.50
      Expected: Adds a menu item "French Fries" with code W12 and price 2.50 to the menu.

    5. Test case: addToMenu p/4.00 n/Beef Burger c/W04
      Expected: Adds a menu item "Beef Burger" with code W04 and price 4.00 to the menu.

    6. Test case for invalid name format: addToMenu n/N@me c/W12 p/2.50
      Expected: No menu items are added. Error message is shown in the Results Display.

    7. Test case for invalid code format: addToMenu n/French Fries c/102W p/2.50
      Expected: Similar to previous.

    8. Test case for invalid price format: addToMenu n/French Fries c/W12 p/2.505
      Expected: Similar to previous.

  2. Deleting from the menu

    1. Switch to Menu Mode (menuMode).

    2. For the following tst cases, deleteFromMenu can be replaced with the shortcut del.

    3. Test case: deleteFromMenu W09
      Expected: Deletes the menu item "Chicken Wings" with code W09 from the menu.

    4. Test case for invalid code format: deleteFromMenu 102W
      Expected: No items are deleted from the menu. Error message is shown in the Results Display.

    5. Test case for item does not exist in the menu: deleteFromMenu A01
      Expected: Similar to previous.

  3. Clearing the menu

    1. Switch to Menu Mode (menuMode).

    2. Test case: clearMenu or clear Expected: All the menu items are deleted.

F.5. Orders feature

Pre-requisite: Tables and menu items should have been populated with sample data in order for the test cases below to work. You can restore the original sample data by deleting the json files in the data folder and restarting the application.

  1. Adding to an order

    1. Switch to Table Mode for Table 10 (tableMode 10).

    2. For the following test cases, addToOrder can be replaced with the shortcut add.

    3. Test case: addToOrder W09 2
      Expected: Adds an order item "W09 Chicken Wings" with quantity of 2/2 to Table 10’s order.

    4. Test case: addToOrder W12 2 W13 1
      Expected: Adds order items "W12 French Fries" with quantity of 2/2 and "W13 Coke" with quantity of 1/1 to Table 10’s order.

    5. Test case: addToOrder W09 1
      Expected: Updates the order item "W09 Chicken Wings" in Table 10’s order from quantity of 2/2 to quantity of 3/3.

    6. Test case for invalid command format: addToOrder W09
      Expected: No order items are added. Error message is shown in the Results Display.

    7. Test case for invalid item code format: addToOrder 2A 2
      Expected: Similar to previous.

    8. Test case for item code not in menu: addToOrder W35 2
      Expected: Similar to previous.

    9. Test case for invalid quantity: addToOrder W09 3000000000
      Expected: Similar to previous.

    10. Other invalid test cases: addToOrder, addToOrder 3, addToOrder W99 1, addToOrder W09 1 W12, addToOrder W09 -2

  2. Deleting from an order

    1. Switch to Table Mode for Table 22 (tableMode 22).

    2. For the following test cases, deleteFromOrder can be replaced with the shortcut del.

    3. Test case: deleteFromOrder W09
      Expected: Deletes the order item "W09 Chicken Wings" from Table 22’s order.

    4. Test case for invalid command format: deleteFromOrder W09 3
      Expected: No order items are deleted. Error message is shown in the Results Display.

    5. Test case for invalid item code format: deleteFromOrder 2A
      Expected: Similar to previous.

    6. Test case for item code not in order: deleteFromOrder W14
      Expected: Similar to previous.

    7. Other invalid test cases: deleteFromOrder, deleteFromOrder W01, deleteFromOrder W35

  3. Clearing an order

    1. Switch to Table Mode for Table 5 (tableMode 5).

    2. Test case: clearOrder or clear
      Expected: All the order items from Table 5’s order are deleted.

  4. Serving items in an order

    1. Switch to Table Mode for Table 24 (tableMode 24).

    2. For the following test cases, serve can be replaced with the shortcut s.

    3. Test case: serve W01 3
      Expected: Updates the order item "W01 Chicken Burger" in Table 24’s order from quantity of 3/3 to quantity of 0/3.

    4. Test case: serve W12
      Expected: Updates the order item "W12 French Fries" in Table 24’s order from quantity of 3/3 to quantity of 2/3.

    5. Test case for invalid item code format: serve 2A
      Expected: No order items are updated. Error message is shown in the Results Display.

    6. Test case for item code not in order: serve W02
      Expected: Similar to previous.

    7. Test case for item already fully served: serve W01 2
      Expected: Similar to previous.

    8. Other invalid test cases: serve, serve W12 4, serve W03 2

F.6. Statistics feature

  1. Billing an order

    1. Switch to Table Mode of the selected table. Orders should have been populated and served from the Order tests above.

    2. Test case: bill or b
      Expected: Mode change to Bill Mode where a receipt of the orders will be shown. The total bill is calculated and recorded. If Bill is the first bill of the day, a new daily revenue card will appear in the default display of Statistics Mode. Otherwise, it just updates the daily revenue by adding the total bill to the current revenue of the current date. The order list and occupancy of the selected table should be cleared.

    3. Switch to Table Mode of the selected table. Orders should have been populated from the Order tests above. Some of the order items are not served.

    4. Test case: bill or b
      Expected: Error message is shown in Results Display.

  2. Retrieving revenue for a specified day, month or year

    1. Switch to Statistics Mode. Daily revenue should have been populated from the Bill test.

    2. Test case: revenue or r
      Expected: Revenue of the current date will be calculated.

    3. Test case: revenue y/2019 m/1 d/1 or r y/2019 m/1 d/1
      Expected: Revenue of 1-Jan-2019 will be calculated.

    4. Test case: revenue y/2019 m/1 or r y/2019 m/1
      Expected: Revenue of Jan-2019 will be calculated.

    5. Test case: revenue y/2019 or y/2019
      Expected: Revenue of year 2019 will be calculated.

    6. Test case: revenue d/1 y/2019 or r d/1 y/2019
      Expected: No revenue are calculated. Error message is shown in the Results Display.

    7. Other invalid test cases: revenue m/1, revenue d/1, revenue m/1 d/1, revenue y/2019 m/2 d/29

F.7. Data storage

  1. Missing data files

    1. Open the data folder inside your RestOrRant home folder and delete one or more of the json files.

    2. Launch RestOrRant by double-clicking the jar file.
      Expected: Shows the GUI with sample data restored.

  2. Corrupted data files

    1. Open any of the json files in the data folder and delete all its contents.

    2. Launch RestOrRant by double-clicking the jar file.
      Expected: Shows the GUI with no data.