Developer Guide
Table of Contents
- Acknowledgements
- Setting up
- Design
- Implementation
- Documentation
- Appendix: Requirements
- Appendix: Instruction for Manual Testing
- Appendix: Planned Enhancements
- Appendix: Effort
Acknowledgements
PharmHub is built upon AddressBook-Level3 project created by the SE-EDU initiative.
The following libraries have been used
Setting up, getting started
Refer to the guide Setting up and getting started.
Design

.puml
files used to create diagrams in this document docs/diagrams
folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.
Architecture
The Architecture Diagram given above explains the high-level design of the App.
Given below is a quick overview of main components and how they interact with each other.
Main components of the architecture
Main
(consisting of classes Main
and MainApp
) is in charge of the app launch and shut down.
- At app launch, it initializes the other components in the correct sequence, and connects them up with each other.
- At shut down, it shuts down the other components and invokes cleanup methods where necessary.
The bulk of the app’s work is done by the following four components:
-
UI
: The UI of the App. -
Logic
: The command executor. -
Model
: Holds the data of the App in memory. -
Storage
: Reads data from, and writes data to, the hard disk.
Commons
represents a collection of classes used by multiple other components.
How the architecture components interact with each other
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command deletep 1
.
Each of the four main components (also shown in the diagram above),
- defines its API in an
interface
with the same name as the Component. - implements its functionality using a concrete
{Component Name}Manager
class (which follows the corresponding APIinterface
mentioned in the previous point.
For example, the Logic
component defines its API in the Logic.java
interface and implements its functionality using the LogicManager.java
class which follows the Logic
interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component’s being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
The sections below give more details of each component.
UI component
The API of this component is specified in Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, PersonListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class which captures the commonalities between classes that represent parts of the visible GUI.
The UI
component uses the 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. - listens for changes to
Model
data so that the UI can be updated with the modified data. - keeps a reference to the
Logic
component, because theUI
relies on theLogic
to execute commands. - depends on some classes in the
Model
component, as it displaysPerson
object residing in theModel
.
Logic component
API : Logic.java
Here’s a (partial) class diagram of the Logic
component:
The sequence diagram below illustrates the interactions within the Logic
component, taking execute("deletep 1")
API call as an example.

DeletePersonCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
How the Logic
component works:
- When
Logic
is called upon to execute a command, it is passed to anPharmHubParser
object which in turn creates a parser that matches the command (e.g.,DeletePersonCommandParser
) and uses it to parse the command. - This results in a
Command
object (more precisely, an object of one of its subclasses e.g.,DeletePersonCommand
) which is executed by theLogicManager
. - The command can communicate with the
Model
when it is executed (e.g. to delete a person). - The result of the command execution is encapsulated as a
CommandResult
object which is returned back fromLogic
.
Here are the other classes in Logic
(omitted from the class diagram above) that are used for parsing a user command:
How the parsing works:
- When called upon to parse a user command, the
PharmHubParser
class creates anXYZCommandParser
(XYZ
is a placeholder for the specific command name e.g.,AddPersonCommandParser
) which uses the other classes shown above to parse the user command and create aXYZCommand
object (e.g.,AddPersonCommand
) which thePharmHubParser
returns back as aCommand
object. - All
XYZCommandParser
classes (e.g.,AddOrderCommandParser
,DeletePersonCommandParser
, …) inherit from theParser
interface so that they can be treated similarly where possible e.g, during testing.
Model component
API : Model.java
The Model
component,
- stores the PharmHub data i.e., all
Person
,Order
, andMedicine
objects. - stores the currently ‘selected’
Person
,Order
,Medicine
objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiableObservableList<Person>
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. - stores a
UserPref
object that represents the user’s preferences. This is exposed to the outside as aReadOnlyUserPref
objects. - does not depend on any of the other three components (as the
Model
represents data entities of the domain, they should make sense on their own without depending on other components)
Storage component
API : Storage.java
The Storage
component,
- can save both PharmHub data and user preference data in JSON format, and read them back into corresponding objects.
- inherits from both
PharmHubStorage
andUserPrefStorage
, which means it can be treated as either one (if only the functionality of only one is needed). - depends on some classes in the
Model
component (because theStorage
component’s job is to save/retrieve objects that belong to theModel
)- The Main Application has 3 JsonAdapted component in storage:
JsonAdaptedPerson
,JsonAdaptedOrder
,JsonAdaptedMedicine
.-
JsonAdaptedPerson
further containsJsonAdaptedTag
andJsonAdaptedAllergy
. -
JsonAdaptedOrder
further containsJsonAdaptedStatus
andJsonAdaptedMedicine
.`
-
- The Main Application has 3 JsonAdapted component in storage:
Common classes
Classes used by multiple components are in the seedu.pharmHub.commons
package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
ListX feature
The listing functionality is supported by the listPanelPlaceholder in the Ui.
On start of application, two listPanels (PersonListPanel, OrderListPanel) are created, and person list panel is attached to the ListPanel Placeholder as default.
On execution of the any listing commands (listo
or listp
), the resultant CommandResult contains details on which
panel to choose to display.
These are 3 different options for these details:
- Person - Panel will display list of people
- Order - Panel will display list of orders
- NoChange - Panel will keep whatever was there previously
Using these specifications, the CommandResult from executing listo
and listp
will inform the Ui of which
panel to attach to the listPanelPlaceHolder
ViewX feature
The viewp/ viewo
feature allows users to view details of a person/ order in the info display panel. This is facilitated with the new interface, InfoObject
, which classes that want to be displayed are required to implement.
To support this feature, CommandResult
has the field InfoObject
. If present (not null), the UI will create and attach a view for that InfoObject
onto the Info Display.
The code excerpt for MainWindow#handleDisplayInfo(InfoObject)
below shows how the InfoDisplay is rendered:
@FXML
protected void handleDisplayInfo(InfoObject objectToDisplay) {
assert(objectToDisplay instanceof Order || objectToDisplay instanceof Person);
if (objectToDisplay instanceof Order) {
Order order = (Order) objectToDisplay;
OrderDisplay orderDisplay = new OrderDisplay(order);
infoDisplay.attach(orderDisplay)
} else if (objectToDisplay instanceof Person) {
Person person = (Person) objectToDisplay;
PersonDisplay personDisplay = new PersonDisplay(person);
infoDisplay.attach(personDisplay)
} else {
throw new RuntimeException("Invalid object to display");
}
}
The following sequence diagram shows how the viewp
operation works:
The following activity diagram summarizes what happens when a user executes a new command:

Design considerations:
-
Aspect: How the UI is informed about the object to display:
-
Alternative 1 (current choice): Require Classes that want to be displayed to implement
InfoObject
- Pros: Allows
CommandResult
to contain a uniform type (InfoObject
), which scales easily. Also gives control over which classes can be displayed, and which classes cannot (yet). - Cons: Empty
InfoObject
interface may lead to confusion for new developers.
- Pros: Allows
-
Alternative 2: Have
CommandResult
contain all types of objects that we want displayed, iePerson
andOrder
- Pros: Possibly clearer in intent
- Cons: Not easily scalable
-
Alternative 1 (current choice): Require Classes that want to be displayed to implement
-
Aspect: Creation of relevant
InfoObject
UI component:-
Alternative 1 (current choice): If-else checks in
MainWindow#handleDisplayInfo
, and create the required UI component for each clause- Pros: Reduces coupling between
Person
(andOrder
) model and UI - Cons: Less scalable
- Pros: Reduces coupling between
-
Alternative 2: Create abstract method
InfoObject#createUIComponent
, and have Classes that wish to be displayed implement this method and create their own UI components- Pros: Easily scalable - each class implements their own UI display to attach into the placeholder
- Cons: Increased coupling between UI and Model. UI-creation code exists inside the
Person
(orOrder
) Class
-
Alternative 1 (current choice): If-else checks in
Edit person feature
Implementation
After adding a person in PharmHub, the user would be able to edit the person using the editp
command with the following format:
editp INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]… [no/ALLERGY]… [ia/]
The command edits the person information based on the given fields. At least one field must be updated.
Existing values will be replaced with the new input values.
If the edits cause the person to be allergic to any orders associated with them, a warning will be raised. Using the ia/
flag will override this warning.
This command cannot add or delete orders for this person. It’s limited to editing the person’s information only.
Prerequisite: There should be a person at index 1, with “Aspirin” in the PharmHub medicine list and also among one or more of his/her orders.
The following sequence diagram illustrates some of the steps that happen when user execute editp 1 no/Aspirin
Steps to trigger
Step 1: The user executes editp 1 no/Aspirin
to add the allergy “Aspirin” to the person at index 1.
Step 2: Logic Manager calls PharmHubParser#parse
which extracts the arguments and calls EditPersonCommandParser
.
Step 3: EditPersonCommandParser
parses the index and the allergy and returns an EditPersonCommand
.
Step 4: LogicManager
calls EditPersonCommand#execute
to edit the details of the person.
Step 5: Since an allergy is added in this command, and an ia/
flag is not passed as an argument, EditPersonCommand
checks among the list of order of the person whether there are any medications that match this allergy by calling the hasOrderConflict()
function.
Step 6: Since there are medications that match this allergy, a CommandException
is returned.
Step 7: An error message is shown to warn the user that the person’s new allergy conflicts with existing orders, and hints the user about adding the ia/
flag to add the allergy to the person anyway.
EditPersonCommand#execute
utilizes the following method Person#hasOrderConflicts
to check if there are any conflicts between the person’s allergy and his/her orders.
public boolean hasOrderConflicts() {
return orders.stream().anyMatch(o -> isAllergicToAny(o.getMedicines()));
}
}
Person#isAllergicToAny(medicines)
returns true if the person is allergic to any of the given medicines.
public boolean isAllergicToAny(Set<Medicine> medicines) {
return medicines.stream().anyMatch(medicine -> isAllergicTo(medicine));
}
Add medicine short form feature
Implementation
After the creation of new Medicine
, a short form can be assigned to that Medicine
using the sfm
command.
After this command, the user would be able to use this short form interchangeably with the full name in fields requiring medicine or allergy names.
Currently, only one short form can be assigned to one Medicine
at a time.
Steps to trigger
Step 1: The User launches the application.
Step 2: The user executes sfm 1 m/pan
to add the short form of pan
to the medicine at index 1 in the last shown medicine list.
Step 3: Logic Manager calls PharmHubParser#parse
which extracts the arguments and calls ShortFormCommandParser
Step 4: ShortFormCommandParser
parses the index, short form name and returns a ShortFormComamnd
Step 5: LogicManager
calls ShortFormCommand#execute
to assign the short form to the medicine.
Step 6: ShortFormCommand
checks if an existing medicine with the same name or same short form is already present using Model#hasMedicine(m)
.
Step 7: The Medicine
at index 1 is replaced with a new Medicine
which has same medicine name but with pan
as the short form.
The following sequence diagram illustrates some of the steps that happen when user execute sfm 1 m/pan

Model#hasMedicine(m)
utilizes the following method Medicine#isSameMedicine
to check equality of two Medicine
.
public boolean isSameMedicine(Medicine m) {
if (m == this) {
return true;
}
if (m == null) {
return false;
}
return (medicineName.equalsIgnoreCase(m.medicineName)
|| medicineName.equalsIgnoreCase(m.shortForm)
|| m.medicineName.equalsIgnoreCase(shortForm));
}
Delete medicine short form feature
Implementation
sfm
can also be used to delete the short form of a medicine by providing the d/
flag.
If the d/
flag is provided any provided medicine names using m/
will be ignored.
Hence sfm 1 m/pan d/
will
be treated as deleting the short form of medicine at index 1 in the last shown medicine list.
Steps to trigger
Given below is an example scenario to delete the short form of medicine at index 1.
Step 1. The user lists all medicines using the listm
command.
Step 2. The user deletes the medicine at 1st index using sfm 1 m/pan d/
Step 3: Logic Manager calls PharmHubParser#parse
which extracts the arguments and calls ShortFormCommandParser
Step 4: ShortFormCommandParser
parses the index, ignores the short form and returns a ShortFormComamnd
Step 5: LogicManager
calls ShortFormCommand#execute
to delete the short form of medicine at index 1.
Step 6: The Medicine
at index 1 is replaced with a new Medicine
which has same medicine name but with no short form.
The following activity diagram summarises the sequence of steps for the whole sfm
command.
Adding an Order feature
Implementation
The adding an Order feature allows the user to add an order to a person.
- The add order feature includes the ignore allergy flag to allow further flexibility for the pharmacist when assigning orders to people. This is because they may have other considerations when it comes to assigning a medication for a person.
- The add order feature do not allow duplicated order as we believe that each order should be distinct base on their order number, ensuring that no orders go missing or forgotten.
Steps to Trigger
- The User launches the application.
- The User executes “addo 1 m/aspirin o/1” to add a new order.
- The
AddOrderParser#parse()
checks whether all the prefixes and the required values are provided. - If the check is successful, the
ParseUtil#parseOrderNumber
,ParseUtil#parseOrderNumber
andParseUtil#parseMedicine
will then check and create anIndex
,OrderNumber
andMedicine
passing it toAddOrderCommand
. - Depending on whether an ignore allergy flag
ia/
is added in the command input, user can input medications in the order that the person is allergic to. -
AddOrderCommand#execute
then checks if the person exist base on index and creates an Order with theperson
,orderNumber
,medicine
and aPENDING
Status, thenModel#hasOrder
also checks whether the order is a duplicated order. - If the order does not exist, then the
Model#addOrder
adds the order into the order list.
The following sequence diagram shows how addo
works on an example input. addo 1 m/aspirin o/1000
Finding an Order Feature
Implementation
The finding an Order feature allows the user to find other base on either the orderStatus, medication in the order or both.
- The find order feature allows independent Predicate finding.
- i.e. you can find order base on only
Status
orMedicine
. - We implemented this format to expend the utility of the feature.
- i.e. you can find order base on only
Steps to Trigger
- The User Launches the application.
- The User decides to find/filter through the order list after adding multiple orders.
- The User executes “findo s/pd m/panadol” to find orders that has
PENDING
status andPANADOL
in the order. - The
FindOrderCommandParser#parse()
checks whether all the prefixes and the required values are provided. - If the check is successful, depending on the User input if
s/
is presentParseUtil#parseStatus
will return aStatus
, and ifm/
is presentParseUtil#parseMedicines
will return theMedicine
. - The
Status
andMedicine
will then be passed to the FindOrderCommand. - The
Model#updateFilteredOrder
will then take either or bothStatus
andMedicine
as predicates to filter through the order list and return valid orders. - The filtered order list will then be returned and shown on the Displayed list.
The following sequence diagram shows how findo
works on an example input. findo s/pd m/panadol
Undo/redo feature
The undo/redo mechanism is facilitated by VersionedPharmHub
. It extends PharmHub
with an undo/redo history, stored internally as an undoHistory Deque
and redoHistory Deque
. Additionally, it implements the following operations:
-
VersionedPharmHub#canUndo()
— Checks if there is a previous PharmHub state to revert to -
VersionedPharmHub#undo()
— Restores the previous PharmHub state from its `undoHistory. -
VersionedPharmHub#canRedo()
— Checks if there is a future PharmHub state to revert to. -
VersionedPharmHub#redo()
— Restores a previously undone PharmHub state from itsredoHistory
.
These operations are exposed in the Model
interface as Model#canUndo()
, Model#undo()
, Model#canRedo()
and Model#redo()
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 VersionedPharmHub
will be initialized with the initial pharmHub state, and the CurrentState
points to that single pharmHub state. Both undoHistory
and redoHistory
will be empty.
Step 2. The user executes deletep 5
command to delete the 5th person in PharmHub. The deletep
command calls Model#deletePerson
, which in turn calls VersionedPharmHub#deletePerson
. This causes the VersionedPharmHub
to save the current state into undoHistory
, before calling PharmHub#deletePerson
, which deletes the person from PharmHub
Step 3. The user executes addp n/David …
to add a new person. The addp
command calls Model#addPerson
, which in turn calls VersionedPharmHub#addPerson
repeating the process in step 2.
Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the undo
command. The undo
command will call Model#undo()
, which does the following things in order. It saves the current state into redoHistory
, updates Current State
to the last state in undoHistory
, and removes that last state from undoHistory
.

Model
will first check if an undo is possible in the first place, and it does so by calling VersionedPharmHub#canUndo()
, which checks if undoHistory
is empty. If an undo operation cannot be done, an error is thrown by the Model
.The following sequence diagram shows how the undo operation works:

UndoCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
The redo
command does the opposite — it calls Model#redo()
, which saves the Current State
into undoHistory
, sets the Current State
as the last state in redoHistory
, and removes said state from redoHistory
.

undo
, if Model#canRedo
returns False
, ie. the redoHistory
is empty, then the Model
throws an error.
Step 5. The user then decides to execute the command listp
. Commands that do not modify data in PharmHub, such as listp
, will not change the undoHistory
or redoHistory
Step 6. The user executes clear
, which calls Model#Clear()
, which in turn calls VersionedPharmHub#Clear()
. As before, the Current State
is added to undoHistory
, and then PharmHub#clear()
is called, which changes the state. Moreover, the redoHistory
will be cleared, preventing users from running redo
after a data-modifying non-undo command. Reason: This is how most applications handle undo/redo.
The following activity diagram summarizes what happens when a user executes a new command:
Design considerations:
-
Aspect: How undo & redo executes:
-
Alternative 1 (current choice): Saves the entire pharmHub.
- 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
deletep
, just save the person being deleted). - Cons: We must ensure that the implementation of each individual command are correct.
- Pros: Will use less memory (e.g. for
-
Alternative 1 (current choice): Saves the entire pharmHub.
-
Aspect: Effects (Scale) of undo & redo:
-
Alternative 1 (current choice): Only affects data-modifying operations
- Pros: Simple to implement, provides essential functionality
- Cons: Non-data-modifying commands cannot be undone, eg.
listp
-
Alternative 2: Affects all commands
- Pros: Changes in view can be undone, leading to greater convenience. Easily extensible to undo certain views
- Cons: Versioning has to be extended to keep track of UI, which may lead to increased coupling.
-
Alternative 1 (current choice): Only affects data-modifying operations
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile:
Tech-savvy fresh-graduate remote pharmacist managing medication orders for a small-medium sized business, looking to move up the corporate ladder quickly.
Value proposition:
PharmHub provides an efficient yet guarded approach towards processing orders. It allows users to go through the lifecycle of order-fulfillment quickly, whilst protecting against erroneous entries and providing a quick and efficient methods of correcting mistakes.
It is also optimised for CLI use.
User stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
* * * |
Pharmacist | Add a new patient into PharmHub | Keep track of all information of my patients |
* * * |
Pharmacist | Add a medication order for a patient | Process new orders |
* * * |
Pharmacist | Add a medicine into PharmHub | Update the inventory of my pharmacy |
* * * |
Pharmacist | Add allergies of a patient towards certain medications | Keep track of patient allergies |
* * * |
Pharmacist | View the status of an order | I am able to keep track of the state of an order |
* * * |
Pharmacist | Update the status of an order | Update the order records with the most current information |
* * * |
Pharmacist | Edit the details of a patient | Update patient records with the most current information |
* * * |
Pharmacist | View all medication orders | Have an overview of all medication orders |
* * * |
Pharmacist | Delete a patient from PharmHub | Remove a patient that is no longer under my care |
* * * |
New user | View a summary of all the commands | Recall how to use the application |
* * * |
Pharmacist | Delete an order from the system | Remove erroneous orders |
* * * |
Detail-oriented Pharmacist | Be alerted if a patient is given a medication that he is allergic to | Detect and prevent erroneous orders from going through |
* * * |
Time-efficient Pharmacist | Be alerted if a medication in an order is nonsensical | Waste less time looking for a non-existent medication |
* * * |
Mistake-prone Pharmacist | Undo/Redo my last action | Correct mistakes made into the system quickly and effectively |
* * |
Busy Pharmacist | Abbreviate medication names | Maximise my efficiency and type less |
* * |
Pharmacist | View all details of a patient (including past/ present orders for that patient) | Easily track orders for that patient |
* * |
Forgetful Pharmacist | Search for orders by their status | Find out which orders to prepare next |
* * |
Time-efficient Pharmacist | Search for orders by their medication name | Prepare all orders that contain a particular medicine simultaneously |
* * |
Result-oriented Pharmacist | See the number of unfulfilled orders | Keep track of my progress in dispensing orders |
* * |
Pharmacist | Add priorities to orders | Keep track of an order’s priority |
* * |
Pharmacist | Sort orders on various categories (eg. Priority, Time) | Fulfill the important orders first |
* |
Pharmacist | Edit an order | Correct a mistake made in an order |
* |
Pharmacist | Track my medication inventory | I have easy access to the amount of medication I have |
* |
Pharmacist | Calculate estimated time an order will take to ship | Notify patients of an estimated wait time before receiving their medication |
Use cases
For all use cases below, the Software System is the PharmHub
and the Actor is the user
, unless specified otherwise.
Use case: UC01 - Adding a person
Guarantees: The new person(patient) will be added to the list of Person only if the person does not exist in the system.
MSS
- User chooses to add a person.
- PharmHub adds the new person to the person list.
- PharmHub displays the updated list and person.
Use case ends
Extensions
-
1a. PharmHub detects an error in the input.
- 1a1. PharmHub shows error message.
- 1a2. User enters new input.
- Steps 1a1-1a2 are repeated until the input are correct.
Use case resumes from step 2.
Use case: UC02 - Listing Person list
Guarantees: The Person List would be displayed.
MSS
- User adds a person UC01.
- User chooses to view the list.
- PharmHub shows the Person list.
Use case ends
Extensions
-
2a. PharmHub detects an error in the input.
- 2a1. PharmHub shows error message.
- 2a2. User enters new input.
- Steps 2a1-2a2 are repeated until the input are correct.
Use case resumes from step 3.
-
2a. The list is empty.
Use case ends
Use case: UC03 - Editing Person Detail
Guarantees: The Person’s detail would be updated only if the person exists in the system.
MSS
- User adds a person UC01.
- User views the person list UC02.
- User chooses to edit a person’s detail.
- PharmHub updates the Person list with the updated information
- PharmHub displays the person.
Use case ends
Extensions
-
3a. PharmHub detects an error in the input.
- 3a1. PharmHub shows error message.
- 3a2. User enters new input.
- Steps 3a1-3a2 are repeated until the input are correct.
Use case resumes from step 4.
Use case: UC04 - Adding a medicine
Guarantees: The new medicine will be added to the list of medicines only if the medicine does not exist in the system.
MSS
- User chooses to add a medicine.
- PharmHub adds the new medicine to the medicine list.
Use case ends
Extensions
-
1a. Medicine already exists in the system.
- 1a1. PharmHub shows error message.
- 1a2. User enters new input.
- Steps 1a1-1a2 are repeated until the input is correct.
Use case resumes from step 2.
Use case: UC05 - Listing Medicine list
Guarantees: The Medicine List would be displayed.
MSS
- User adds a medicine UC04.
- User chooses to view the medicine list.
- PharmHub shows the Medicine list.
Use case ends
Extensions
-
2a. PharmHub detects an error in the input.
- 2a1. PharmHub shows error message.
- 2a2. User enters new input.
- Steps 2a1-2a2 are repeated until the input are correct.
Use case resumes from step 3.
-
2b. The list is empty.
Use case ends
Use case: UC06 - Adding a medicine short form
Guarantees: The Medicine would have a short form only if the medicine exist in the system and the short form is unique.
MSS
- User adds a Medicine UC04.
- User views the person list UC05.
- User chooses to add a short form for medicine.
- PharmHub updates Medicine list with short form.
Use case ends
Extensions
-
3a. PharmHub detects an error in the input.
- 3a1. PharmHub shows error message.
- 3a2. User enters new input.
- Steps 3a1-3a2 are repeated until the input are correct.
Use case resumes from step 4.
Use case: UC07 - Deleting a medicine short form
Guarantees: The short form for the medicine will be removed.
MSS
- User adds a short form U06.
- User chooses to delete the short form.
- PharmHub updates Medicine list with an empty short form.
Use case ends
Extensions
-
2a. PharmHub detects an error in the input.
- 2a1. PharmHub shows error message.
- 2a2. User enters new input.
- Steps 2a1-2a2 are repeated until the input are correct.
Use case resumes from step 3.
Use Case: UC08 - Add an Order for a Person
Guarantees: The order is added to the person only if the person exist while the order does not exist in the system.
MSS
- User views the person list UC02.
- User chooses to add an order.
- PharmHub adds the new order to the order list.
- PharmHub displays the order.
Use case ends
Extensions
-
2a. PharmHub detects an error in the input.
- 2a1. PharmHub shows error message.
- 2a2. User enters new input.
- Steps 2a1-2a2 are repeated until the input are correct.
Use case resumes from step 3.
-
2b. The person is allergic to the input medication.
- 2b1. The user input the wrong medication resulting in the error.
- 2b1a. PharmHub warns the user.
Use case resumes from step 2.
- 2b1a. PharmHub warns the user.
- 2b2. The User wants to ignore the warning.
- 2b2a. PharmHub warns the user
- 2b2b. User acknowledges the allergy warning but chooses to ignore it.
- 2b2c. PharmHub creates the order with the provided information.
Use case resumes from step 3.
- 2b1. The user input the wrong medication resulting in the error.
In this use case, a pharmacist adds a medication order for a patient using PharmHub. The system first allows the pharmacist to select a patient from the list and provide order details. It then checks for potential contraindications based on the patient’s allergies and issues a warning if necessary. The pharmacist can acknowledge the warning and proceed with the order by adding an “IA” flag to the command. Once confirmed, the system records the order and sends a confirmation to the pharmacist.
Use case: UC09 - Listing Order list
Guarantees: The Order List would be displayed.
MSS
- User adds an order UC08.
- User chooses to view the order list.
- PharmHub shows the Order list.
Use case ends
Extensions
-
2a. PharmHub detects an error in the input.
- 2a1. PharmHub shows error message.
- 2a2. User enters new input.
- Steps 2a1-2a2 are repeated until the input are correct.
Use case resumes from step 3.
-
2b. The list is empty.
Use case ends
Use case: UC10 - Update Order Status
Guarantees: The order status will be updated only if the order exist in the system and the new status is of chronological order.
MSS
- User views the order list UC09
- User chooses to update the order status.
- PharmHub updates the order status.
- PharmHub displays updated order
Use case ends
Extensions
-
1a. The list is empty.
Use case ends -
2a. PharmHub detects an error in the input.
- 2a1. PharmHub shows error message.
- 2a2. User enters new input.
- Steps 2a1-2a2 are repeated until the input are correct.
Use case resumes from step 3.
Use case: UC11 - Finding person
Guarantees: The list of person that contains the keywords would be displayed.
MSS
- User added multiple person UC01.
- User chooses to the find person command.
- PharmHub filters through the person list.
- PharmHub then displays the filtered person list.
Use case ends
Extensions
-
2a. PharmHub detects an error in the input.
- 2a1. PharmHub shows error message.
- 2a2. User enters new input.
- Steps 2a1-2a2 are repeated until the input are correct.
Use case resumes from step 3.
-
3a. The keywords given does not match with any person.
- 3a1. Empty list is shown.
Use case ends.
- 3a1. Empty list is shown.
Use case: UC12 - Finding orders
Guarantees: The list of Orders that fulfills the status or medicine or both will be displayed.
MSS
- User added multiple order UC08.
- User chooses to find orders base on their status or medicine or both.
- PharmHub filters through the order list.
- PharmHub then displays the filtered order list.
Use case ends
Extensions
-
2a. PharmHub detects an error in the input.
- 2a1. PharmHub shows error message.
- 2a2. User enters new input.
- Steps 2a1-2a2 are repeated until the input are correct.
Use case resumes from step 3.
-
3a. The status and medicine keyword given does not match with any order.
- 3a1. Empty list is shown.
Use case Ends.
- 3a1. Empty list is shown.
-
3b. User input invalid fields.
-
3b1. PharmHub detects an error in the input.
- 3b1a. PharmHub shows error message.
- 3b1b. User enters new input.
- Steps 3b1a-3b1b are repeated until the input are correct.
Use case resumes from step 4.
-
Non-Functional Requirements
- Should work on any mainstream OS as long as it has Java
11
or above installed. - 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.
- Each command should take no longer than 0.1s to execute.
- Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.
- Should be able to hold up to 1000 orders without a noticeable sluggishness in performance for typical usage.
- Should be able to hold up to 1000 medicines without a noticeable sluggishness in performance for typical usage.
- Application should be a standalone executable so that it doesn’t require the user to install other libraries to run.
- Application should be smaller than 100mb so that application can be run on space constrained systems.
- Generated storage file shouldn’t take up more than 100mb of storage so that the application can be run on space constrained systems.
Glossary
Application
- Patient: A person who is under the care of a Pharmacist.
- Pharmacist: A person who is responsible for dispensing Medicine to Patients.
- Order: An order by a Patient for one or multiple Medicines from a Pharmacy.
- Medicine: A substance used for medical treatment.
- Allergy: A Medicine that a Patient is allergic to.
General
- Mainstream OS: Windows, Linux, Unix, OS-X
- CLI: Command Line Interface
- GUI: Graphical User Interface
- API: Application Programming Interface
- MSS: Main Success Scenario (or, Happy Path)
Appendix: Instructions for manual testing
Given below are instructions to test the app manually.

Launch and shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file Expected: The application opens. The window size may not be optimum.
-
-
Saving window preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
Adding a person
-
Adding a Person with Valid Details
-
Test Case:
addp n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01
Expected: John Doe is added as a new person to PharmHub. All provided details, including the name, phone number, email, and address, are correctly saved. No errors or warnings are displayed.
-
Test Case:
addp n/Betsy Crowe t/diabetic e/betsycrowe@example.com a/19 Kent Ridge Crescent p/1234567 t/senior
Expected: Betsy Crowe is added as a new person to PharmHub. All provided details, including the name, phone number, email, and address, tags, and allergies, are correctly saved. No errors or warnings are displayed.
-
-
Adding a Person with Incorrect/Incomplete Information
-
addp n/Jane Doe
Expected: An error message is displayed due to the absence of the phone number, email, and address. The person isn’t added as these details are mandatory.
-
-
Adding a Person with Duplicate Name
-
Prerequisites: A person with name “John Doe” already exists in PharmHub. This can be done using the command in 1.i.
-
Test case:
addp n/John Doe p/9872 e/anotherjohnd@example.com a/Jane street, block 42, #03-02
Expected: An error message is displayed due to the presence of a person with the same name. The person isn’t added as names must be unique.
-
-
Adding a Person with an allergy which is not yet in the Medicine list
-
Prerequisites: A persons list without the person with name “John Doe”. A medicine list without the medicine “Aspirin”.
-
Test case:
addp n/John Doe p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 no/Aspirin
Expected: An error message is displayed due to the presence of an allergy which is not in the medicine list. The person isn’t added as the allergy must be in the medicine list.
-
Deleting a person
-
Deleting a person while all persons are being shown
-
Prerequisites: List all persons using the
listp
command. Multiple persons in the list. -
Test case:
deletep 1
Expected: First contact is deleted from the list. Details of the deleted contact is shown in Info Display.
-
Test case:
deletep 0
Expected: No person is deleted. An error message is displayed due to an invalid index being used. -
Other incorrect delete commands to try:
deletep
,deletep x
,...
(where x is larger than the list size)Expected: Similar to previous.
-
Viewing a person
-
Viewing a person with valid index
-
Prerequisites: There is at least 1 person in the persons list of PharmHub. This can be done using the command in 1.i. This can be checked using the command:
listp
-
Test case:
viewp 1
Expected: First contact is shown in the Info Display.
-
Test case:
viewp 0
Expected: No person is shown. An error message is displayed due to an invalid index being used.
-
Other incorrect view commands to try:
viewp
,viewp x
(where x is larger than the list size)Expected: No person is shown. An error message id displayed due to an invalid index being used.
-
Editing a person
-
Editing a person using valid fields
-
Prerequisites: List all persons using the
list
command. Multiple persons in the list. No person named ‘Johnson Doe’ in PharmHub. -
Test case:
editp 1 n/Johnson Doe p/98765432
Expected: First person is edited. Details of the edited person is shown in the Info Display.
-
Finding person(s)
-
Finding person(s) using valid fields
-
Test case:
findp n/John
Expected: The person(s) with the word “John” or “john” in his/her name are listed in the display list panel.
-
Test case:
findp n/Alex John
Expected: All person(s) with name containing “Alex”, “alex”, “John”, or “john” are listed in the display list panel.
-
Test case:
findp no/Aspirin Paracetamol
Expected: All person(s) with allergy containing “Aspirin” or “Paracetamol” are listed in the display list panel.
-
Test case:
findp p/123456
Expected: All person(s) with phone number exactly equal to “123456” are listed in the display list panel.
-
Test case:
findp e/john@gmail.com
Expected: All person(s) with email exactly equal to “john@gmail.com” are listed in the display list panel.
-
-
Finding person(s) using invalid input
-
Test case:
findp
Expected: An error message is displayed due to the absence of any field. No person is shown.
-
Test case:
findp n/
Expected: An error message is displayed due to the absence of the name. No person is shown.
-
Listing all medicines
-
Test case:
listm
Expected: All medicines listed in the display panel.
Finding medicine(s)
-
Test case:
findm pan
Expected: All medicine whose names have
pan
as a substring listed in the list display. The number of medicines listed shown in result display box. -
Test case:
findm pan ol
Expected: All medicine whose names have either
pan
orol
as a substring are listed in the list display. The number of medicines listed shown in result display box.
Adding a medicine
-
Adding a medicine that doesn’t exist
-
Prerequisites: List all medicines using the
listm
command. No medicine namedmetformin
should be in the list. -
Test case:
addm m/metformin
Expected: Medicine is added.
-
Test case:
addm m/
Expected: No medicine is added. An error message is displayed due to the invalid command format.
-
Other incorrect
addm
commands to try:addm
,addm p/
,addm 1
Expected: Similar to previous.
-
-
Adding a medicine that already exists
-
Prerequisites: List all medicines using the
listm
command. A medicine namedmetformin
should be in the list. -
Test case:
addm m/metformin
Expected: Medicine is not added. An error message is displayed because ‘metformin’ already exists in PharmHub.
-
Deleting a medicine
-
Deleting a medicine no person is allergic to and not part of any order.
-
Prerequisites:
metformin
should be in the medicine list. Find it usingfindm metformin
. NoOrder
containsmetformin
and noPerson
has an allergy tometformin
. -
Test case:
deletem 1
Expected: First medicine is deleted from the list.
-
Test case:
deletem 0
Expected: No medicine is deleted. An error message is shown due to an invalid index being used.
-
Other incorrect delete commands to try:
deletem
,deletem x
,...
(where x is larger than the list size)Expected: Similar to previous.
-
-
Deleting a medicine a person is allergic to.
-
Prerequisites:
panadol
should be in the medicine list. If not add it usingaddm
. At least one person should be allergic topanadol
. If not useeditp
to make a person allergic topanadol
.panadol
should be the 1st medicine in the list. If not, findpanadol
usingfindm panadol
-
Test case:
deletem 1
Expected: No medicine is deleted. An error message is shown as a
Person
or andOrder
uses this medicine.
-
Short form of medicine
-
Adding a short form to a medicine
-
Prerequisites:
metformin
should be in the medicine list, it should have no short form and no medicine should havemet
as the short form. It should be the 1st medicine in the medicine list. If not, find it usingfindm metformin
-
Test case:
sfm 1 m/met
Expected: Short form of
met
is added to themetformin
medicine. -
Test case:
sfm 1 met
Expected: Short form is not added to the
metformin
medicine. An error message is shown due to an invalid command format used. -
-
Deleting a short form of a medicine.
-
Prerequisites:
metformin
should be in the medicine list, it should have a short form. It should be the first medicine in the medicine list. If not, find it usingfindm metformin
-
Test case:
sfm 1 d/
Expected: Short form of
metformin
medicine is deleted. -
Test case:
sfm 1 m/ d/
Expected: Short form of
metformin
medicine is deleted. -
Test case:
sfm m/ d/
Expected: Short form of
metformin
medicine is not deleted. An error message is shown due to an invalid format being used.
-
Listing all orders
- Test case:
listo
Expected: All orders listed in the display list panel.
Finding order(s)
-
Finding an order by status short form only
-
Test case:
findo s/pd
Expected: All orders with
PENDING
Status will be listed in the display list panel. The number of orders listed shown in result display box.
-
-
Finding an order by status full form only
-
Test case:
findo s/pending
Expected: All orders with
PENDING
Status will be listed in the display list panel. The number of orders listed shown in result display box.
-
-
Finding an order by multiple medicine keywords
- Test case:
findo m/pan m/ol
Expected: All orders with medicine whose names have
pan
orol
as a substring will be listed in the display list panel. The number of orders listed shown in result display box. - Test case:
-
Finding an order by both status and medicine keywords
- Test case:
findo s/pd m/pan m/ol
Expected: All orders that has both
PENDING
status and with medicine whose names have eitherpan
orol
as a substring will be listed in the display list panel(both conditions - Status and Medicine - must be fulfilled). The number of orders listed shown in result display box. - Test case:
-
Finding an order with an invalid status
-
Test case:
findo s/wowow
Expected: No orders will be listed in the display list panel. An error message will be shown due to an invalid status searched for
-
Adding an order
-
Adding an order that doesn’t exist
-
Prerequisites: List all order using the
listo
command. No order with the order number1234
should be in the list. -
Prerequisites: List all person using the
listp
command. Person base on index that is assigned order to should exist. -
Prerequisites: List all medicine using the
listm
command. Medicine that will be in the order must be in the list. -
Prerequisites: Person should not be assigned to medicine they are allergy to.
-
Test case:
addo 1 m/pan o/1234
Expected: Order is added. Details of the order shown in the Information display.
-
Test case:
addo 1 o/1234
,addo 1 m/pan
Expected: No Order is added due to missing details. An error is displayed due to an invalid format being used.
-
Test case:
addo m/
,addo
Expected: Similar to previous.
-
-
Adding an order that already exists (i.e. OrderNumber already exist)
-
Prerequisites: List all orders using the
listo
command. An order with order number1234
should be in the list. -
Test case:
addo 1 m/pan o/1234
Expected: Order is not added. An error message is shown because an order with the same order number already exists.
-
-
Adding an order with medicine the person is allergy to.
-
Prerequisites: List all orders using the
listo
command. An order with order number2222
should not be in the list. -
Prerequisites: List all person using the
listp
command. The 1st person in the person list should be allergic toparacetamol
. If not, edit his details to make him so. -
Test case:
addo 1 m/paracetamol
Expected: Order is not added. A warning message is displayed as the patient is allergic to
paracetamol
. -
Test case:
addo 1 m/paracetamol ia/
(ignore allergy flagia
)Expected: Order is added. Details of tht order shown in the Information display.
-
Updating the order status
-
Updating an order status with valid chronological order.
-
Prerequisites: List all orders using the
listo
command. The status of the 1st order should bePENDING
. If not, add a new order and make that order the 1st in the order list usingfindo
. -
Test case:
updates 1 s/pr
Expected: Order status updated to
PREPARING
. Details of the updated order is shown in the Information display box. -
Test case:
updates 1 s/completed
Expected: Order status updated to
COMPLETED
. Details of the updated order is shown in the Information display box. -
Test case:
updates 1 s/pending
Expected: Order status not updated. An error message is shown as orders can only be updated to a status of higher hierarchy.
-
Test case:
updates 1 s/pwpw
Expected: Order status not updated. An error message is shown due to an invalid status being entered.
-
Deleting an order
-
Deleting an order
-
Prerequisites: List all orders using the
listo
command. The order to delete should be the 1st in the list shown. -
Test case:
deleteo 1
Expected: First order is deleted from the list. Details of the deleted order is shown in Information display box.
-
Test case:
deleteo
Expected: No order deleted. An error message is shown due to an invalid command format being used.
-
Saving data
-
Dealing with missing/corrupted data files
- {explain how to simulate a missing/corrupted file, and the expected behavior}
Appendix: Planned Enhancements
-
Currently, to delete a short form a medicine the user has to provide
d/
flag through thesfm
command to delete the short form of a medicine at a specified index. However, if any short form name is provided using them/
flag, it will be ignored without any warning. Thus,sfm 1 m/met d/
deletes the short form the medicine at index 1 which ignoringmet
. We plan to make thesfm
command accept exactly one ofm/
ord/
flags. This way the commandsfm 1 m/met d/
would be regarded as an invalid command rather than one which deletes the short form. -
Currently, when adding a medicine there is no way for the user to specify the short form right then. Hence, the user has to first add the medicine followed by using
sfm
command to add a short form to it. We plan to allowaddm
accept an optional parameter for the short form of the medicine being added. -
Currently, the sample data shows incorrect values for tags of persons. We plan to fix this by replacing the current sample tags with tags that are appropriate in a pharmacy setting like
Elderly
,Diabetic
orChild
and so on. -
Currently, there are no sample data for orders. We plan to add some sample orders so that users are better equipped to play around with the application on first start.
-
Currently, some error messages are not standardised. We plan to standardise all error messages across the application with the following format. All error messages will 1. Give an explanation of what went wrong, 2. Provide the appropriate formats and examples for the command that the user is trying to enter.
-
Currently, Undo and Redo commands do not affect the UI. As such, commands such as
listp
andfindp
cannot be undone. Moreover, this makes it such that a recently added person, that is later deleted byundo
, will still be visible in the Info Display, until another command (eg.viewp
) displaces it.
Example:
Originally, PharmHub has the following display.
After adding a ‘newguy2’ usingaddp
, he shows up in the Info Display.
Onundo
, the person’s information still stays on the Info Display, even though he no longer exists in PharmHub.
We plan to enhance the Undo/Redo feature to allow UI changes to be undone/ redone. In this way, UI states are saved along with the data, and can be reverted withundo
andredo
.
In the case of the example given, after the implementation of this planned enhancement, the UI should revert back to an empty display after theundo
command is ran.
Appendix: Effort
Challenges
- As a four-man team, each of us had to do take on a heavier workload to hit the necessary level of functionality in our product.
- We also had to pick up many new things along the way. Some of this include Json for storage, JavaFx for Ui and PlantUml for documentation.
- Unexpected dependencies in code between different team members significantly slowed down speed of development as one member had to wait for another before proceeding.
Effort Required
Original AB3 only tracked one entity type, person
. Our application however, tracks three different entity types, person
, order
and medicine
.
This required significant effort.
- In Storage, we had to ensure that the data is valid in all the different places. For example, a
medicine
could appear as an entity in PharmHub itself, or in the allergies or aperson
or in anorder
. Similar checking had to be done forperson
andorder
. Validating all this and ensuring that referential integrity was upheld took significant effort. - In Ui, due to the coupling between the three entities, it once again took a lot of effort to ensure all the UI information is being displayed correctly.
For example, when a
person
is edited we had to ensure the corresponding UI fororder
reflects it as well. - In Logic, we had to ensure no regressions took place due to user behaviour. For example, we had to ensure a
medicine
can’t be deleted when there is aperson
allergic to it or when anorder
contains it. We also had to check if editing aperson
will result in them being allergic to amedicine
in theirorder
. There were several other such instances.
Achievements
Despite the steep learning curve and difficulties, we managed to build a product that we believe will be beneficial to our target audience.
It has features that cater to their needs and solves their pain points.