Mastering Hospital Supply Chain ERDs with PlantUML: A Step-by-Step Guide

In the high-stakes environment of healthcare administration, the efficiency of the supply chain is not merely a logistical concern but a critical component of patient care. Hospitals manage thousands of medical consumables, from standard syringes to specialized surgical implants, requiring a data model that is as precise as the medical procedures it supports. A robust Entity Relationship Diagram (ERD) serves as the architectural blueprint for these systems, ensuring that procurement workflows, inventory tracking, and stock movements are recorded with absolute accuracy. Without a clear data structure, the risk of stockouts, expired medication distribution, or audit failures increases significantly.

Real-world system context and operational workflow illustration

Building this complex data model using traditional drag-and-drop tools can often lead to visual clutter and difficulty in maintaining relationships across dozens of entities. Diagram-as-code approaches, specifically using PlantUML, offer a superior alternative for architects and database designers. By defining the schema in text, developers can version the logic (conceptually), iterate rapidly, and ensure that every foreign key and relationship is explicitly defined before the database is ever created. In this masterclass, we will construct a comprehensive ERD for a Hospital Supply Chain Procurement and Medical Consumables Inventory System using VPasCode, a free, browser-based PlantUML editor.

Understanding the Model: Purpose, Scope & Problem Framing

Diagram Abstraction & Representation

An Entity Relationship Diagram (ERD) is the foundational tool for database design, visualizing the structure of data rather than the logic of the application. In the context of healthcare supply chains, this diagram maps the lifecycle of medical consumables. It abstracts physical objects (like a box of gloves) into data entities (the Item) and defines how they interact with operational processes (like Purchase Orders and Goods Receipts). The diagram specifically focuses on cardinality (one-to-many relationships) and foreign keys, ensuring that every transaction can be traced back to its source.

Target Domain Scope & Scenario

This model covers the end-to-end procurement and inventory lifecycle within a hospital setting. The scope includes:

  • Master Data: Managing Suppliers, Item Categories, Units of Measure, and Storage Locations.
  • Procurement: The flow from Purchase Requisitions to Purchase Orders and their subsequent fulfillment.
  • Receiving: The critical validation step where Goods Receipts and Receipt Lines capture batch numbers and expiry dates.
  • Inventory & Consumption: Tracking stock levels, movements between locations, and issues to specific departments.

Crucially, this diagram enforces strict data integrity rules, such as ensuring that a stock movement is always linked to a specific item and location, and that every goods receipt is tied to a specific purchase order line.

Key Takeaways & Educational Insights

By constructing this diagram, you will gain insights into how to model complex transactional systems with line-item granularity. You will learn how to separate master data from transactional data, how to handle batch tracking (essential for medical expiry management), and how to represent the hierarchical nature of procurement documents (e.g., a Purchase Order containing multiple Lines).

Complete Diagram & Full Source Code

Below is the final rendered Entity Relationship Diagram for the Hospital Supply Chain system. This visual blueprint encapsulates the relationships between suppliers, inventory, and procurement workflows.

Descriptive Alt Text

@startuml
!theme plain

/' 
Diagram Context:
This ERD models the core data structure for a hospital supply chain procurement 
and medical consumables inventory system. It captures how suppliers provide items, 
how purchase orders are created and fulfilled, how items are received into 
inventory, and how stock movements are tracked across storage locations. The 
diagram supports operational visibility for procurement, receiving, inventory 
control, and consumption tracking of medical consumables.
'/

title Hospital Supply Chain Procurement and Medical Consumables Inventory System - ERD

' =========================
' Master / Reference Entities
' =========================

entity "Supplier" as supplier {
  * supplier_id : INT <<PK>>
  --
  supplier_code : VARCHAR
  supplier_name : VARCHAR
  contact_person : VARCHAR
  phone : VARCHAR
  email : VARCHAR
  address : TEXT
  payment_terms : VARCHAR
  status : VARCHAR
}

entity "Item Category" as category {
  * category_id : INT <<PK>>
  --
  category_name : VARCHAR
  description : TEXT
}

entity "Unit of Measure" as uom {
  * uom_id : INT <<PK>>
  --
  uom_code : VARCHAR
  uom_name : VARCHAR
}

entity "Storage Location" as location {
  * location_id : INT <<PK>>
  --
  location_code : VARCHAR
  location_name : VARCHAR
  location_type : VARCHAR
  department : VARCHAR
}

entity "Item" as item {
  * item_id : INT <<PK>>
  --
  item_code : VARCHAR
  item_name : VARCHAR
  category_id : INT <<FK>>
  uom_id : INT <<FK>>
  specification : TEXT
  reorder_level : INT
  max_stock_level : INT
  is_controlled : BOOLEAN
  status : VARCHAR
}

' =========================
' Procurement Entities
' =========================

entity "Purchase Requisition" as requisition {
  * requisition_id : INT <<PK>>
  --
  requisition_no : VARCHAR
  request_date : DATE
  requested_by : VARCHAR
  department : VARCHAR
  status : VARCHAR
  remarks : TEXT
}

entity "Purchase Requisition Line" as requisition_line {
  * requisition_line_id : INT <<PK>>
  --
  requisition_id : INT <<FK>>
  item_id : INT <<FK>>
  requested_qty : DECIMAL
  approved_qty : DECIMAL
  status : VARCHAR
}

entity "Purchase Order" as purchase_order {
  * po_id : INT <<PK>>
  --
  po_no : VARCHAR
  supplier_id : INT <<FK>>
  order_date : DATE
  expected_delivery_date : DATE
  status : VARCHAR
  total_amount : DECIMAL
  currency : VARCHAR
  remarks : TEXT
}

entity "Purchase Order Line" as po_line {
  * po_line_id : INT <<PK>>
  --
  po_id : INT <<FK>>
  item_id : INT <<FK>>
  ordered_qty : DECIMAL
  unit_price : DECIMAL
  line_amount : DECIMAL
  received_qty : DECIMAL
  status : VARCHAR
}

entity "Goods Receipt" as goods_receipt {
  * receipt_id : INT <<PK>>
  --
  receipt_no : VARCHAR
  po_id : INT <<FK>>
  receipt_date : DATE
  received_by : VARCHAR
  supplier_delivery_note : VARCHAR
  status : VARCHAR
  remarks : TEXT
}

entity "Goods Receipt Line" as receipt_line {
  * receipt_line_id : INT <<PK>>
  --
  receipt_id : INT <<FK>>
  po_line_id : INT <<FK>>
  item_id : INT <<FK>>
  received_qty : DECIMAL
  batch_no : VARCHAR
  expiry_date : DATE
  condition_status : VARCHAR
}

' =========================
' Inventory Entities
' =========================

entity "Inventory Stock" as inventory {
  * inventory_id : INT <<PK>>
  --
  item_id : INT <<FK>>
  location_id : INT <<FK>>
  batch_no : VARCHAR
  expiry_date : DATE
  quantity_on_hand : DECIMAL
  quantity_reserved : DECIMAL
  quantity_available : DECIMAL
  last_updated : DATETIME
}

entity "Stock Movement" as stock_movement {
  * movement_id : INT <<PK>>
  --
  item_id : INT <<FK>>
  location_id : INT <<FK>>
  movement_type : VARCHAR
  reference_type : VARCHAR
  reference_id : INT
  quantity : DECIMAL
  movement_date : DATETIME
  performed_by : VARCHAR
  remarks : TEXT
}

entity "Stock Issue" as stock_issue {
  * issue_id : INT <<PK>>
  --
  issue_no : VARCHAR
  issue_date : DATE
  department : VARCHAR
  issued_by : VARCHAR
  status : VARCHAR
}

entity "Stock Issue Line" as issue_line {
  * issue_line_id : INT <<PK>>
  --
  issue_id : INT <<FK>>
  item_id : INT <<FK>>
  location_id : INT <<FK>>
  issued_qty : DECIMAL
  batch_no : VARCHAR
  expiry_date : DATE
}

' =========================
' Relationships
' =========================

category ||--o{ item
uom ||--o{ item

supplier ||--o{ purchase_order

requisition ||--o{ requisition_line
item ||--o{ requisition_line

purchase_order ||--o{ po_line
item ||--o{ po_line

purchase_order ||--o{ goods_receipt
goods_receipt ||--o{ receipt_line
po_line ||--o{ receipt_line
item ||--o{ receipt_line

item ||--o{ inventory
location ||--o{ inventory

item ||--o{ stock_movement
location ||--o{ stock_movement

stock_issue ||--o{ issue_line
item ||--o{ issue_line
location ||--o{ issue_line

stock_movement }o--|| stock_issue : "may reference"
stock_movement }o--|| goods_receipt : "may reference"

@enduml

Step-by-Step Architectural Walkthrough

Building a complex ERD like this requires a structured approach. We will break the construction down into four distinct phases: setting the stage, defining master data, modeling transactions, and establishing relationships.

Phase 1: Canvas Configuration & Layout Directives

Every PlantUML diagram begins with configuration directives that set the visual tone and behavior. In our healthcare context, we want a clean, professional look that is easy to read for stakeholders. We use the !theme plain directive to remove unnecessary decorative elements and focus on the data structure.

@startuml
!theme plain

/' 
Diagram Context:
This ERD models the core data structure for a hospital supply chain procurement 
and medical consumables inventory system...
'/

title Hospital Supply Chain Procurement and Medical Consumables Inventory System - ERD

The title directive provides a clear header for the diagram, which is essential for documentation. The comment block (starting with /' and ending with '/) serves as a crucial piece of metadata, explaining the business context without cluttering the visual diagram.

Phase 2: Declaring Core Entities, Actors, and Boundaries

The foundation of any ERD is the entity block. In PlantUML, we define an entity using the entity "Display Name" as alias syntax. This alias is used for referencing the entity in relationships. Inside the curly braces {}, we define the attributes.

For the Item entity, which is central to the supply chain, we define the primary key (PK) and foreign keys (FK) explicitly:

entity "Item" as item {
  * item_id : INT <>
  --
  item_code : VARCHAR
  item_name : VARCHAR
  category_id : INT <>
  uom_id : INT <>
  specification : TEXT
  reorder_level : INT
  max_stock_level : INT
  is_controlled : BOOLEAN
  status : VARCHAR
}

Key observations here include:

  • Primary Key: Marked with * and the stereotype <<PK>>.
  • Foreign Keys: Marked with <<FK>> to indicate relationships to other tables.
  • Separators: The -- line separates keys from regular attributes, improving readability.
  • Data Types: We use standard SQL types like INT, VARCHAR, DECIMAL, and BOOLEAN to ensure the diagram accurately reflects the underlying database schema.

Phase 3: Mapping Data Flows & Key Interactions

Once the entities are defined, we model the transactional flow. In a hospital supply chain, the flow is linear: Requisition → Purchase Order → Goods Receipt → Inventory. We replicate this structure by creating entities for each stage, such as PurchaseOrder and GoodsReceipt.

A critical aspect of this diagram is the handling of line items. A Purchase Order is not just a single number; it contains multiple lines (items). We model this by creating a po_line entity that links back to the purchase_order and the item:

entity "Purchase Order Line" as po_line {
  * po_line_id : INT <>
  --
  po_id : INT <>
  item_id : INT <>
  ordered_qty : DECIMAL
  unit_price : DECIMAL
  received_qty : DECIMAL
  status : VARCHAR
}

This structure allows us to track exactly how many units of a specific item were ordered versus how many were actually received, which is vital for inventory reconciliation.

Phase 4: Grouping, Annotations & Visual Polish

While PlantUML ERD mode doesn’t support visual grouping boxes in the same way as class diagrams, we use logical separation by defining entities in blocks (as seen in the code with comments like ' ========================= Procurement Entities'). We also use relationship arrows to define the cardinality of the data flow.

The final relationships are drawn using the ||--o{ syntax. For example, category ||--o{ item means “One Category has zero or many Items”. The double bar || indicates mandatory participation (an item must have a category), while the crow’s foot { indicates one-to-many.

Syntax & Keyword Deep Dive

To master PlantUML ERD diagrams, it is essential to understand the specific keywords and symbols used to define structure and relationships.

  • entity: The fundamental building block used to define a table or entity. Syntax: entity "Name" as alias { attributes }.
  • <<PK>> & <<FK>>: Stereotypes used to visually mark Primary Keys and Foreign Keys. The asterisk * is also used to denote the primary key attribute.
  • --: A separator line used within an entity block to visually divide keys from other attributes.
  • ||--o{: A relationship arrow indicating a one-to-many relationship where the “one” side is mandatory (the double bar) and the “many” side is optional (the open circle).
  • o--||: A relationship arrow indicating a one-to-one relationship where the “one” side is optional and the “many” (or other side) is mandatory.
  • /' ... '/: The syntax for multi-line comments in PlantUML, used here to provide a business context description.

Best Practices & Pitfalls to Avoid

When designing ERDs for complex systems like hospital supply chains, following best practices ensures the diagram remains maintainable and useful.

  • Separate Master Data from Transactional Data: Notice how we have distinct entities for Item (Master Data) and GoodsReceipt (Transactional Data). Do not mix these; keeping them separate allows for historical analysis of transactions without bloating the master record.
  • Use Aliases Consistently: Always use short, unique aliases (e.g., item, po) for relationships. This prevents the diagram from becoming cluttered with long entity names and makes the relationship syntax cleaner.
  • Explicit Cardinality: Always define the cardinality (one-to-many, one-to-one) explicitly. Ambiguous relationships lead to database design errors where referential integrity cannot be enforced.
  • Standardize Data Types: Use consistent data types across similar fields (e.g., always use DECIMAL for quantities and currency) to avoid confusion when translating the diagram to actual SQL code.

Design Hospital ERDs Instantly with VPasCode

Transform your database design ideas into professional Entity Relationship Diagrams instantly using our free, browser-based PlantUML editor—no installation required.

Scroll to Top