Developed in Fall 2024 by Skyler DeVaughn, Katelyn McLean, Oliver Seymour, Hsinwei Lin, CJ Reda, and Dalia Castro
- Overview
- Features
- Setup Instructions
- Code Structure
- Architecture
- Frontend
- Backend
- API Documentation
- Walkthrough and Screenshots
This is a complete Web App for managing a modern pharmacy, including managing prescriptions, inventory, employee accounts, and more, and selling items to customers with a point-of-sale.
Security and user experience were considered during all points of the development process, as well as future maintainability and extensibility.
This pharmacy management system is a robust and scalable solution designed to streamline pharmacy operations. It integrates modern technologies such as React, FastAPI, and PostgreSQL to provide a seamless user experience and efficient data management. The system's architecture ensures maintainability, security, and extensibility, making it an ideal choice for modern pharmacies looking to enhance their operational efficiency.
Note: more details on the system are below the 'Running the code' section.
Key users of this app include:
- Pharmacy Managers: can create and manage user (employee) accounts, manage and update inventory, view logs of all user actions and inventory changes, receive notifications for medications expiring soon or with low stock
- Pharmacists: can fill prescriptions, view employee accounts, view medication inventory, view the same logs as pharmacy managers, receive same notifications as pharmacy managers
- Pharmacy technicians: Can view medication inventory and checkout customers
- Cashiers: can checkout customers
- Overview
- Setup Instructions
- Code Structure
- Architecture
- Frontend
- Backend
- API Documentation
- Walkthrough and Screenshots
Follow these steps to run the project locally.
-
Install Docker Desktop
-
Install Python
-
Check Docker status, making sure it's properly installed
docker --version docker-compose --version
-
Clone the repository:
git clone https://github.com/oliver-sey/pharmacy-management-system
-
Navigate to the project directory:
cd pharmacy-management-system
-
Build Docker container
You only need to run this command with the
--build
flag the first time or after changing the environment (importing a new package, etc).docker-compose up --build
The Docker container should now be up and running, and the website should be available at localhost:3000 in your browser.
-
Starting the container in a typical situation:
Start the container:
docker-compose up
-
Stop the container (stops the code):
ctrl (control key) c
The project is organized into the following directories:
-
frontend/: Contains the React application for the user interface.
- src/: Source code for the React components, pages, and utilities.
- public/: Public assets and the main HTML file.
- build/: Build output for the React application.
- Dockerfile: Dockerfile for building the frontend container.
- package.json: Configuration file for npm dependencies and scripts.
-
backend/: Contains the FastAPI application for the backend logic.
- api_server/: Source code for the FastAPI application, including models, schemas, and routes.
- database/: SQL scripts and database-related files.
- tests/: Unit tests for the backend.
- Dockerfile: Dockerfile for building the backend container.
- requirements.txt: List of Python dependencies.
-
docker-compose.yml: Docker Compose file for orchestrating the frontend and backend containers.
This structure ensures a clear separation of concerns, making the project easy to navigate and maintain.
This project includes a FastAPI backend with SQLAlchemy models and schema design, a PostgreSQL database, a React frontend, and the use of Docker containers for easy deployment.
The project follows a full-stack/client-server architecture with the following components:
- FastAPI Backend: Provides RESTful API endpoints for the frontend to interact with the database.
- SQLAlchemy: ORM (Object Relational Mapper) used to define models and handle database interactions.
- PostgreSQL Database: Relational database used to persist data.
- React Frontend: User interface built using React.js that communicates with the backend API.
- Docker: Containerization tool for easy deployment and isolation of all components.
+---------------------+ +---------------------+
| React Frontend | <--> | FastAPI Backend |
| (User Interface) | | (API Server) |
+---------------------+ +---------------------+
|
v
+---------------------+
| SQLAlchemy ORM |
| (Models, Schema) |
+---------------------+
|
v
+---------------------+
| PostgreSQL Database |
| (Data Persistence) |
+---------------------+
Layer | Technology Used |
---|---|
Frontend | React (JavaScript), Material-UI |
Backend | FastAPI (Python), Pydantic |
Database | PostgreSQL |
The frontend and backend are interconnected through RESTful APIs. The frontend makes HTTP requests to the backend to perform various operations. The backend processes these requests, interacts with the database, and returns the appropriate responses. Docker is used to containerize both the frontend and backend, ensuring a consistent and isolated environment for the application.
The frontend of the application is built using React and Material-UI. It provides a dynamic and responsive user interface that allows users to interact with the system seamlessly. Key features include:
- Component-Based Architecture: The frontend is structured into reusable components, making it easy to maintain and extend.
- State Management: React's state management is used to handle the application's state, ensuring a smooth user experience.
- Form Validation: Custom validation logic is implemented to ensure data integrity before submission.
- API Integration: The frontend communicates with the backend via RESTful APIs to fetch and submit data.
The backend is powered by FastAPI, a modern, high-performance web framework for building APIs with Python. It handles the core logic and data processing of the application. Key features include:
- Database Management: PostgreSQL is used as the database to store structured data. SQLAlchemy is used for ORM (Object-Relational Mapping).
- Authentication and Authorization: Secure authentication mechanisms are implemented using JWT (JSON Web Tokens).
- Data Validation: Pydantic is used for data validation and serialization, ensuring that the data conforms to the expected schema.
- API Endpoints: FastAPI is used to define RESTful API endpoints for various operations such as managing prescriptions, inventory, and user accounts.
The backend is built using FastAPI, with a PostgreSQL database managed through SQLAlchemy. This application exposes several RESTful API endpoints for authentication, user management, patient management, and password resetting. The backend also incorporates JWT-based authentication for secure API access.
The application uses SQLAlchemy for interacting with a PostgreSQL database. The SessionLocal
object is used to manage database sessions.
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
The authentication system in the backend is based on JSON Web Tokens (JWT). The following endpoints are responsible for handling user authentication, token issuance, and token verification:
This endpoint is used for user authentication. It accepts the username (email) and password, verifies the credentials, and returns a JWT access token. This token is then used for subsequent requests to authenticate the user.
- Input: Username (email) and password.
- Output: A JWT token that the user can use for authorization in future requests.
This endpoint allows a user to verify whether their JWT token is valid. It decodes the token and checks its expiration time. If the token is valid, it returns a success message.
- Input: JWT token.
- Output: A message indicating if the token is valid.
These endpoints manage user accounts and their details:
- POST
/users/
: Creates a new user. Allowed user: [Pharmacy Manager] - GET
/users/{user_id}
: Retrieves details of a specific user. Allowed user: [Pharmacy Manager, Pharmacist] - DELETE
/users/{user_id}
: Deletes a user if no prescriptions are associated. Allowed user: [Pharmacy Manager] - PUT
/users/{user_id}
: Updates the details of an existing user. Allowed user: [Pharmacy Manager] - GET
/userslist/
: Lists all users in the system. Allowed user: [Pharmacy Manager, Pharmacist] - PUT
/users/unlock/{user_id}
: Unlocks a user account after it has been locked due to too many incorrect login attempts. Allowed user: [Pharmacy Manager]
NOTE: The endpoints below do not require authentication (a JWT) in the request. These are a very limited number of endpoints, and we intentionally only return very limited information.
The first two endpoints are used on the /setpassword
page so that a user can set the first password on their account. We cannot pass a JWT (token) to these endpoints because the user does not have a password yet and cannot create a token.
The third endpoint is used on the login page (so the user does not have a JWT yet), and locks a user account if they make too many incorrect login attempts.
We found these endpoints to be necessary to the proper function of our system and made sure to make the system as secure as possible.
- GET
/userslist/new/
: Returns a list of emails and user ID's for accounts that have been newly created, but the user has not activated their account by setting a password yet. Allowed user: [Pharmacy Manager] - PUT
/users/{user_id}/setpassword
: Sets a new password for a user account, only works if the account has not had a password before (i.e. it is a new account). Passwords are normally changed with the PUT/users/{user_id}
endpoint. - PUT
/users/lock
: Locks a user's account so they cannot login until a pharmacy manager unlocks their account.
These endpoints handle patient records:
- POST
/patient
: Creates a new patient. Allowed user: all - GET
/patient/{patient_id}
: Retrieves details of a specific patient. Allowed user: all - GET
/patients
: Lists all patients in the system. Allowed user: all - PUT
/patient/{patient_id}
: Updates a patient’s information. Allowed user: all - DELETE
/patient/{patient_id}
: Deletes a patient and updates any associated prescriptions. Allowed user: [Pharmacy Manager]
These endpoints are used to manage medications in the system:
- POST
/medication/
: Adds a new medication to the database. Allowed user: [Pharmacy Manager] - GET
/medication/{medication_id}
: Retrieves details of a specific medication. Allowed user: [Pharmacy Manager, Pharmacist, pharmacy technician] - PUT
/medication/{medication_id}
: Updates details of an existing medication. Allowed user: [Pharmacy Manager] - DELETE
/medication/{medication_id}
: Deletes a specific medication from the system. Allowed user: [Pharmacy Manager] - GET
/medicationlist/
: Lists all medications in the system. Allowed user: [Pharmacy Manager, Pharmacist, pharmacy technician]
These endpoints handle prescription management, including creation, updating, deletion, and filling:
- GET
/prescriptions
: Retrieves all prescriptions, optionally filtered by patient ID. Allowed user: all - GET
/prescription/{prescription_id}
: Retrieves a specific prescription. Allowed user: all - POST
/prescription
: Creates a new prescription. Allowed user: all - PUT
/prescription/{prescription_id}
: Updates an existing prescription (unless it has been filled). Allowed user: all - DELETE
/prescription/{prescription_id}
: Deletes a prescription. Allowed user: [ Pharmacy Manager ] - PUT
/prescription/{prescription_id}/fill
: Marks a prescription as filled and updates inventory accordingly. Allowed user: [ Pharmacist ]
These endpoints manage changes to medication inventory, such as adding, filling, or selling medications:
- POST
/inventory-updates
: Creates an inventory update (used internally). - GET
/inventory-updates/{id}
: Retrieves a specific inventory update. Allowed user: [Pharmacy Manager, Pharmacist] - GET
/inventory-updates
: Lists all inventory updates, optionally filtered by activity type (e.g., "add", "fillpresc", "sellnonpresc"). Allowed user: [Pharmacy Manager, Pharmacist]
These endpoints track user activities, such as inventory updates and prescription fillings:
- POST
/user-activities
: Creates a new user activity (used internally). - GET
/user-activities
: Lists all user activities recorded in the system. Allowed user: [Pharmacy Manager, Pharmacist]
These endpoints manage transactions in the pharmacy system:
- POST
/transaction
: Creates a new transaction. Allowed user: [Pharmacy Manager, Pharmacist] - GET
/transaction/{transaction_id}
: Retrieves a specific transaction. Allowed user: [Pharmacy Manager, Pharmacist] - GET
/transactions
: Lists all transactions in the system. Allowed user: [Pharmacy Manager, Pharmacist]
These endpoints manage transaction items. A transaction item is like one line-item on a receipt, it is a medication and the quantity of that medication. A transaction is associated with multiple transaction items:
- There is no endpoint to create a transaction item, they are created when you create a transaction and pass the medications and quantities to that endpoint.
- POST
/transaction
: Creates a new transaction. Allowed user: [Pharmacy Manager, Pharmacist] - GET
/transaction/{transaction_id}
: Retrieves a specific transaction. Allowed user: [Pharmacy Manager, Pharmacist] - GET
/transactions
: Lists all transactions in the system. Allowed user: [Pharmacy Manager, Pharmacist]
These describe our database tables
Represents a user (employee) in the system (e.g., Pharmacy Manager, Technician, etc.). Patients (customers) do not get a user account and cannot login.
Column Name | Data Type |
---|---|
id | Integer (Primary Key) |
first_name | String |
last_name | String |
user_type | SQLAlchemyEnum(UserType) |
String (Unique) | |
password | String |
is_locked_out | Boolean (Default: True) |
Represents a patient with personal and insurance details.
Column Name | Data Type |
---|---|
id | Integer (Primary Key) |
first_name | String |
last_name | String |
date_of_birth | Date |
address | String |
phone_number | String |
String (Unique) | |
insurance_name | String |
insurance_group_number | String |
insurance_member_id | String |
Represents a prescription given to a patient, including medication details.
Column Name | Data Type |
---|---|
id | Integer (Primary Key) |
patient_id | Integer (ForeignKey) |
user_entered_id | Integer (ForeignKey) |
user_filled_id | Integer (ForeignKey) |
date_prescribed | Date (Default: current date) |
filled_timestamp | DateTime (Nullable) |
medication_id | Integer (ForeignKey) |
doctor_name | String |
quantity | Integer |
Represents a medication, including its name, dosage, and inventory details.
Column Name | Data Type |
---|---|
id | Integer (Primary Key) |
name | String |
dosage | String |
quantity | Integer |
prescription_required | Boolean |
expiration_date | Date |
dollars_per_unit | Float |
Tracks a user's activities in the system (e.g., login, logout, etc.).
Column Name | Data Type |
---|---|
id | Integer (Primary Key) |
user_id | Integer (ForeignKey) |
activity_type | SQLAlchemyEnum(UserActivityType) |
timestamp | DateTime (Default: current time) |
Represents changes to the inventory, such as adding or discarding medication.
Column Name | Data Type |
---|---|
id | Integer (Primary Key) |
medication_id | Integer (ForeignKey) |
user_activity_id | Integer (ForeignKey) |
transaction_id | Integer (ForeignKey, Nullable) |
quantity_changed_by | Integer |
timestamp | DateTime (Default: current time) |
activity_type | SQLAlchemyEnum(InventoryUpdateType) |
Represents a transaction involving a user and a patient (e.g., payment for medication).
Column Name | Data Type |
---|---|
id | Integer (Primary Key) |
user_id | Integer (ForeignKey) |
patient_id | Integer (ForeignKey) |
timestamp | DateTime (Default: current time) |
payment_method | String |
These restrict the possible values that can be stored in certain database columns.
Defines the roles for a user.
Value |
---|
PHARMACY_MANAGER |
PHARMACY_TECHNICIAN |
CASHIER |
PHARMACIST |
Defines the types of user activities.
Value |
---|
LOGIN |
LOGOUT |
UNLOCK_ACCOUNT |
INVENTORY_UPDATE |
CREATE_USER |
DELETE_USER |
UPDATE_USER |
CREATE_PATIENT |
DELETE_PATIENT |
UPDATE_PATIENT |
CREATE_PRESCRIPTION |
CREATE_MEDICATION |
DELETE_MEDICATION |
UPDATE_MEDICATION |
OTHER |
ERROR |
Defines the types of inventory updates.
Value |
---|
ADD |
DISCARD |
FILLPRESC |
SELLNONPRESC |
Represents updates made to the inventory, such as adding or discarding medication.
quantity_changed_by
will be positive or negative depending on if medication was added or removed.
Column Name | Data Type |
---|---|
id | Integer (Primary Key) |
medication_id | Integer (ForeignKey) |
user_activity_id | Integer (ForeignKey) |
transaction_id | Integer (ForeignKey, Nullable) |
quantity_changed_by | Integer |
timestamp | DateTime (Default: current time) |
activity_type | SQLAlchemyEnum(InventoryUpdateType) |
Represents a transaction between a user and a patient (e.g., payment for medications or services).
Column Name | Data Type |
---|---|
id | Integer (Primary Key) |
user_id | Integer (ForeignKey) |
patient_id | Integer (ForeignKey) |
timestamp | DateTime (Default: current time) |
payment_method | String |
When you first visit the pharmacy management system, you will be shown the homepage with information about location, hours and more.
You can click the login button to login to your account.
If your pharmacy manager has made an account for you but you haven't set a password yet, you can click the link on the login page to go to the page to set your first password.
With input validation!
After you login, you will be shown the dashboard. The dashboard is different for every user type, this screenshot shows the dashboard for Pharmacy Managers.
You can navigate to different places on the website using the navbar at the top or the buttons on the dashboard. Here are a few of the pages.
-
Medications: With a description of the warning icons when you hover
-
You can also delete prescriptions, and edit and delete patients, medications, and users, complete with input validation to ensure data integrity.
Each page serves a unique purpose, most are simple CRUD operations for prescriptions, medication, patients, and users. Pages like user activity, report engine, and notifications allow pharmacy managers to monitor logs and get reports.
Since this system is also meant to be a point-of-sale, employees can checkout customers.
Customers can pay with cash:
Or they can pay with debit or credit card: