This document describes the architecture, software design and implementation decisions of BakingHome, a one-stop application for managers of home bakeries to manage their business efficiently.
This documentation is intended for the developers, designers, and software testers of BakingHome.
This section describes the architecture and components of BakingHome.
The Architecture Diagram given above explains the high-level design of BakingHome. Given below is a quick overview of each component.
BakingHome consists of four components.
Each of the four components
-
Defines its API in an
interfacewith the same name as the Component. -
Exposes its functionality using a
Managerclass.
API : Ui
.java
The UI consists of a MainWindow that contains 5 separate pages, namely the SalePage,
ProductPage, ShoppingPage, InventoryPage and OrderPage.
All these, including the MainWindow, inherit from the abstract UiPart class.
Some pages consist of smaller components.
For example, SalePage has a SaleCard, ProductPage has an
IngredientCard, and OrderPage has an OrderCard and an OrderItemBox.
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
Logiccomponent. -
Listens for changes to
Modeldata so that the UI can be updated with the modified data.
API :
Logic.java
-
LogicManageruses theBakingHomeParserclass to parse the user command. -
This results in a
Commandobject which is executed by theLogicManager. -
The command execution can affect the
Model(for example, deleting an order). -
The result of the command execution is encapsulated as a
CommandResultobject which is passed back to theUI. -
In addition, the
CommandResultobject can also instruct theUIto perform certain actions, such as displaying a certain page to the user.
API :
Model.java
The Model,
-
Stores classes that are used for BakingHome (namely Ingredient, Customer, Order, IngredientInfoList, IngredientItemList, Product, ProductIngredient, Sale)
-
Exposes unmodifiable
ObservableList<>of Orders, Products, Sales, Inventory, and ShoppingList 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.
-
Stores the UniqueEntityLists used for BakingHome and its methods.
API :
BakingHomeStorage.java
The Storage component,
-
Can save the Ingredient objects, Ingredient Item objects, Order objects, Product objects, Product Item objects, Sale objects, and Shortcut objects, in json format and read it back.
This section describes some noteworthy details on how certain features are implemented.
BakingHome comes with an auto-complete feature that predicts the commands or arguments that the user attempts to type based on what has already been entered.
Auto-Complete is invoked by pressing the Tab key.
If there are multiple suggestions available, the user can navigate among the suggestions by repeatedly pressing the Tab key.
The auto-complete mechanism is facilitated by AutoCompleter in Logic component.
AutoCompleter has a nested class Input, which represents the details of user input, including the text and the caretPosition.
AutoCompleter implements the following operations:
-
AutoCompleter#addCommandClass(Class<? extends Command>)— Adds a command class forAutoCompleterto complete. -
AutoCompleter#isAutoCompletable(Input)— Returns true if the current user input can be completed byAutoCompleter. -
AutoCompleter#complete()— Returns anInputobject that specifies the details of the user input after auto-completion.
The last two operations are exposed in the Model interface as Model#isAutoCompletable(Input)() and Model#complete() respectively.
When the user presses a key in the command box, command box checks if the key pressed is Tab.
If Tab is pressed, the command box checks with AutoCompleter to verify if the current input is auto-completable.
If the state is eligible for auto-completion, the command box will request for a suggestion by calling Model#complete() and set its text and caret position accordingly.
The workflow is illustrated in the diagram below:
The auto-complete feature allows users to navigate among possible suggestions by repeatedly pressing Tab.
Internally, AutoCompleter maintains a list suggestionList containing all possible Input suggestions. suggestionList is implemented as a cyclic list by maintaining a suggestionPointer:
-
When
AutoCompleter#complete()is called, theInputobject pointed bysuggestionPointerinsuggestionListis returned, andsuggestionPointeris set to(suggestionPointer + 1) % suggestionList.size(). -
suggestionListis updated if the user input no longer matches any of the suggestions insuggestionList.
Following the Open-Closed Principle, the Auto-Complete feature is highly extensible.
You can add a command to support auto-completion by taking the following steps:
Step 1. Declare AUTO_COMPLETE_INDICATOR and AUTO_COMPLETE_PARAMETERS fields in your command class.
|
ℹ️
|
AUTO_COMPLETE_INDICATOR is a string specifying when the arguments should be completed.
Auto-complete only completes the arguments of a command when the text in command box with that command’s AUTO_COMPLETE_INDICATOR.
AUTO_COMPLETE_PARAMETERS is an array of Prefix that you want to auto-complete.
|
An example is shown below:
public class AddOrderCommand extends Command {
public static final String AUTO_COMPLETE_INDICATOR = "order add"; // This tells AutoCompleter that if user input begins with "order add", it should be recognized as an AddOrderCommand by AutoCompleter.
public static final Prefix[] AUTO_COMPLETE_PARAMETERS = new Prefix("by"), new Prefix("name"); //This tells AutoCompleter that AddOrderCommand has these parameters.
}Step 2. Add the command to AutoCompleter by calling AutoCompleter#addCommandClass(Class<? extends Command>)
-
Alternative 1: Hard-code command words and arguments in AutoCompleter class.
-
Pros: Easy to implement.
-
Cons: Violates the Open-Closed Principle because developers need to modify AutoCompleter’s internal structure to add new commands. Also, it makes code more coupled since if we change parameters of a command, we need to change corresponding fields in AutoCompleter as well.
-
-
Alternative 2 (Current choice): Use Reflection API to obtain command words and arguments from CommandClass at runtime.
-
Pros: Avoids modification to the internal structure of AutoCompleter class
-
Cons: Since Reflection allows code to perform operations that would be illegal in non-reflective code, it could lead to unexpected side-effects if implemented wrongly.
-
-
Alternative 1: Use a drop-down list to display all possible suggestions.
-
Pros: Intuitive and allows users to see all possible commands in one place.
-
Cons: Hard to implement. May require additional components other than JavaFx’s built-in components.
-
-
Alternative 2 (Current choice): Navigate between possible suggestions by repeatedly pressing
Tabkey.-
Pros: Easier to implement since no additional components are needed
-
Cons: Cannot display all possible commands in one place.
-
BakingHome’s products can have two status: ACTIVE or ARCHIVE.
This feature allows user to view products with a given certain status, i.e. shows only products with an ARCHIVE status.
The filter mechanism in product is facilitated by
FilteredList
which wraps a ObservableList and
filters using the provided Predicate.
A FilteredList<Product> filteredProducts is stored in the ModelManager.
In BakingHome, there is an
ObservableList<Product> products which contains all products, regardless of its status. filteredProducts
in the ModelManager is initialized with this ObservableList.
Since a FilteredList needs a Predicate, which matches the elements in the source list that should be visible, the filter mechanism implements the following operation to support filtering:
-
Model#updateFilteredProductList(Predicate<Product> predicate)— Sets the value of the property Predicate in thefilteredProducts.-
Predicates are declared statically in the
Modelinterface, namelyPREDICATE_SHOW_ACTIVE_PRODUCTS,PREDICATE_SHOW_ARCHIVE_PRODUCTS, andPREDICATE_SHOW_ALL_PRODUCT. In particularPREDICATE_SHOW_ARCHIVE_PRODUCTSis as follows
-
Predicate<Product> PREDICATE_SHOW_ARCHIVE_PRODUCTS = product -> {
return product.getStatus() == Product.Status.ARCHIVE;
};-
The
ListProductCommandwill call this method to change the visibility of products with different status by passing in the corresponding predicate.
An example usage scenario and how the filter mechanism behaves at each step is shown below.
Step 1. The user launches the application for the first time.
UniqueProductList will be initialized with a list of default products in BakingHome.
This list contains a few active products and a few archived products.
Step 2. The user inputs product filter -scope archive to list all archived products. UI passes the input to Logic.
Logic then uses a few Parser classes to extract layers of information out as seen from steps 3 to 5.
Step 3. Logic passes the user input to BakingHomeParser. BakingHomeParser identifies that this is a ProductCommand
through the word "product".
It then creates a
ProductCommandParser to parse the remaining information, i.e. "filter -scope archive".
Step 4. ProductCommandParser identifies that this is a FilterProductCommand through the word "filter".
It then creates a FilterProductCommandParser to parse the scope.
Step 5. FilterProductCommandParser parse "-scope archive" and get the scope.
It then returns a
FilterProductCommand with the scope information.
Step 6. Logic finally gets the FilterProductCommand and execute it.
The execution firstly calls
Model#updateFilteredProductList(Predicate<Product> predicate) to update the Predicate in
filteredProducts in Model.
This execution then returns a CommandResult to UI, containing the response to the user.
Step 7. UI displays the response in the CommandResult.
In addition, UI will change to display archived products after model updates filteredProducts, since UI is constantly listening for the change in Model.
The Sequence Diagram below shows how the components interact with each other for the above mentioned scenario.
Note that almost all other commands follow the same sequence, with different Command and Parser classes.
-
Alternative 1 (current choice): Save all products in an
ObservableListinBakingHome, and keep aFilteredListin theModelManager.ProductCommandParserparses the user input and gets the Predicate to update theFilteredList.-
Advantages: Implementation is clearer and code is more human-readable.
-
Disadvantages: More difficult to write a Predicate.
-
-
Alternative 2: Keep two separate product lists, one for archived products and one for active products.
-
Advantages: Fast access to products of both status.
-
Disadvantages: Implementation will become complicated. It also makes it very expensive when adding features like sorting all products according to name, price or cost.
-
-
Alternative 2: Keep only one list of products. Loop through the list to get the products with the desired status.
-
Advantages: Simplicity in storing data.
-
Disadvantages: Time complexity is very high, resulting in a slow response of the application when the product list gets long.
-
BakingHome comes with a shop buy command in its shopping list feature.
This command transfers ingredients and its respective quantity from the shopping list to the inventory list.
It will then generate a sales transaction automatically in the Sales page.
The shop buy feature is facilitated by the UniqueEntityLists initialized in BakingHome, which is an implementation of Iterable and contains an ObservableList.
There are 3 UniqueEntityLists, inventory, shoppingList and sales, which are involved in this feature and each of them has an add and set operation.
-
UniqueEntityList<class>#add(toAdd)- Adds objecttoAddinto theObservableListstored in theUniqueEntityList. -
UniqueEntityList<class>#set(toEdit, edited)- Replaces objecttoEditwith the new objecteditedin theObservableListstored in theUniqueEntityList. Objecteditedwill take the index position of objecttoEditin theObservableList.
These operations are exposed in the Model interface as Model#addInventory, Model#setInventory, Model#setShoppingList, and Model#addSaleFromShopping.
The UniqueEntityLists are also exposed in the Model as FilteredLists, which wraps an ObservableList and filters using the provided Predicate.
Given below is an example usage scenario and how the shop buy mechanism works.
Step 1. The user launches the application for the first time.
The UniqueEntityLists inventory, shoppingList, and sales are initialized in BakingHome with the initial data stored in the Storage.
Step 2. The user inputs shop buy 1,2 command to buy the first and second ingredient in the shopping list.
This command goes through the Parser to get the indices of the ingredients to be bought and executes the BuyShoppingCommand.
Step 3. The BuyShoppingCommand calls the FilteredLists stored in the Model
through Model#getFilteredInventoryList(), Model#getFilteredShoppingList() and stores them in the ArrayLists<Item<Ingredient>> inventoryList and shoppingList respectively.
Step 4. For every index, the Item<Ingredient> object is called from shoppingList.
Each ingredient is checked whether inventoryList already contains it using
inventoryList#contain(Item<Ingredient> toBuy).
-
If
inventoryListcontains it, a newItem<Ingredient>constructor is created with the added quantities of both lists. The new constructor then replaces the current one ininventoryListusing theinventoryList#set()method. -
Else, the
Item<Ingredient>object inshoppingListis just added toinventoryListusing theinventoryList#add()method.
Step 6. For every ingredient that is bought in the shopping list, a new Item<Ingredient> constructor is created using the original ingredient’s data but with quantity = 0. This new constructor then replaces the current one in shoppingList using the shoppingList#set() method.
Step 7. BuyShoppingCommand will calculate the total cost of the ingredients bought and pass it as parameters to AddSaleFromShopping method in Model, along with an ArrayList of the bought ingredients. AddSaleFromShopping will then create a Sale constructor with these values and add it to sales.
Step 8. These will be updated in the UI automatically as these objects are stored in ObservableLists.
The following sequence diagram shows how the shop buy mechanism works in showing the correct UI to the user after a shop buy command is inputted.
-
Alternative 1: Removing the ingredients from the shopping list after they are bought.
-
Pros: The shopping list is clearer and more readable for the user as redundant ingredients that he/she has already bought will not be shown on the list.
-
Cons: The costs and remarks that the user had made will be lost and he has to input them again the next time he wants to buy the same ingredients.
-
-
Alternative 2 (current choice): Set the bought ingredients’ quantity to 0 in the shopping list.
-
Pros: There is a saved template of the shopping list with past costs and remarks of the ingredients, making it convenient for the user to just edit the quantity to the quantity he needs to buy.
-
Cons: The shopping list may become very cluttered with too many ingredients. Hence, a
shop listcommand can be executed in the command line to filter out ingredients that have 0 quantity.
-
For every purchase made under the shopping tab and every order delivered successfully under the orders tab,
a sale entry is automatically added into BakingHome.
Apart from that, users can manually add their own sale entries by using the sale add command.
These entries can then be accessed under the Sales tab in the application.
The sale add feature involves adding a Sale object to a UniqueEntityList named sales which is initialized inside BakingHome.
A filtered copy of this list is also found inside ModelManager named filteredSales.
Having two separate lists allow us to store all Sale objects that have been added while at the same time displaying only the desired entries using predicates.
Similar to the other 3 features, the operations to edit the two lists are exposed in ModelManager as the following public methods:
-
ModelManager#AddSale(sale)– Adds aSaleobject to theUniqueEntityListsales. ThefilteredListis also updated to show the new entry and filter predicate is reset to show all entries. -
ModelManager#AddSaleFromOrder(order)– Same as the above. Except the argument being passed in is anOrderobject, from which the necessary fields are being copied over to a newSaleobject being created and added to the two lists. -
ModelManager#AddSaleFromProduct(totalCost, toBuyList)– Same as the above. Except the arguments being passed in are adoublevalue (total cost) and anArrayList(ingredients). A newSaleobject is created with a fixed description denoting its origin from Products. The value and remarks are populated with the given value and ingredient lists respectively.
Given below is an example usage scenario and how the add sale command works.
Step 1. The user launches the application for the first time. The UniqueEntityList sales is initialized in BakingHome with the initial data stored in the storage.
Step 2. The user inputs sale add -desc TestDescription -val 12.34 to add a Sale entry with description “TestDescription” and positive value of 12.34 in revenue to sales.
Step 3. The command is parsed by SaleParserUtil to collect the relevant String and Double data which are then copied over to the new Sale object. The fields that are not given are initialized to their default value.
Step 4. ModelManager#AddSale() is called which in turn calls BakingHome#AddSale() and passes the created Sale object into the UniqueEntityList sales. At the same time, the FilteredSaleList is updated with the predicate PREDICATE_SHOW_ALL_SALES which as the name suggests, is used to display every sale entry.
-
Alternative 1 (current implementation): Store all sale entries in an
ObservableListin BakingHome and keep aFilteredListinModelManager.-
Pros: Implementation is clearer and code is more readable.
-
Cons: Memory is wasted in keeping two sets of the list.
-
-
Alternative 2: Store only one sale list. Loop through the list to get the desired entries for display.
-
Pros: Structure is simpler.
-
Cons: Time complexity is higher, which may result in slower queries especially as the number of entries increase.
-
-
Alternative 3: Store a new list for each month / year.
-
Pros: Fast access times and easy transfer of data should new features require sale data for a specific month.
-
Cons: Potentially messier code.
-
We are using java.util.logging package for logging. The LogsCenter class is used to manage the logging levels and logging destinations.
-
The
Loggerfor a class can be obtained usingLogsCenter.getLogger(Class)which will log messages according to the specified logging level -
Currently log messages are output through:
Consoleand to a.logfile.
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
Target user profile:
Bakery managers of home bakeries, who prefer typing and is willing to use a Desktop application to manage his business.
Such a manager needs to take care of every single aspect of his bakery business, from allocating responsibilities and keeping track of revenue, to taking the customer’s order. He might even need to do the baking, since there is limited manpower.
Though currently there are many well-developed applications for the work he needs to do, there isn’t one that integrates all the features he needs. Thus, it is hard for him to switch between different apps to manage his bakery business.
BakingHome is a one-stop desktop application that has all the important features for such a manager to eliminate the trouble of changing between different apps.
Value proposition:
A one-stop bakery management system for home bakeries.
Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *
| Priority | As a … | I want to … | So that I can… |
|---|---|---|---|
|
user |
Add products with details |
Track what products my bakery has |
|
user |
Edit a product’s details |
Keep my products updated to new improvements |
|
user |
Delete a product |
Remove irrelevant products that have been phased out |
|
user |
Archive a product |
In case my business has evolved but I do not want to lose an older product |
|
user |
Have a default ingredient cost calculated for me even if I don’t enter the cost |
Have something to refer to when deciding the retail price |
|
user |
Search for a product through keywords |
find a product easily when the list gets long. |
|
user |
Sort products through name, cost, price, profit |
find a product easily when the list gets long. |
|
user |
Add a new order without specifying any details |
Add orders more quickly |
|
user |
Edit an order’s details |
Adjust the order if my customer’s preferences change |
|
user |
Delete multiple orders in one go |
Save myself from the trouble of deleting them one by one |
|
user |
Sort the orders by date created, deadline, and total price |
Look for orders more easily |
|
careless user |
Undo deleting an order |
save myself from the trouble of typing out the whole order again |
|
user |
Mark orders as done, canceled or completed |
Track the status of my orders more easily |
|
user |
Add, edit and delete my ingredients in the shopping list easily |
Manage the bakery more easily |
|
user |
Transfer my ingredients from the shopping list to inventory list in a single step |
Save the trouble of having to manually re-key every single ingredient |
|
user |
Clear all list items in one go |
saved myself from the trouble of deleting one by one |
|
user |
Be able to know the price or estimated prices of the ingredients in my shopping list |
I can budget myself and cut costs if necessary |
|
user |
Be able to track the expiry dates of ingredients in my inventory |
Keep stock without having to physically check it myself |
|
user |
Be reminded of ingredients that are going to expire soon in the inventory |
Reduce wastage of ingredients |
|
user |
Have saved templates of my shopping lists |
I do not have to input main ingredients that I usually buy every time |
|
user |
Be able to input the places of the ingredients sold in my shopping list and sort those ingredients according to those places |
I do not miss out an ingredient when going shopping at a certain location |
|
user |
Input where I store my ingredients in my inventory |
I can find them easily in real life |
|
user |
Store my transactions |
Reference them easily in the future |
|
user |
Edit older transaction details |
Change individual records which may have been logged wrongly |
|
user |
Delete older transaction |
Remove older and irrelevant data from my sales calculations |
|
user |
Automatically log expenditures and sales |
There is no need to retype information from completing an order or shopping buy |
|
user |
Calculate revenue, cost and hence profit instantly |
Check the bakery’s finances with a quick glance |
(For all use cases below, the System is BakingHome and the Actor is the user, unless specified otherwise)
MSS
-
User requests to list all orders.
-
BakingHome shows a list of orders.
-
User requests to delete a specific order or multiple orders in the list.
-
BakingHome deletes the order(s).
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index(indices) is(are) invalid.
-
3a1. BakingHome shows an error message.
Use case resumes at step 2.
-
MSS
-
User requests to list all ingredients in the shopping list.
-
BakingHome shows a list of ingredients.
-
User requests to buy a specific ingredient or multiple ingredients in the shopping list.
-
BakingHome transfers these ingredients from the shopping list to the inventory list and adds a sales transaction with the total cost of the bought ingredients to the Sales page.
Use case ends.
Extensions
-
2a.The list is empty.
Use case ends.
-
3a. The given index(indices) is(are) invalid.
-
3a1. BakingHome shows an error message.
Use case resumes at step 2.
-
MSS
-
User requests to list all orders.
-
BakingHome shows a list of orders.
-
User requests to complete a specific order or multiple orders in the list.
-
BakingHome checks inventory for the required ingredients by the order and deducts the necessary amount from inventory (if insufficient it deducts to zero).
-
BakingHome marks the order(s) as complete.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index(indices) is(are) invalid.
-
3a1. BakingHome shows an error message.
Use case resumes at step 2.
-
MSS
-
User requests to list all sales.
-
BakingHome shows a list of sales.
-
User requests to delete a specific sale or multiple sales in the list.
-
BakingHome deletes the sale(s).
Use case ends.
MSS
-
User requests to list all active orders.
-
BakingHome shows a list of active orders.
Use case ends.
Precondition: BakingHome has at least 1 product in the product list.
MSS
-
User request to list products
-
BakingHome lists products
-
User request to view a specific product’s ingredients
-
BakingHome shows the ingredients of that product
Use case ends.
-
3a. The given index(indices) is(are) invalid.
-
3a1. BakingHome shows an error message.
Use case resumes at step 2.
-
-
-
Precondition: User has at least 1 product in the product list.
-
MSS
-
User can be viewing any pages.
-
User enters a ShowProductCommand indicating the index of the product to be shown, e.g.
product show 1. -
BakingHome parses the command.
-
BakingHome executes the command.
-
BakingHome displays the details of the product.
-
-
Extensions
-
3a. BakingHome detects a invalid command.
-
3a1. BakingHome shows an error message
-
3a2. Use case ends.
-
Should be an open-source project.
-
Should be portable (i.e. it does not require installation to run).
-
Should a single user application.
-
Should work on any mainstream OS as long as it has Java
11installed. -
Should be able to hold up to 150 entities without a noticeable sluggishness in performance for typical usage.
-
Should have a low response time of not more than 2 seconds.
-
Changes are saved automatically and no manual saving is needed.
-
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.
-
A user would be able to execute every operation with typing only, and without the assist of a mouse
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. |
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Run the jar file in console using
java -jar
Expected: Shows the GUI. The window size may not be optimum.
-
-
Showdown
-
Enter
exitin command box.
Expected: The application quits.
-
-
Dealing with missing/corrupted data files
-
Test case (missing data file): Delete the folder
datain BakingHome’s directory and restart BakingHome
Expected: BakingHome loads demo data -
Test case (corrupted data file): Change the content of data file at
data/baking.jsonto "This file is damaged" and restart BakingHome.
Expected: BakingHome loads demo data
-
BakingHome provides basic Add, Edit, Delete operations for all sections.
| Section | Test Case | Expected Output |
|---|---|---|
Products |
Product with name |
|
|
A new product named Bread is added. Ingredient cost and Retail price are both $0.0. Status is active. |
|
|
A new product named |
|
Orders |
|
A new order with no item is added. The order’s customer name field is Jiajun |
|
An error message pops up. The error message is "Name should be no more than 20 characters" |
|
|
An error message pops up. The error message is "Remarks should be no more than 50 characters" |
|
Pre-requisite for below |
||
|
A new order with one item Cake is added. |
|
|
A error message pops up. |
|
Sales |
Adding an empty sale |
|
|
A new sale with default value 0.0 is added. The sale’s description and remarks are "N/A" and date is set to current date and time. |
|
|
An error message pops up. The error message is "Description should be no more than 50 characters" |
|
|
An error message pops up. The error message is "Remarks should be no more than 50 characters" |
|
Adding a sale with date |
||
|
A new sale with date Tue, Dec 31, 2019 23:00 is added |
|
|
An error message pops up |
|
Prerequisite: At least one entry in each list.
| Section | Test Case | Expected Output |
|---|---|---|
Shopping |
|
In the shopping list, the first ingredient’s quantity is changed to 10, and its unit cost is changed to 10. All other data that is not inputted as parameters will be unchanged. |
Products |
|
The name of the first product is changed to _name, and cost is changed to $5. It has a ingredient Cream cheese associated. You can check the ingredient using command |
Orders |
|
In orders, the first order’s customer name is changed to Eugene, and the remarks is changed to Birthday All other data that is not inputted as parameters will not be changed. This is provided the order status is not completed. If the order status is completed, you cannot edit the order. |
Delete operation have the same syntax for all five sections.
The table below use order as an example.
order
can be replaced with inv, shop, product and sale
| Section | Test Case | Expected Output |
|---|---|---|
Orders |
Deleting a single order Prerequisites: At least 1 order in Order List |
|
|
The first order is deleted from the list |
|
|
No order is deleted. Error details shown in the pop-up bar |
|
Deleting multiple orders Prerequisites: At least 2 orders in Order List |
||
|
The first and second orders are deleted |
|
|
The first and second orders are deleted |
|
1. 2. 3. |
Error details shown in the pop-up bar. |
|
Sales can be filtered by time.
| Section | Test Case | Expected Output \ |
|---|---|---|
Sales |
Showing only sales between two dates not inclusive of date itself. |
|
|
Sale entries starting from 02/01/2019 00:00 to 09/01/2019 23:59 are shown |
|
|
Error is shown. Nothing changes to sale. Error details shown in the pop-up bar. |
|
| Feature | Test Case | Expected |
|---|---|---|
Buying a single ingredient. Prerequisites: At least 1 ingredient in Shopping List. |
|
The first ingredient is bought and transferred to Inventory List. |
|
No ingredients are bought. Error details shown in the pop-up bar. |
|
|
No ingredients are bought. Error details shown in the pop-up bar. |
|
Buying multiple ingredients. Prerequisites: At least 2 ingredients in Shopping List. |
|
The first and second ingredients are bought and transferred to Inventory List. |
|
No ingredients are bought. Error details shown in the pop-up bar. |
| Feature | Test Case | Expected |
|---|---|---|
Searching for products whose name contains the given keyword |
|
All products whose name include |
Filtering Products by Categories, namely |
|
Only active products are listed. |
|
Both active and archived products are listed. Note that no archived products will be shown if no products are archived. |
|
Sorting Products. |
|
Active products are sorted by cost in ascending order. |
|
Active products are sorted by price in descending order. |







