Skip to content

Latest commit

 

History

History
2352 lines (1689 loc) · 98.4 KB

DeveloperGuide.adoc

File metadata and controls

2352 lines (1689 loc) · 98.4 KB

Inventory Manager - Developer Guide

By: Team W14-4      Since: August 2018      Licence: MIT

1. Setting Up

The steps for developers to setup Inventory Manager locally is described below.

1.1. Prerequisites

The prerequisites for setting up Inventory Manager is described below.

  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.

1.2. Setting up the project in your computer

Begin setting up Inventory Manager 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 where the JDK is stored.

  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 XmlAdaptedItem.java and 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 the keys:[ALT + ENTER], and select Add '--add-modules=…​' to module compiler options for each error.

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

1.3. Verifying the setup

After setting up, you can verify if Inventory Manager is setup correctly by performing the steps below.

  1. Run the seedu.inventory.MainApp and try a few commands (e.g. help or list).

  2. Run the tests to ensure all tests pass.

1.4. Configuring the setup

You are encouraged to customise your setup in your local computer by following the steps below.

1.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, please follow the steps described below.

  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.

1.4.2. Updating documentation to match your fork

After forking the repo, the documentation will still have the SE-EDU branding and reference 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.

1.4.3. Setting up Continuous Integration

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 may 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).

1.4.4. Getting started with coding

Below you start coding, do take note of the below two points.

  1. Get some sense of the overall design by reading Section 2.1, “Architecture”.

  2. Take a look at Appendix A, Suggested Programming Tasks to Get Started.

2. Design

This section describes and explains the design and structure behind the App, as well as all the core components that form the entire structure.

2.1. Architecture

Architecture
Figure 1. Architecture Diagram

The Architecture Diagram given above explains the high-level design of the application. There are a total of 6 major components: Main, Commons, UI, Logic, Model, and Storage. Given below is a quick overview of each component.

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

Main has only one class called MainApp. It is responsible for:

  • At app launch: Initializes the components in the correct sequence, and connects them up with each other.

  • At shut down: Shuts down the components and invokes cleanup method where necessary.

Commons represents a collection of classes used by multiple other components. Classes used by multiple components are in the seedu.inventory.commons package.

Two of those classes play important roles at the architecture level:

  • EventsCenter : This class (written using Google’s Event Bus library) is used by components to communicate with other components using events (i.e. a form of Event Driven design)

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

UI consists of the different parts that make up the User Interface (UI) of the App.

Logic is the App’s command executor. It takes care of parsing, executing and processing commands entered by the users.

Model holds the data of the App in-memory.

Storage reads data from, and writes data to, the hard disk. It is responsible for storing data used by the App.

Each of the four components Ui, Logic, Model, and Storage:

  • 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 it’s API in the Logic.java interface and exposes its functionality using the LogicManager.java class.

LogicClassDiagram
Figure 2. Class Diagram of the Logic Component

Events-Driven nature of the design

The Sequence Diagram below shows how the components interact for the scenario where the user issues the command delete-item 1.

SDforDeletePerson
Figure 3. Component interactions for delete-item 1 command (part 1)
ℹ️
Note how the Model simply raises an InventoryChangedEvent when the Inventory List data are changed, instead of asking the Storage to save the updates to the hard disk.

The diagram below shows how the EventsCenter reacts to that event, which eventually results in the updates being saved to the hard disk and the status bar of the UI being updated to reflect the 'Last Updated' time.

SDforDeletePersonEventHandling
Figure 4. Component interactions for delete 1 command (part 2)
ℹ️
Note how the event is propagated through the EventsCenter to the Storage and UI without Model having to be coupled to either of them. This is an example of how this Event Driven approach helps us reduce direct coupling between components.

The sections below give more details of the UI, Logic, Model, Storage components.

2.2. UI component

UiClassDiagram
Figure 5. Structure of the UI Component

API : Ui.java

The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, ItemListPanel, StatusBarFooter, BrowserPanel etc. All these, including the MainWindow, inherit from the abstract UiPart class.

The UI component uses JavaFx UI framework. The layout of these UI parts are defined in matching .fxml files that are in the 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.

  • Binds itself to some data in the Model so that the UI can auto-update when data in the Model change.

  • Responds to events raised from various parts of the App and updates the UI accordingly.

2.3. Logic component

LogicClassDiagram
Figure 6. Structure of the Logic Component

API : Logic.java

  1. Logic uses the InventoryParser class to parse the user command.

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

  3. The command execution can affect the Model (e.g. adding a item) and/or raise events.

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

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

DeleteItemSdForLogic
Figure 7. Interactions Inside the Logic Component for the delete 1 Command

2.4. Model component

ModelClassDiagram
Figure 8. Structure of the Model Component

API : Model.java

The Model,

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

  • stores the Inventory Manager data.

  • exposes an unmodifiable ObservableList<Item> 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.

ℹ️
As a more OOP model, we can store a Tag list in Inventory, which Item can reference. This would allow Inventory to only require one Tag object per unique Tag, instead of each Item needing their own Tag object. An example of how such a model may look like is given below.

ModelClassBetterOopDiagram

2.5. Storage component

StorageClassDiagram
Figure 9. Structure of the Storage Component

API : Storage.java

The Storage component,

  • can save UserPref objects in json format and read it back.

  • can save the Inventory Manager data in xml format and read it back.

3. Implementation

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

3.1. Undo/Redo feature

3.1.1. Current implementation

The undo/redo mechanism is facilitated by VersionedInventory. It extends Inventory with an undo/redo history, stored internally as an inventoryStateList and currentStatePointer. Additionally, it implements the following operations:

  • VersionedInventory#commit() — Saves the current inventory state in its history.

  • VersionedInventory#undo() — Restores the previous inventory state from its history.

  • VersionedInventory#redo() — Restores a previously undone inventory state from its history.

These operations are exposed in the Model interface as Model#commitInventory(), Model#undoInventory() and Model#redoInventory() respectively.

Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.

Step 1. The user launches the application for the first time. The VersionedInventory will be initialized with the initial state, and the currentStatePointer pointing to that single state.

UndoRedoStartingStateListDiagram

Step 2. The user executes delete-item 5 command to delete the 5th item in Inventory Manager. The delete command calls Model#commitInventory(), causing the modified state of Inventory Manager after the delete-item 5 command executes to be saved in the InventoryStateList, and the currentStatePointer is shifted to the newly inserted Inventory Manager state.

UndoRedoNewCommand1StateListDiagram

Step 3. The user executes add-item n/iPhone …​ to add a new item. The add command also calls Model#commitInventory(), causing another modified inventory state to be saved into the inventoryStateList.

UndoRedoNewCommand2StateListDiagram
ℹ️
If a command fails its execution, it will not call Model#commitInventory(), so the inventory state will not be saved into the inventoryStateList.

Step 4. The user now decides that adding the item was a mistake, and decides to undo that action by executing the undo command. The undo command will call Model#undoInventory(), which will shift the currentStatePointer once to the left, pointing it to the previous inventory state, and restores the inventory to that state.

UndoRedoExecuteUndoStateListDiagram
ℹ️
If the currentStatePointer is at index 0, pointing to the initial inventory state, then there is no previous inventory state to restore. The undo command uses Model#canUndoInventory() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo.

The following sequence diagram shows how the undo operation works:

UndoRedoSequenceDiagram
Figure 10. A high level sequence diagram showing events triggered by the undo command.

The redo command does the opposite — it calls Model#redoInventory(), which shifts the currentStatePointer once to the right, pointing to the previously undone state, and restores the inventory to that state.

ℹ️
If the currentStatePointer is at index inventoryStateList.size() - 1, pointing to the latest inventory state, then there is no undone inventory state to restore. The redo command uses Model#canRedoAInventory() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.

Step 5. The user then decides to execute the command list-item. Commands that do not modify Inventory Manager, such as list-item, will usually not call Model#commitInventory(), Model#undoInventory() or Model#redoInventory(). Thus, the inventoryStateList remains unchanged.

UndoRedoNewCommand3StateListDiagram

Step 6. The user executes clear, which calls Model#commitInventory(). Since the currentStatePointer is not pointing at the end of the inventoryStateList, all inventory states after the currentStatePointer will be purged. We designed it this way because it no longer makes sense to redo the add-item n/iPhone …​ command. This is the behavior that most modern desktop applications follow.

UndoRedoNewCommand4StateListDiagram

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

UndoRedoActivityDiagram
Figure 11. State diagram when a user executes a command

3.1.2. Design considerations

Aspect: How undo & redo executes
  • Alternative 1 (current choice): Saves the entire inventory.

    • Pros: Easy to implement.

    • Cons: May have performance issues in terms of memory usage.

  • Alternative 2: Individual command knows how to undo/redo by itself.

    • Pros: Will use less memory (e.g. for delete, just save the item being deleted).

    • Cons: We must ensure that the implementation of each individual command are correct.

Aspect: Data structure to support the undo/redo commands
  • Alternative 1 (current choice): Use a list to store the history of inventory states.

    • Pros: Easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project.

    • Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both HistoryManager and VersionedInventory.

  • Alternative 2: Use HistoryManager for undo/redo

    • Pros: We do not need to maintain a separate list, and just reuse what is already in the codebase.

    • Cons: Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as HistoryManager now needs to do two different things.

3.2. Inventory management

Inventory management is introduced in Inventory manager as a core feature to allow users to easily manage items in their inventory list.

An Item consists of Name, Price, Quantity, unique SKU, Image Path, and optional Tags. An item is considered unique based on its SKU.

An item can only be successfully added into the inventory if the following criteria are met:

  • Name contains only alphanumeric characters

  • Price contains only positive whole numbers or decimals

  • Quantity contains only positive whole numbers

  • SKU is unique and contains only alphanumeric characters and the dash '-' or underscore '_' symbols

  • Image Path is a valid file path and the file must be a valid image (i.e. either .png or .jpeg format)

3.2.1. Model component

Items are stored and maintained in the Model in a UniqueItemList, allowing only unique items to be stored in the inventory.

UniqueItemList
Figure 12. UML diagram showing a representation of UniqueItemList in Inventory Manager.

Individual items are represented by the Item object with attributes Name, Price, Quantity, SKU, Image and Tag.

3.2.2. Storage component

Items are stored in XmlAdaptedItem objects, and physically stored in XML files. An item is stored in an XML file in the following format:

<inventory>
    <items>
        <name>iPhone XR</name>
        <price>1500.00</price>
        <quantity>30</quantity>
        <sku>apple-iphone-xr</sku>
        <image>/images/iphone.jpg</image>
    </items>
</inventory>

3.2.3. UI component

The main window contains a ListPanel, which can be used interchangeably to display any lists based on the command entered by the user.

Inventory list is displayed in the ListPanel upon executing the list-item command, and individual items are represented using ItemCard. The items' attribute fields uses Label.

An example of the ItemCard in Inventory Manager UI is shown in Figure 11 below:

ItemCard
Figure 13. Example of the an item card in Inventory Manager UI.

3.2.4. Logic component

The following commands were added and/or modified to handle inventory management operations:

  • list-item : List all items in the inventory

  • list-low-qty : List all items with quantities less than or equal to 10 in the inventory

  • find-item-sku : Search for items based on SKU

  • filter-item : Filter items based on quantity and/or price

  • add-item : Add a unique item into the inventory

  • delete-item : Delete an existing item in the inventory

  • edit-item : Edit an existing item in the inventory

Operations performed on items follow the sequence of operations as described below:

  1. Command input is received by the UI component.

  2. The command execution is handled by the Logic component.

  3. The changes to data are effected by the Model component.

  4. The new Inventory Manager data is saved by the Storage component.

This sequence of events is summarized in the following sequence diagram:

DeleteItemSdForLogic
Figure 14. A high level sequence diagram showing events triggered by the delete-item command.

3.2.5. Design considerations

Aspect: Where updateItem is executed
There are various places where updateItem can be executed. The pros and cons for each option is shown below.

Alternative 1 (current choice): Executed at Logic
Pros: Easy to implement.
Cons: Direct calling of Model.updateItem() will not be validated.

Alternative 2: Executed at Model
Pros: Ensures that all entries are validated before item can be edited.
Cons:Model.updateItem() will need to access inventory to perform validation.

Based on the above consideration, the first option is chosen as it is easier to implement, and it does not require Model.updateItem() to read the inventory and perform validation.

3.2.6. Implementation details for validation of fields

SKU field

Since an item is considered unique based on its SKU, it is important to validate that an item has a unique SKU before it is allowed to be added or edited in the inventory.

Before an item is allowed to be added or edited, it is checked against every item in the inventory list. There are two checks to verify if two items are the same: 1. comparing the two items against every field, and 2. comparing only SKU.

The following code snippet shows how the item is checked for uniqueness:

public boolean isSameItem(Item otherItem) {
    if (otherItem == this) {
        return true;
    }

    return otherItem != null
            && otherItem.getSku().equals(getSku());
}
Image field

Inventory Manager supports images for items, therefore, it is important to validate that images supplied by users are stored in a valid file path, and that the image is a valid image file.

Inventory Manager checks if the directory supplied by the user is a valid file path. Then, the file’s mime-type will be verified to ensure that it is a valid image file.

The following code snippet shows how the image URL is checked and validated:

public static boolean isValidImage(String test) {
    if (test.matches(IMAGE_VALIDATION_REGEX)) {
        File file = new File(test);

        if (file.exists()) {
            try {
                String mimeType = Files.probeContentType(file.toPath());

                return (mimeType != null && mimeType.split("/")[0].equals("image"));
            } catch (IOException e) {
                e.printStackTrace();

                return false;
            }
        } else {
            return (Image.class.getResource(test) != null && (test.endsWith("png") || test.endsWith("jpg")));
        }
    } else {
        return false;
    }
}

3.3. Sale order creation

The sale order creation command was developed to allow users to easily deduct item quantity from the inventory.

Creating a sale order requires the item SKU and the quantity that has been sold to be entered. The sale ID and sale date will then be automatically generated by the system.

Creating a new sale requires SaleID, Item, Quantity and SaleDate.

3.3.1. Implementation

The creation of sale order is handled by the AddSaleCommand. The use of it will trigger Model#updateItem, which will then update the item’s quantity. After Model#updateItem has complete, the sale order will be stored into the XML file.

Conditions

For AddSaleCommand#execute to be successful, there are two conditions which needs to be met. The following two conditions are:

  1. The item must exists in Inventory Manager.

  2. The available item quantity must be more than or equal to the quantity sold.

AddSaleCommand#execute will perform the validation before proceeding. If any of the above validation fails, CommandException will be thrown and it will not call Model#updateItem.

Logic

The sequence diagram below shows the interactions inside the logic component for the AddSaleCommand.

AddSaleCommandSequenceDiagram
Figure 15. Sequence diagram for the add sale command.
Storage

The sale orders created are stored into a XML file, named sale.xml, in the data folder. Below shows an example of how each sale is stored in the XML file.

<sale>
    <saleId>1</saleId>
    <saleSku>apple-iphone-xr</saleSku>
    <saleQuantity>1</saleQuantity>
    <saleDate>2018-08-01</saleDate>
</sale>

Each sale created is represented as a sale element in the XML file as seen above. Each tag represents information for the sale order, which is further explained below.

  • saleID - This represents the sale ID of the created sale order.

  • saleSku - This represents the sold item SKU.

  • saleQuantity - This represents the sold quantity for the created sale order.

  • saleDate - This represents the date the sale order was created.

On starting Inventory Manager, the XML file will be loaded, and each sale will be validated for corruption before being loaded into memory.

3.3.2. Design consideration

There are a few design considerations when creating a sale order. Below will explain the various considerations and the choices made.

Aspect: Where updateItem executes

There are various places where updateItem can be executed. The pros and cons for each option is shown below.

  1. Executed at Logic

    • Pros: Easy to implement

    • Cons: Direct calling of Model#addSale will not be validated.

  2. Executed at Model

    • Pros: Ensures that all entries are validated before sale can be created.

    • Cons: Model#addSale will need to access inventory to perform validations.

Based on the above pros and cons, the first option is chosen as it is easier to implement, and it does not require Model#addSale to read the inventory and perform validations.

3.4. 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 3.5, “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

3.5. Configuration

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

3.6. Authentication

Authentication is introduced in Inventory Manager which allows the users to authenticate themselves to perform their inventory management tasks. This is to prevent any unauthorized users from accessing the system.

3.6.1. Implementation

The authentication of the user is handled by LoginCommand. The use of it will trigger Model#authenticateUser, which will authenticate the user in the Inventory Manager. However, for LoginCommand#execute to be successful, there are two conditions which need to be met.

Conditions

The following two conditions must be met before executing:

  1. The current user who is executing the command must be providing a valid username and password.

  2. The user must have an account exists in the system.

LoginCommand#execute will perform the validation before proceeding. If any of the above validation fails, CommandException will be thrown and it will not call Model#authenticateUser.

3.6.2. Design consideration

There are a few design considerations when authenticating a staff. Below will explain the various considerations and the choices made.

Aspect: Where login executes

There are various places where authenticateUser can be executed. The pros and cons of each option are shown below.

Alternative 1 (current choice): Executed at Logic
Pros: Easy to implement.
Cons: Direct calling of Model.authenticateUser() will not be validated.

Alternative 2: Executed as an Event
Pros: Able to be used by any component of the application.
Cons: AuthenticateUserEvent.execute() will need to access the staff list to perform validations.

Based on the above pros and cons, the first option is chosen as it is easier to implement, and it does not require AuthenticateUserEvent.execute() to read the inventory and perform validations.

Logic component

The following commands are added for Authentication:

  • login : Authenticates a staff

  • change-password : Changes the current user’s password

  • logout : Logout a staff and clears all the command history

The sequence diagram below shows how login is executed at the logic component.

LoginSDForLogic
Figure 16. This UML diagram shows how login is executed and works at the logic component.
Model component

Authentication has a UserSession object stores in the Model Component of the system. This is due to this component has to be largely relied on when the commands are executed.

Newly created user session object comes with a Staff object to store the current user’s information.

The class diagram below shows the relationship between each object used in authentication feature.

AuthenticationModelComponentClassDiagram
Figure 17. This UML diagram shows the UserSession object stores a Staff object and is stored in ModelManager.

3.7. User management

Editing a user will have the staff’s account information updated. Below will describe how this feature is implemented in the Inventory Manager.

3.7.1. Implementation

The update of the user is handled by EditStaffCommand. The use of it will trigger Model#updateStaff, which will edit the user in the Inventory Manager. However, for EditStaffCommand#execute to be successful, there are two conditions which need to be met.

Conditions

The following two conditions must be met before:

  1. Ther current user who is executing the command must be a user with admin role.

  2. The index of the staff must be a valid number and existing in the staff list shown.

EditStaffCommand#execute will perform the validation before proceeding. If any of the above validation fails, CommandException will be thrown and it will not call Model#updateStaff.

3.7.2. Design consideration

There are a few design considerations when editing a staff. Below will explain the various considerations and the choices made.

Aspect: Where edit-staff executes

There are various places where edit-staff can be executed. The pros and cons of each option are shown below.

  1. Executed at Logic

    • Pros: Easy to implement

    • Cons: The direct calling of Model#editStaff will not be validated.

  2. Executed at Model

    • Pros: Ensures that all entries are validated before staff can be updated.

    • Cons: Model#editStaff will need to access the staff list to perform validations.

Based on the above pros and cons, the first option is chosen as it is easier to implement, and it does not require Model#editStaff to read the inventory and perform validations.

Logic component

The following commands are added for User Management:

  • add-staff : Add a staff

  • list-staff : List all staffs

  • edit-staff : Edit a staff

  • delete-staff : Delete a staff

The sequence diagram below shows how edit-staff is executed at the logic component.

EditStaffSDForLogic
Figure 18. This UML diagram shows how edit-staff is executed and works at the logic component.
Model component

Staffs are managed in the Model as a UniqueStaffList which disallows duplicated staff to be added.

The class diagram shows the relationship of the objects involved in the user management feature.

StaffModelComponentClassDiagram
Figure 19. This UML diagram shows the representation of Staff and UniqueStaffList in Inventory Manager.
Storage component

Staffs are stored as XmlAdaptedStaff objects in XMLSerializableStaffList, which maintain a class relationship as shown in this diagram.

StaffStorageComponentClassDiagram
Figure 20. This UML diagram shows Staff objects are saved using XmlAdaptedStaff objects into a XMLSerializableStaffList as shown above.

An example staff stored in XML format is reproduced below:

<staffs>
    <username>johndoe</username>
    <password>5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8</password>
    <name>John Doe</name>
    <role>user</role>
</staffs>
ℹ️
Passwords are stored in hashed format using SHA-256 encoding.

3.8. Export CSV mechanism

In order to enable users to have further analysis of the data in Inventory Manager, we support several export-csv commands to export the data in Inventory Manager to CSV files. We are using export-csv-items, export-csv-sales, export-csv-staffs, export-csv-orders to support exporting of Inventory Manager data through CSV files.

In the following sections, we will focus on how these commands depend on the Model and Storage component to retrieve external data and where to validate the given file path.

3.8.1. Implementation

Data access

In order to achieve their tasks, all export commands require direct access to the data and output to the external environment. Unlike other commands, these commands highly depend on the Storage component. However, if we only directly set a Storage for the command and manipulate on the Storage in export command, we also need Storage component to directly access the data in Model component which will violate Single Responsibility Principle.

So we decide to use the same method as other command use to export data. When executing the export commands, a DataExportEvent will be raised in the EventsCenter through Model component. Since Logic directly has Model as one field, it will not violate the Single Responsibility Principle. Then, the handler function in Storage component will catch this DataExportingEvent and export data to the external environment.

File path validation

File paths, as required by export commands, need to be validated during the execution of the commands. However, where to implement it can be decided. It can be implemented in CommandParser or Command or even in Storage.

In our implementation, we choose to validate the file path during the execution of export Command. A static method in FileUtil will be called to test whether the given file path is valid when executing the command. In doing so, we are able to make sure that the path parameter passed to Model component and Storage component is a valid file path. Also, it is easier for the implementation of command feedback to the users. Further details can be found in the sequence diagram of the next section.

Sequence diagram

Considering that all the export commands have similar sequence diagrams. So we will just show the sequence diagrams for export-csv-items command to illustrate how the export commands work.

The Sequence Diagram below shows how the components interact for the scenario where the user issues the command export-csv-items f/items.csv.

SDforExportCsvItem
Figure 21. Component interactions for export-csv-items f/items.csv command (part 1)

The diagram below shows how the EventsCenter reacts to that event, which eventually results in the item list being exported and the alert window of the UI being popped up to reflect the success of exporting.

SDforExportCsvItemEventHandling
Figure 22. Component interactions for export-csv-items f/items.csv command (part 2)

The diagram below is the lower level sequence diagram for export-csv-items f/items.csv command inside Logic component.

ExportCsvItemCommandSequenceDiagram
Figure 23. Lower level sequence diagram for export-csv-items f/items.csv command inside Logic component

3.8.2. Design considerations

Aspect: How export commands interact with Storage component and Logic component
  • Alternative 1 (current choice): Raises a DataExportEvent in the EventsCenter through Model and handles it in Storage.

    • Pros: Avoids direct access of Storage from export commands. Does not violate the Single Responsibility Principle.

    • Cons: Needs a middle Component to achieve the goal. Needs to raise an additional event.

  • Alternative 2: Couples Command with Storage

    • Pros: Provides direct access to Storage for commands that is highly depend it.

    • Cons: Needs directly access of data in Model component for Storage which will violate the Single Responsibility Principle.

Aspect: Location for implementation of file path validation and location to check file path validation
  • Alternative 1 (current choice): Implements in FileUtil and checks in export command.

    • Pros: It’s sure that the path passed to Model component and Storage component is a valid file path. The implementation of command feedback is easier. File Validation is reusable in other component.

    • Cons: File Validation needs to interact with Commons component.

  • Alternative 2: Implements in CommandParser and checks in CommandParser class.

    • Pros: There is no need to interact with other component.

    • Cons: File Validation is not reusable in other component.

  • Alternative 3: Implements in Storage and checks in Storage.

    • Pros: There is no need to interact with other component. File Validation is reusable in other method of Storage.

    • Cons: The path passed to Storage component may not be valid. The implementation of command feedback is more difficult. Storage needs to raise an additional event for invalid file path.

3.8.3. CSV file recognizing

Since Csv file do not have standardized format for storing objects. So we designed a format that need to be followed when importing csv files. If the process of importing fails due to unrecognizable failure, do check the file to import meets the requirement.

An example of recognizable csv file storing the items is reproduced below: (Opened with a Plain Text Editor,not Excel)

Item,,,,,
name,price,quantity,sku,image,tags
iPhone XR,1500.00,30,apple-iphone-xr,/images/iphone.jpg,"apple,iphone"
LG G7,1250.50,90,lg-g7,/images/lg.jpg,"smartphone,lg"
Samsung S9,1499.99,14,samsung-s9,/images/samsung.jpg,"samsung,smartphone"
HTC U6 ,999.00,88,htc-u6,/images/htc.jpg,"samsung,phablet"
Google Pixel XL,1435.90,3,google-pixel-xl,/images/google.jpg,google

The first two lines are headers to determine which kind of object is this csv file storing. Following lines are the details of the objects. Empty lines are not allowed. If a cell contains commas(,), it must be surrounded by double quotation marks(" ").

The required format can be gotten by simply exporting each kind of objects first.

3.9. Purchase order management

Purchase order management is introduced in Inventory manager to allow user to restock item easily and manage the purchase order details efficiently.

The purchase order details includes the item’s SKU, restock quantity, required date and supplier.
Purchase order can only be added if it matches the item’s SKU. Newly added purchase order comes with a default PENDING status which subsequently can be edited, approved, rejected and deleted.

3.9.1. Implementation

Model component

Purchase orders are maintained in the Model in a NonUniquePurchaseOrderList, allowing non unique purchase order among the stored individual purchase orders. The UML diagram is as shown in the figure below.

PoModel1
Figure 24. UML diagram showing representation of NonUniquePurchaseOrderList in Inventory Manager.

Individual purchase order are represented by the PurchaseOrder object with attributes Sku, Quantity, RequiredDate and Supplier. The class relationship is shown in Figure 11 below.

PoModel2
Figure 25. UML diagram showing representation of Purchase Order in Inventory Manager.
Storage component

Purchase orders are stored in XmlAdaptedPurchaseOrder objects, which maintain a class relationship as shown in the diagram below:

PoStorage
Figure 26. PurchaseOrder objects are saved using XmlAdaptedPurchaseOrder objects as shown above.

To save the XmlAdaptedPurchaseOrder objects created, it is stored in a plain text file following the XML format.
An example purchase order stored in XML format is reproduced below:

<purchaseOrders>
                <sku>apple-iphone-xr</sku>
                <quantity>1000</quantity>
                <reqDate>2018-12-12</reqDate>
                <supplier>Apple Inc.</supplier>
                <status>APPROVED</status>
</purchaseOrders>
UI component

The main window contains a ListPanel, which is interchangeable to display any list based on the command entered. Purchase order list is shown in the ListPanel upon executing list-po. Individual purchase orders are represented using PurchaseOrderCards that contains the attributes fields using labels and flowPanes.

The diagram below shows how purchase order panel is integrated in the Ui class diagram.

PoUi
Figure 27. Example of the a purchase order card in Inventory Manager UI.
Logic component

The following commands were added to handle the purchase order:

  • list-po : List all purchase orders

  • add-po : Add a purchase order

  • delete-po : Delete a purchase order

  • approve-po : Approve a purchase order

  • reject-po : Reject a purchase order

  • edit-po : Edit a purchase order

The following diagram details the class relationship for the Purchase order Commands.

PoCommand
Figure 28. UML Diagram depicting the class relationship between command classes.

Operations performed on purchase orders follow the sequence of operations described in the diagram below.

  1. Command input is received by the UI component.

  2. The command execution is handled by the Logic component.

  3. The changes to data are effected by the Model component.

  4. The new Inventory Manager data is saved by the Storage component.

This sequence of events is summarized in the following sequence diagrams:

PoList
Figure 29. A high level sequence diagram showing events triggered by the list-po command.
PoAdd
Figure 30. A high level sequence diagram showing events triggered by the add-po command.

3.9.2. Design considerations

Aspect: Implementation of purchase order commands that requires the item to be updated.
Commands such as approve-po will update the item quantity upon approval. Consequently, there are various places where updateItem can be executed. The pros and cons for each option is shown below.

Alternative 1 (current choice): Executed at Logic
Pros: Allows the user to get the item list from the model directly; less hassle.
Cons: Complicates the test cases as everything is done in one method.

Alternative 2: Executed at Model
Pros: Allows the user to call the item directly from the list that is stored in the model.
Cons: Requires additional method in the model which in turns requires to update 4 relevant model files.

Based on the above pros and cons, the first option is chosen as it is easier to implement and only requires 1 logic file to be edited.


Aspect: Implementation of purchase order storage
There are various design on how to integrate purchase order in the existing storage system. The pros and cons for each option is shown below.

Alternative 1 (current choice): Stored in the same file as the item but with different xml tag
Pros: Eases the implementation by adding in relevant purchase order detail in the existing storage implementation.
Cons: Disorganizes the storage file as all the different data is clunked together

Alternative 2: Created a whole new standalone storage for purchase order
Pros: Organizes the data separately into it’s own file.
Cons: Requires a lot of work, additional files and test cases.

Based on the above pros and cons, the first option is chosen as it is easier to implement, requires lesser test case and more efficient work load.

4. Documentation

The documentation for this project is written and maintained using asciidoc, it can found in the docs folder.

ℹ️
We chose asciidoc over Markdown because asciidoc provides more flexibility in formatting.

4.1. Editing documentation

We use IntelliJ to edit the documentation. The documentation can be rendered and previewed on IntelliJ.

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

4.2. Publishing documentation

We use Travis as an automation tool to deploy the documentation to Github Page.

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

4.3. Converting documentation to PDF format

We use Google Chrome for converting the 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 withGoogle Chrome.

  3. Click on the Print option in Google 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 31. Saving documentation as PDF files in Chrome

4.4. Changing site-wide documentation settings

We specify project-specific asciidoc attributes in build.gradle file 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

The 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

The attribute to indicate 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

4.5. Specifying per-file documentation settings

We specify the file-specific asciidoc attributes in each .adoc file 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

4.6. Modifying 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.

5. Testing

Testing the software will provide a better user experience for the users. Below will describe the type of tests available, the ways it can be run and the steps to troubleshooting the tests when it fails.

5.1. Types of tests

Below describe the two types of tests available.

  1. GUI tests - These are tests involving the GUI. They include the below.

    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.inventory.ui package.

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

    1. Unit tests targeting the lowest level methods / classes.
      e.g. seedu.inventory.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.inventory.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.inventory.logic.LogicManagerTest

5.2. Running tests

There are three ways to run tests.

💡
The most reliable way to run tests is the third method. 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 'X'.

Method 2: Using Gradle

  • Open a console and run the command gradlew clean allTests (Mac/Linux: ./gradlew clean allTests).

ℹ️
See UsingGradle.adoc for more information on how to run tests using Gradle.

Method 3: Using Gradle (headless)

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

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

5.3. Troubleshooting testing

Below is a common error faced during testing.

NullPointerException error.

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

  • Solution: Execute Gradle task processResources.

6. Dev Ops

This part will guide you through on the approach of integration we used.

6.1. Build automation

We use Gradle as our build automation tool to automate the build process.

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

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

6.3. Coverage reporting

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

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

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

6.6. Managing dependencies

A project often depends on third-party libraries. For example, Inventory Manager depends on the Jackson library for XML parsing. Managing these dependencies can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than these alternatives.
a. Include those libraries in the repo (this bloats the repo size)
b. Require developers to download those libraries manually (this creates extra work for developers)

Appendix A: Suggested Programming Tasks to Get Started

Suggested path for new programmers:

  1. First, add small local-impact (i.e. the impact of the change does not go beyond the component) enhancements to one component at a time. Some suggestions are given in Section A.1, “Improving each component”.

  2. Next, add a feature that touches multiple components to learn how to implement an end-to-end feature across all components. Section A.2, “Creating a new command: remark explains how to go about adding such a feature.

A.1. Improving each component

Each individual exercise in this section is component-based (i.e. you would not need to modify the other components to get it to work).

Logic component

Scenario: You are in charge of logic. During dog-fooding, your team realize that it is troublesome for the user to type the whole command in order to execute a command. Your team devise some strategies to help cut down the amount of typing necessary, and one of the suggestions was to implement aliases for the command words. Your job is to implement such aliases.

💡
Do take a look at Section 2.3, “Logic component” before attempting to modify the Logic component.
  1. Add a shorthand equivalent alias for each of the individual commands. For example, besides typing clear, the user can also type c to remove all items in the list.

    • Hints

    • Solution

      • Modify the switch statement in InventoryParser#parseCommand(String) such that both the proper command word and alias can be used to execute the same intended command.

      • Add new tests for each of the aliases that you have added.

      • Update the user guide to document the new aliases.

      • See this PR for the full solution.

Model component

Scenario: You are in charge of model. One day, the logic-in-charge approaches you for help. He wants to implement a command such that the user is able to remove a particular tag from everyone in the image book, but the model API does not support such a functionality at the moment. Your job is to implement an API method, so that your teammate can use your API to implement his command.

💡
Do take a look at Section 2.4, “Model component” before attempting to modify the Model component.
  1. Add a removeTag(Tag) method. The specified tag will be removed from everyone in the image book.

    • Hints

      • The Model and the Inventory API need to be updated.

      • Think about how you can use SLAP to design the method. Where should we place the main logic of deleting tags?

      • Find out which of the existing API methods in Inventory and Item classes can be used to implement the tag removal logic. Inventory allows you to update a item, and Item allows you to update the tags.

    • Solution

      • Implement a removeTag(Tag) method in Inventory. Loop through each item, and remove the tag from each item.

      • Add a new API method deleteTag(Tag) in ModelManager. Your ModelManager should call Inventory#removeTag(Tag).

      • Add new tests for each of the new public methods that you have added.

      • See this PR for the full solution.

Ui component

Scenario: You are in charge of ui. During a beta testing session, your team is observing how the users use your image book application. You realize that one of the users occasionally tries to delete non-existent tags from a contact, because the tags all look the same visually, and the user got confused. Another user made a typing mistake in his command, but did not realize he had done so because the error message wasn’t prominent enough. A third user keeps scrolling down the list, because he keeps forgetting the index of the last item in the list. Your job is to implement improvements to the UI to solve all these problems.

💡
Do take a look at Section 2.2, “UI component” before attempting to modify the UI component.
  1. Use different colors for different tags inside item cards. For example, friends tags can be all in brown, and colleagues tags can be all in yellow.

    Before

    getting started ui tag before

    After

    getting started ui tag after
    • Hints

      • The tag labels are created inside the ItemCard constructor (new Label(tag.tagName)). JavaFX’s Label class allows you to modify the style of each Label, such as changing its color.

      • Use the .css attribute -fx-background-color to add a color.

      • You may wish to modify DarkTheme.css to include some pre-defined colors using css, especially if you have experience with web-based css.

    • Solution

      • You can modify the existing test methods for ItemCard 's to include testing the tag’s color as well.

      • See this PR for the full solution.

        • The PR uses the hash code of the tag names to generate a color. This is deliberately designed to ensure consistent colors each time the application runs. You may wish to expand on this design to include additional features, such as allowing users to set their own tag colors, and directly saving the colors to storage, so that tags retain their colors even if the hash code algorithm changes.

  2. Modify NewResultAvailableEvent such that ResultDisplay can show a different style on error (currently it shows the same regardless of errors).

    Before

    getting started ui result before

    After

    getting started ui result after
  3. Modify the StatusBarFooter to show the total number of people in the image book.

    Before

    getting started ui status before

    After

    getting started ui status after
    • Hints

      • StatusBarFooter.fxml will need a new StatusBar. Be sure to set the GridPane.columnIndex properly for each StatusBar to avoid misalignment!

      • StatusBarFooter needs to initialize the status bar on application start, and to update it accordingly whenever the image book is updated.

    • Solution

Storage component

Scenario: You are in charge of storage. For your next project milestone, your team plans to implement a new feature of saving the image book to the cloud. However, the current implementation of the application constantly saves the image book after the execution of each command, which is not ideal if the user is working on limited internet connection. Your team decided that the application should instead save the changes to a temporary local backup file first, and only upload to the cloud after the user closes the application. Your job is to implement a backup API for the image book storage.

💡
Do take a look at Section 2.5, “Storage component” before attempting to modify the Storage component.
  1. Add a new method backupInventory(ReadOnlyInventory), so that the image book can be saved in a fixed temporary location.

A.2. Creating a new command: remark

By creating this command, you will get a chance to learn how to implement a feature end-to-end, touching all major components of the app.

Scenario: You are a software maintainer for inventory, as the former developer team has moved on to new projects. The current users of your application have a list of new feature requests that they hope the software will eventually have. The most popular request is to allow adding additional comments/notes about a particular contact, by providing a flexible remark field for each contact, rather than relying on tags alone. After designing the specification for the remark command, you are convinced that this feature is worth implementing. Your job is to implement the remark command.

A.2.1. Description

Edits the remark for a item specified in the INDEX.
Format: remark INDEX r/[REMARK]

Examples:

  • remark 1 r/Likes to drink coffee.
    Edits the remark for the first item to Likes to drink coffee.

  • remark 1 r/
    Removes the remark for the first item.

A.2.2. Step-by-step instructions

[Step 1] Logic: Teach the app to accept 'remark' which does nothing

Let’s start by teaching the application how to parse a remark command. We will add the logic of remark later.

Main:

  1. Add a RemarkCommand that extends Command. Upon execution, it should just throw an Exception.

  2. Modify InventoryParser to accept a RemarkCommand.

Tests:

  1. Add RemarkCommandTest that tests that execute() throws an Exception.

  2. Add new test method to InventoryParserTest, which tests that typing "remark" returns an instance of RemarkCommand.

[Step 2] Logic: Teach the app to accept 'remark' arguments

Let’s teach the application to parse arguments that our remark command will accept. E.g. 1 r/Likes to drink coffee.

Main:

  1. Modify RemarkCommand to take in an Index and String and print those two parameters as the error message.

  2. Add RemarkCommandParser that knows how to parse two arguments, one index and one with prefix 'r/'.

  3. Modify InventoryParser to use the newly implemented RemarkCommandParser.

Tests:

  1. Modify RemarkCommandTest to test the RemarkCommand#equals() method.

  2. Add RemarkCommandParserTest that tests different boundary values for RemarkCommandParser.

  3. Modify InventoryParserTest to test that the correct command is generated according to the user input.

[Step 3] Ui: Add a placeholder for remark in ItemCard

Let’s add a placeholder on all our ItemCard s to display a remark for each item later.

Main:

  1. Add a Label with any random text inside ItemListCard.fxml.

  2. Add FXML annotation in ItemCard to tie the variable to the actual label.

Tests:

  1. Modify ItemCardHandle so that future tests can read the contents of the remark label.

[Step 4] Model: Add Remark class

We have to properly encapsulate the remark in our Item class. Instead of just using a String, let’s follow the conventional class structure that the codebase already uses by adding a Remark class.

Main:

  1. Add Remark to model component (you can copy from Inventory, remove the regex and change the names accordingly).

  2. Modify RemarkCommand to now take in a Remark instead of a String.

Tests:

  1. Add test for Remark, to test the Remark#equals() method.

[Step 5] Model: Modify Item to support a Remark field

Now we have the Remark class, we need to actually use it inside Item.

Main:

  1. Add getRemark() in Item.

  2. You may assume that the user will not be able to use the add and edit commands to modify the remarks field (i.e. the item will be created without a remark).

  3. Modify SampleDataUtil to add remarks for the sample data (delete your inventory.xml so that the application will load the sample data when you launch it.)

[Step 6] Storage: Add Remark field to XmlAdaptedItem class

We now have Remark`s for `Item`s, but they will be gone when we exit the application. Let’s modify `XmlAdapteItem to include a Remark field so that it will be saved.

Main:

  1. Add a new Xml field for Remark.

Tests:

  1. Fix invalidAndValidItemInventory.xml, typicalItemsInventory.xml, validInventory.xml etc., such that the XML tests will not fail due to a missing <remark> element.

[Step 6b] Test: Add withRemark() for ItemBuilder

Since Item can now have a Remark, we should add a helper method to ItemBuilder, so that users are able to create remarks when building a Item.

Tests:

  1. Add a new method withRemark() for ItemBuilder. This method will create a new Remark for the item that it is currently building.

  2. Try and use the method on any sample Item in TypicalItems.

[Step 7] Ui: Connect Remark field to ItemCard

Our remark label in ItemCard is still a placeholder. Let’s bring it to life by binding it with the actual remark field.

Main:

  1. Modify ItemCard's constructor to bind the Remark field to the Item 's remark.

Tests:

  1. Modify GuiTestAssert#assertCardDisplaysItem(…​) so that it will compare the now-functioning remark label.

[Step 8] Logic: Implement RemarkCommand#execute() logic

We now have everything set up…​ but we still can’t modify the remarks. Let’s finish it up by adding in actual logic for our remark command.

Main:

  1. Replace the logic in RemarkCommand#execute() (that currently just throws an Exception), with the actual logic to modify the remarks of a item.

Tests:

  1. Update RemarkCommandTest to test that the execute() logic works.

A.2.3. Full solution

See this PR for the step-by-step solution.

Appendix B: Product Scope

Target user profile:

  • Small to medium enterprises to manage their inventory

  • Experienced computer user with basic knowledge on Command Line Interface (CLI)

  • Organised business owners who wish to manage multiple groups of staffs

  • Efficient user who prefers typing over mouse input

Value proposition: Inventory Manager assists small to medium enterprises (SMEs) in managing their staff and inventory without a need to invest a huge sum in complicated and expensive inventory management systems.

Appendix C: User Stories

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

Table 3. List of user stories
Priority As a …​ I want to …​ So that I can…​

* * *

user

login through CLI

use the features of the app

`* * `

user

change my password

ensure the security of my account

* * *

user

logout through CLI

ensure no one can access the system without my presence

* * *

user

be able to create sale order

deduct inventory quantity

* * *

user

be able to show created sale order

check created sale order

* * *

user

be able to delete sale order

restore deducted inventory quantity

* * *

user

be able to update sale order

update wrongly deducted inventory quantity

* * *

user

be able to view all the items as a list

* * *

new user

view all commands that I can use

learn how to use the app

* * *

manager

export sales report to a CSV file

better manage the inventory

* * *

manager

export all users to a CSV file

better manage staff under me

* * *

manager

send sku to users

send important information to staff

* * *

manager

search for item

better manage items in inventory

* * *

manager

duplicate items

create items efficiently

* * *

manager

update / modify existing items

better manage items in inventory

* * *

manager

add items into inventory

* * *

manager

generate purchase order

restock the inventories

* * *

manager

delete items from inventory

remove items that are out-of-stock

* * *

manager

be notified for low inventory

generate the purchase order

* * *

admin

add users to the system

allow stakeholders of the system to use the apps

* * *

admin

remove users from the system

remove the users who are no longer using the system.

* * *

admin

update users in the system

update users' detail if necessary

* * *

admin

view all existing users in the system

ensure that the system is always up to date

* * *

admin

assign roles to users

restrict their user permissions

*

expert user

use shortcuts for commands

use the app more efficiently

*

user

group items according to categories

Appendix D: Use Cases

For all use cases below, the System is the Inventory Manager application and the Actor is the user, unless specified otherwise.

Below are various use cases for the System.

Use case: Add item

MSS

  1. User requests to add item into Inventory.

  2. Inventory Manager adds the item into the Inventory list.

    Use case ends.

Extensions

  • 1a. Item name does not follow the correct format.

    • 1a1. Inventory Manager shows an error message.

      Use case resumes at step 1.

  • 1b. Item SKU does not follow the correct format.

    • 1b1. Inventory Manager shows an error message.

      Use case resumes at step 1.

  • 1c. Item Quantity does not follow the correct format.

    • 1c1. Inventory Manager shows an error message.

      Use case resumes at step 1.

  • 1d. Item Image does not follow the correct format or is an invalid image/path.

    • 1d1. Inventory Manager shows an error message.

      Use case resumes at step 1.

Use case: Edit item

MSS

  1. User requests to list items.

  2. Inventory Manager shows a list of items

  3. User requests to edit a specific item in the list

  4. Inventory Manager edits the item

    Use case ends.

Extensions

  • 2a. The list is empty.

    Use case ends.

  • 3a. The given index is invalid.

    • 3a1. Inventory Manager shows an error message.

      Use case resumes at step 2.

Use case: Delete item

MSS

  1. User requests to list items.

  2. Inventory Manager shows a list of items

  3. User requests to delete a specific item in the list

  4. Inventory Manager deletes the item

    Use case ends.

Extensions

  • 2a. The list is empty.

    Use case ends.

  • 3a. The given index is invalid.

    • 3a1. Inventory Manager shows an error message.

      Use case resumes at step 2.

Use case: Add user

MSS

  1. Admin requests to add item into Inventory Manager.

  2. Inventory Manager adds the staff into the Staff list.

    Use case ends.

Extensions

  • 1a. Staff name does not follow the correct format.

    • 1a1. Inventory Manager shows an error message.

      Use case resumes at step 1.

  • 1b. Staff username does not follow the correct format.

    • 1b1. Inventory Manager shows an error message.

      Use case resumes at step 1.

  • 1c. Staff password does not follow the correct format.

    • 1c1. Inventory Manager shows an error message.

      Use case resumes at step 1.

  • 1d. Staff Role does not equal to user, manager or admin.

    • 1d1. Inventory Manager shows an error message.

      Use case resumes at step 1.

Use case: Edit user

MSS

  1. User requests to list staffs.

  2. Inventory Manager shows a list of staffs

  3. User requests to edit a specific staff in the list

  4. Inventory Manager edits the staff

    Use case ends.

Extensions

  • 2a. The list is empty.

    Use case ends.

  • 3a. The given index is invalid.

    • 3a1. Inventory Manager shows an error message.

      Use case resumes at step 2.

Use case: Delete user

MSS

  1. Admin requests to list staffs

  2. Inventory Manager shows a list of staffs

  3. Admins requests to delete a specific staff in the list

  4. Inventory Manager deletes the staff

    Use case ends.

Extensions

  • 2a. The list is empty.

    Use case ends.

  • 3a. The given username is invalid.

    • 3a1. Inventory Manager shows an error message.

      Use case resumes at step 2.

Use case: Export data

MSS

  1. User requests to export data with a filepath given.

  2. Inventory Manager exports the data to the filepath given.

  3. Inventory Manager shows a preview of the data exported

    Use case ends.

Extensions

  • 1a. The filepath is empty or invalid.

    • 1a1. Inventory Manager shows an error message.

      Use case resumes at step 1.

  • 2a. The exporting progress fails.

    • 2a1. Inventory Manager shows an error message.

      Use case resumes at step 1.

Use case: Import data

MSS

  1. User requests to import data with a filepath given.

  2. Inventory Manager imports the data from the filepath given.

  3. Inventory Manager shows a preview of the data imported

    Use case ends.

Extensions

  • 1a. The filepath is empty or invalid.

    • 1a1. Inventory Manager shows an error message.

      Use case resumes at step 1.

  • 2a. The importing progress fails.

    • 2a1. Inventory Manager shows an error message.

      Use case resumes at step 1.

Use case: Add purchase order

MSS

  1. User requests to list items.

  2. Inventory Manager shows a list of items

  3. User requests to add purchase order on an item based on it’s SKU

  4. Inventory Manager adds purchase order

  5. Inventory Manager shows a successful purchase order added message

    Use case ends.

Extensions

  • 2. The item list is empty.

    Use case ends.

  • 3a. The given command does not contain any item’s sku in the item list.

    • 3a1. Inventory Manager shows an item not found message.

      Use case resumes at step 2.

  • 3b. The given command format is invalid.

    • 3b1. Inventory Manager shows an error message.

      Use case resumes at step 2.

Use case: Approve purchase order

MSS

  1. User requests to list purchase orders

  2. Inventory Manager shows a list of purchase orders

  3. User requests to approve a specific pending Purchase order in the list

  4. Inventory Manager updates purchase order status

  5. Inventory Manager shows a successful approval message

    Use case ends.

Extensions

  • 2. The purchase order list is empty.

    Use case ends.

  • 3a. The given purchase order ID is invalid.

    • 3a1. Inventory Manager shows an error message.

      Use case resumes at step 2.

  • 3b. The given command format is invalid.

    • 3b1. Inventory Manager shows an error message.

      Use case resumes at step 2.

  • 3c. The given purchase order status is not 'PENDING'.

    • 3c1. Inventory Manager shows an error message.

      Use case resumes at step 2.

Use case: Reject purchase order

MSS

  1. User requests to list purchase orders

  2. Inventory Manager shows a list of purchase orders

  3. User requests to reject a specific pending Purchase order in the list

  4. Inventory Manager updates purchase order status

  5. Inventory Manager shows a successful rejection message

    Use case ends.

Extensions

  • 2. The purchase order list is empty.

    Use case ends.

  • 3a. The given purchase order ID is invalid.

    • 3a1. Inventory Manager shows an error message.

      Use case resumes at step 2.

  • 3b. The given command format is invalid.

    • 3b1. Inventory Manager shows an error message.

      Use case resumes at step 2.

  • 3c. The given purchase order status is not 'PENDING'.

    • 3c1. Inventory Manager shows an error message.

      Use case resumes at step 2.

Use case: Edit purchase order

MSS

  1. User requests to list purchase orders

  2. Inventory Manager shows a list of purchase orders

  3. User requests to edit a specific purchase order in the list

  4. Inventory Manager edits the purchase order

  5. Inventory Manager shows a successful edit message

    Use case ends.

Extensions

  • 2. The purchase order list is empty.

    Use case ends.

  • 3a. The given index is invalid.

    • 3a1. Inventory Manager shows an error message.

      Use case resumes at step 2.

  • 3b. The given command format is invalid.

    • 3b1. Inventory Manager shows an error message.

      Use case resumes at step 2.

  • 3c. The given purchase order status is not 'PENDING'.

    • 3c1. Inventory Manager shows an error message.

      Use case resumes at step 2.

Use case: Delete purchase order

MSS

  1. User requests to list purchase orders

  2. Inventory Manager shows a list of purchase orders

  3. User requests to delete a specific purchase order in the list

  4. Inventory Manager deletes the purchase order

  5. Inventory Manager shows a successful deletion message

    Use case ends.

Extensions

  • 2. The purchase order list is empty.

    Use case ends.

  • 3a. The given index is invalid.

    • 3a1. Inventory Manager shows an error message.

      Use case resumes at step 2.

  • 3b. The given command format is invalid.

    • 3b1. Inventory Manager shows an error message.

      Use case resumes at step 2.

Use case: Create sale order

MSS

  1. User requests to add a sale into the System.

  2. System adds the sale into the sales list.

    Use case ends.

Extensions

  • 1a. SKU cannot be found in the System.

    • 1a1. System shows an error message.

      Use case resumes at step 1.

  • 1b. Item SKU does not follow the correct format.

    • 1b1. System shows an error message.

      Use case resumes at step 1.

  • 1c. Sale Quantity does not follow the correct format.

    • 1c1. Inventory Manager shows an error message.

      Use case resumes at step 1.

Use case: Delete sale order

MSS

  1. User request to list sale orders.

  2. System shows a list of sale orders.

  3. User requests to delete a specific sale order in the list.

  4. System deletes the sale order.

    Use case ends.

Extensions

  • 1a. There is no sale order to list.

    Use case ends.

  • 1b. The given sale order ID is invalid.

    • 1b1. System shows an error message.

      Use case resumes at step 2.

Appendix E: Non Functional Requirements

  1. Inventory manager to be compatible with most of the mainstream Operating Systems.

  2. Support up to 100 products for inventory management without affecting performance of the application.

  3. Generate each report within 3 seconds

Appendix F: Project Management

  • Darren Ong Yun Kai

    • Authentication and User management

      • V1.1 Develop a users management API

      • V1.2 Support authentication of users and users management

      • V1.3 Support hashing of password

  • Esmond Tan

    • Sale Orders

      • V1.1 Support sale orders create

      • V1.2 Support sale orders delete

      • V1.3 Support sale orders list

  • Wang Chao

    • Reporting Module

      • V1.1 Support daily sales report

      • V1.2 Support exporting users and sales to sku and excel

      • V1.3 Support exporting inventory to sku and excel

  • Yao TengXiong

    • Inventory Management

      • V1.1 Support inventory create, read, update, delete

      • V1.2 Image support for inventory items

      • V1.3 Develop inventory management API

  • Zulqarnain

    • Purchase Order

      • V1.1 Support purchase order create, read, update, delete

      • V1.2 Integrate with Inventory API to update inventory

      • V1.3 Develop purchase order management API

Appendix G: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X

Appendix H: Acronyms

SKU

SKU stands for Stock Keeping Unit. It is a product and service identification code for a store or product.

UML

UML stands for Unified Modeling Language which is used to specify, visualize, construct and document the behavior of a software.

MSS

MSS stands for Main Success Scenario where it describes the shortest and most straightforward interaction by which the post-conditions can be achieved

PO

PO stands for purchase order.

Appendix I: 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.

I.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 a set of sample contacts. 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.

I.2. Authentication

  1. Login

    1. Prerequisites: The username and password of the staff must be in the staffs record.

    2. Test case: login u/admin p/password
      Expected: User will be logged in and granted access on using other functions of the application.

    3. Test case: login u/asd1231 p/
      Expected: User will be prompted to fill in the empty password field.

    4. Test case: login , login u/ p/password
      Expected: Similar to previous.

  2. Change password

    1. Prerequisites: User must be logged in and the new password must be different from the old password.

    2. Test case: change-password p/newpassword
      Expected: User’s password will be updated and need to login using the new password in the next session.

    3. Test case: change-password p/passworddd p/newpassword
      Expected: newpassword will be used as the new password.

    4. Test case: change-password p/
      Expected: User will be prompted to fill in the empty password field.

    5. Test case: change-password
      Expected: Similar to previous.

  3. Logout

    1. Prerequisites: User must be logged in.

    2. Test case: logout
      Expected: User will be logged out.

I.3. User management

  1. Adding a user

    1. Prerequisites: The staff to be added must not be a duplicated entry in the list and must login as an admin.

    2. Test case: add-staff u/johnd p/johndoe n/John Doe r/user
      Expected: Staff will be added into the system. Details of the added staff shown in the status message. Timestamp in the status bar is updated.

    3. Test case: add-staff u/
      Expected: No staff is added. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect add-staff commands to try: add-staff, `add-staff ,,,`_
      Expected: Similar to previous.

  2. Listing all the users

    1. Prerequisites: Must login as an admin.

    2. Test case: list-staff
      Expected: Staffs will be listed at the panel on the left of the UI.

  3. Editing a user

    1. Prerequisites: The staff to be edited must be an existing entry in the list

    2. Test case: edit-staff 1 u/johnd p/johndoe n/John Doe r/user
      Expected: Edited staff will replace the existing staff in the system. Details of the edited staff shown in the status message. Timestamp in the status bar is updated.

    3. Test case: edit-staff u/
      Expected: No staff is edited. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect add-staff commands to try: edit-staff, `edit-staff ,,,`_
      Expected: Similar to previous.

  4. Deleting a user

    1. Prerequisites: The index provided must be the index in the existing list of staff shown

    2. Test case: delete-staff 2
      Expected: Staff listed as second in the list will be deleted. Details of the deleted staff shown in the status message. Timestamp in the status bar is updated.

    3. Test case: `delete-staff `
      Expected: No staff is deleted. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect delete commands to try: delete-staff, `delete-staff ,,,`_
      Expected: Similar to previous.

I.4. Inventory management

  1. Adding an item

    1. Pre-requisites: The item to be added must not have the same SKU as another existing item in the inventory and must be logged in.

    2. Test case: add-item n/iPhone XR p/1500.00 q/70 s/apple-iphone-xr i/iphone.jpg
      Expected: Item will be added into the inventory. Details of the added item shown in the status message. Timestamp in the status bar is updated.

    3. Test case: add-item n/
      Expected: No item is added. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect add-item commands to try: add-item, `add-item ,,,`_
      Expected: Similar to previous.

  2. Listing all the items

    1. Prerequisites: Must be logged in.

    2. Test case: list-item
      Expected: All items will be listed at the panel on the left of the UI.

  3. Listing all the items that are low on stock

    1. Prerequisites: Must be logged in.

    2. Test case: list-low-qty
      Expected: All items thath as quantities equal or less than 10 will be listed at the panel on the left of the UI.

  4. Editing an item

    1. Prerequisites: The item to be edited must be an existing entry in the inventory list

    2. Test case: edit-item 1 n/iPhone XS Midnight Blue
      Expected: Edited item will replace the existing item in the system. Details of the edited item shown in the status message. Timestamp in the status bar is updated.

    3. Test case: edit-item n/
      Expected: No item is edited. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect edit-item commands to try: edit-item, `edit-item ,,,`_
      Expected: Similar to previous.

  5. Deleting an item while all items are listed

    1. Prerequisites: List all items using the list-item command. Multiple items in the list.

    2. Test case: delete-item 1
      Expected: First item is deleted from the list. Details of the deleted item shown in the status message. Timestamp in the status bar is updated.

    3. Test case: delete-item 0
      Expected: No item is deleted. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect delete commands to try: delete-item, delete-item x (where x is larger than the list size)

I.5. Sale management

  1. Adding a sale

    1. Prerequisites: The sale to be added must be for an existing item.

    2. Test case: add-sale q/lg-g7 q/10
      Expected: Sale will be added into the system. Details of the added sale shown in the status message.

    3. Test case: add-sale q/123456
      Expected: No sale is added. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect add-staff commands to try: add-sale, add-sale abc
      Expected: Similar to previous.

  2. Deleting a sale

    1. Prerequisites: The sale ID provided must be the sale ID as shown when the sale is listed.

    2. Test case: delete-sale 1
      Expected: Sale listed with sale ID "1" will be deleted. Details of the deleted sale shown in the status message.

    3. Test case: delete-sale
      Expected: No sale is deleted. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect delete commands to try: delete-sale, delete-sale ABC
      Expected: Similar to previous.

I.6. Purchase order management

  1. Listing all purchase orders

    1. Prerequisites: User must be logged in

    2. Test case: list-po
      Expected: Purchase orders will be listed at the panel on the left of the UI.

  2. Adding a purchase order

    1. Prerequisites: User must be logged, item sku must exist in the inventory manager, required date must be beyond the current date and the quantity must be valid.

    2. Test case: add-po s/apple-iphone-xr q/1000 d/2018-12-12 sp/Apple Inc
      Expected: Purchase order will be added into the system. Details of the added purchase order is shown in the status message. Timestamp in the status bar is updated.

    3. Test case: add-po s/
      Expected: No purchase order is added. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect add-po commands to try: add-po, `add-po ,,,`_
      Expected: Similar to previous.

  3. Editing a purchase order

    1. Prerequisites: User must be logged, purchase order index must exist in the list and purchase order status must be in 'PENDING' status.

    2. Test case: edit-po 2 q/1000
      Expected: Edited purchase order will replace the existing purchase order in the system. Details of the edited purchase order is shown in the status message. Timestamp in the status bar is updated.

    3. Test case: edit-po asd sd/
      Expected: No purchase order is edited. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect edit-po commands to try: edit-po, `edit-po ,,,`_
      Expected: Similar to previous.

  4. Deleting a purchase order

    1. Prerequisites: User must be logged, purchase order index must exist in the list.

    2. Test case: delete-po 2
      Expected: Purchase order at index 2 will be deleted. Details of the deleted purchase order is shown in the status message. Timestamp in the status bar is updated.

    3. Test case: delete-po asd
      Expected: No purchase order is deleted. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect delete-po commands to try: delete-po, `delete-po ,,,`_
      Expected: Similar to previous.

  5. Approving a purchase order

    1. Prerequisites: User must be logged, purchase order index must exist in the list and purchase order status must be in 'PENDING' status.

    2. Test case: approve-po 2
      Expected: Purchase order at index 2 will be approved. Details of the approved purchase order is shown in the status message. Timestamp in the status bar is updated.

    3. Test case: approve-po asd sd/
      Expected: No purchase order is approved. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect approve-po commands to try: approve-po, `approve-po ,,,`_
      Expected: Similar to previous.

I.7. Reporting module

  1. Export the item list to csv file

    1. Prerequisites: User must be logged in as an admin

    2. Test case: export-csv-items f/items.csv
      Expected: The item list is exported successfully to items.csv in the installed directory.

    3. Test case: export-csv-items n/items.csv
      Expected: The item list is not exported due to invalid prefix. Error details shown in the feedback.

    4. Test case: export-csv-items f/items.xml
      Expected: The item list is not exported due to invalid file format. Error details shown in the feedback.