Blood Bank Management Data Specifications
Shared Entity References
This module references the following shared entities defined in their owning modules. Blood Bank does not redefine these tables — it uses foreign-key references only.
| Shared Entity |
Owning Module |
Table(s) |
FK Used Here |
| Patients |
ehr-patient-mgmt |
patients |
patients.patient_id |
| Providers |
ehr-patient-mgmt |
providers |
providers.provider_id |
| Encounters |
scheduling |
encounters |
encounters.encounter_id |
| Facilities |
ehr-patient-mgmt |
facilities, departments |
facilities.facility_id |
| Users & Auth |
ehr-patient-mgmt |
users, roles, permissions |
users.user_id |
| Lab Results |
lis |
lab_results |
lab_results.result_id |
| Payers |
policy-contract-mgmt |
payers, insurance_plans |
(referenced indirectly via EHR/RCM) |
Entity Relationship Diagram
erDiagram
patients ||--o{ blood_type_screen : "has"
patients ||--o{ crossmatch_records : "has"
patients ||--o{ transfusion_orders : "has"
patients ||--o{ transfusion_administration : "has"
patients ||--o{ transfusion_reactions : "has"
encounters ||--o{ blood_type_screen : "during"
encounters ||--o{ transfusion_orders : "during"
encounters ||--o{ transfusion_administration : "during"
encounters ||--o{ transfusion_reactions : "during"
providers ||--o{ transfusion_orders : "orders"
providers ||--o{ transfusion_reactions : "investigated by"
facilities ||--o{ blood_component_inventory : "stores"
facilities ||--o{ blood_donations : "collected at"
users ||--o{ blood_type_screen : "tested by"
users ||--o{ crossmatch_records : "performed by"
users ||--o{ transfusion_administration : "verified by"
users ||--o{ blood_quarantine_log : "released by"
users ||--o{ blood_discard_log : "discarded by"
blood_donors ||--o{ blood_donations : "donates"
blood_donations ||--o{ blood_components : "produces"
blood_components ||--o{ blood_component_inventory : "tracked as"
blood_components ||--o{ crossmatch_records : "crossmatched"
blood_components ||--o{ transfusion_administration : "transfused"
blood_components ||--o{ blood_quarantine_log : "quarantined in"
blood_components ||--o{ blood_discard_log : "discarded in"
blood_type_screen ||--o{ crossmatch_records : "basis for"
transfusion_orders ||--o{ crossmatch_records : "fulfilled by"
transfusion_orders ||--o{ transfusion_administration : "results in"
transfusion_administration ||--o{ transfusion_reactions : "may cause"
blood_donors {
bigint donor_id PK
}
blood_donations {
bigint donation_id PK
bigint donor_id FK
bigint facility_id FK
bigint collector_id FK
}
blood_components {
bigint component_id PK
bigint donation_id FK
}
blood_component_inventory {
bigint inventory_id PK
bigint component_id FK
bigint facility_id FK
bigint issued_to_patient_id FK
}
blood_type_screen {
bigint ts_id PK
bigint patient_id FK
bigint encounter_id FK
bigint tested_by FK
}
crossmatch_records {
bigint crossmatch_id PK
bigint patient_id FK
bigint component_id FK
bigint ts_id FK
bigint order_id FK
bigint performed_by FK
}
transfusion_orders {
bigint order_id PK
bigint patient_id FK
bigint encounter_id FK
bigint ordering_provider_id FK
}
transfusion_administration {
bigint admin_id PK
bigint order_id FK
bigint component_id FK
bigint patient_id FK
bigint encounter_id FK
bigint verifier1_id FK
bigint verifier2_id FK
}
transfusion_reactions {
bigint reaction_id PK
bigint admin_id FK
bigint patient_id FK
bigint component_id FK
bigint investigated_by FK
}
blood_quarantine_log {
bigint quarantine_id PK
bigint component_id FK
bigint released_by FK
}
blood_discard_log {
bigint discard_id PK
bigint component_id FK
bigint discarded_by FK
bigint witness_id FK
}
Table Definitions
Notes for all tables:
- Use TIMESTAMP WITH TIME ZONE where supported; shown as TIMESTAMP for brevity.
- All tables include created_at, created_by, updated_at, updated_by for auditability (UAE PDPL, DOH/DHA audit expectations), even if not listed in key_fields.
1. blood_donors
Purpose: Donor registry with demographics, Emirates ID, baseline blood group, and deferral history. Used for donor eligibility, recall, and MOH hemovigilance reporting.
Field Specifications
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
donor_id |
BIGINT |
NO |
IDENTITY |
PK |
Internal donor identifier |
emirates_id |
VARCHAR(18) |
NO |
— |
UNIQUE, format 784-YYYY-NNNNNNN-C |
UAE Emirates ID of donor |
mrn_patient_id |
BIGINT |
YES |
— |
FK → patients.patient_id |
Link if donor is also a patient |
first_name_en |
VARCHAR(100) |
NO |
— |
— |
Donor first name (English) |
middle_name_en |
VARCHAR(100) |
YES |
— |
— |
Donor middle name (English) |
last_name_en |
VARCHAR(100) |
NO |
— |
— |
Donor last name (English) |
first_name_ar |
VARCHAR(100) |
YES |
— |
— |
Donor first name (Arabic) |
last_name_ar |
VARCHAR(100) |
YES |
— |
— |
Donor last name (Arabic) |
dob |
DATE |
NO |
— |
— |
Date of birth |
gender |
VARCHAR(10) |
NO |
'unknown' |
IN ('male','female','other','unknown') |
Donor gender |
nationality_code |
VARCHAR(3) |
YES |
— |
ISO 3166-1 alpha-3 |
Nationality |
phone_mobile |
VARCHAR(20) |
NO |
— |
E.164 format |
Mobile number (+971 5X XXX XXXX) |
phone_home |
VARCHAR(20) |
YES |
— |
— |
Home phone |
email |
VARCHAR(150) |
YES |
— |
— |
Email address |
address_line1 |
VARCHAR(200) |
YES |
— |
— |
Address line 1 |
address_line2 |
VARCHAR(200) |
YES |
— |
— |
Address line 2 / PO Box |
emirate |
VARCHAR(20) |
YES |
— |
IN ('Abu Dhabi','Dubai','Sharjah','Ajman','Umm Al Quwain','Ras Al Khaimah','Fujairah') |
Emirate of residence |
abo_group |
VARCHAR(2) |
YES |
— |
FK → md_abo_groups.code |
ABO group (O, A, B, AB) |
rh_type |
VARCHAR(3) |
YES |
— |
FK → md_rh_types.code |
Rh type (+, -) |
blood_type_source |
VARCHAR(20) |
YES |
— |
IN ('donation','external','unknown') |
Source of ABO/Rh |
donor_status |
VARCHAR(20) |
NO |
'active' |
IN ('active','temporarily_deferred','permanently_deferred','inactive','deceased') |
Current donor status |
total_donations |
INTEGER |
NO |
0 |
≥ 0 |
Total successful donations |
last_donation_date |
DATE |
YES |
— |
— |
Date of last successful donation |
deferral_reason_code |
VARCHAR(20) |
YES |
— |
FK → md_donor_deferral_reasons.code |
Latest deferral reason |
deferral_until |
DATE |
YES |
— |
— |
Date until which donor is deferred |
consent_signed |
BOOLEAN |
NO |
FALSE |
— |
Informed consent for donation recorded |
consent_datetime |
TIMESTAMP |
YES |
— |
— |
When consent was obtained |
notes |
TEXT |
YES |
— |
— |
Free-text notes / alerts |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Record creation time |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
User who created record |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Last update time |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
User who last updated |
SQL DDL
SQLCREATE TABLE blood_donors (
donor_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
emirates_id VARCHAR(18) NOT NULL,
mrn_patient_id BIGINT,
first_name_en VARCHAR(100) NOT NULL,
middle_name_en VARCHAR(100),
last_name_en VARCHAR(100) NOT NULL,
first_name_ar VARCHAR(100),
last_name_ar VARCHAR(100),
dob DATE NOT NULL,
gender VARCHAR(10) NOT NULL DEFAULT 'unknown',
nationality_code VARCHAR(3),
phone_mobile VARCHAR(20) NOT NULL,
phone_home VARCHAR(20),
email VARCHAR(150),
address_line1 VARCHAR(200),
address_line2 VARCHAR(200),
emirate VARCHAR(20),
abo_group VARCHAR(2),
rh_type VARCHAR(3),
blood_type_source VARCHAR(20),
donor_status VARCHAR(20) NOT NULL DEFAULT 'active',
total_donations INTEGER NOT NULL DEFAULT 0,
last_donation_date DATE,
deferral_reason_code VARCHAR(20),
deferral_until DATE,
consent_signed BOOLEAN NOT NULL DEFAULT FALSE,
consent_datetime TIMESTAMP,
notes TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT uq_blood_donors_emirates_id UNIQUE (emirates_id),
CONSTRAINT fk_blood_donors_patient
FOREIGN KEY (mrn_patient_id) REFERENCES patients(patient_id)
ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_blood_donors_deferral_reason
FOREIGN KEY (deferral_reason_code) REFERENCES md_donor_deferral_reasons(code),
CONSTRAINT fk_blood_donors_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
CONSTRAINT fk_blood_donors_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id),
CONSTRAINT chk_blood_donors_gender
CHECK (gender IN ('male','female','other','unknown')),
CONSTRAINT chk_blood_donors_status
CHECK (donor_status IN ('active','temporarily_deferred','permanently_deferred','inactive','deceased')),
CONSTRAINT chk_blood_donors_emirate
CHECK (emirate IS NULL OR emirate IN ('Abu Dhabi','Dubai','Sharjah','Ajman','Umm Al Quwain','Ras Al Khaimah','Fujairah')),
CONSTRAINT chk_blood_donors_total_donations
CHECK (total_donations >= 0),
CONSTRAINT chk_blood_donors_deferral_dates
CHECK (deferral_until IS NULL OR deferral_until >= CURRENT_DATE)
);
CREATE INDEX idx_blood_donors_emirates_id ON blood_donors(emirates_id);
CREATE INDEX idx_blood_donors_name_en ON blood_donors(last_name_en, first_name_en);
CREATE INDEX idx_blood_donors_status ON blood_donors(donor_status);
CREATE INDEX idx_blood_donors_deferral ON blood_donors(donor_status, deferral_until);
Terminology Bindings
| Field |
Terminology |
Example Value |
abo_group |
ISBT 128 ABO codes |
A, B, AB, O |
rh_type |
ISBT 128 Rh codes |
POS, NEG |
deferral_reason_code |
UAE MOH donor deferral list / local ValueSet |
TRAVEL_MALARIA, LOW_HB |
gender |
HL7 AdministrativeGender |
male |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
donor_id |
RelatedPerson |
RelatedPerson.id |
mrn_patient_id |
Patient |
RelatedPerson.patient.reference (Patient/{id}) |
emirates_id |
RelatedPerson |
identifier[EmiratesID].value |
first_name_en, last_name_en |
RelatedPerson |
name[0].given, name[0].family |
phone_mobile |
RelatedPerson |
telecom[phone].value |
email |
RelatedPerson |
telecom[email].value |
dob |
RelatedPerson |
birthDate |
gender |
RelatedPerson |
gender |
abo_group, rh_type |
Observation |
Observation.code (LOINC 883-9) + valueCodeableConcept (ISBT) for donor typing |
donor_status |
RelatedPerson |
extension[donor-status].valueCode |
2. blood_donations
Purpose: Individual donation events, including collection details, screening outcome, and infectious disease status. Supports linkage to LIS for infectious marker testing.
Field Specifications
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
donation_id |
BIGINT |
NO |
IDENTITY |
PK |
Donation event identifier |
donor_id |
BIGINT |
NO |
— |
FK → blood_donors.donor_id |
Donor |
donation_number |
VARCHAR(30) |
NO |
— |
UNIQUE |
ISBT 128 donation identification number |
donation_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Start time of collection |
donation_end_datetime |
TIMESTAMP |
YES |
— |
≥ donation_datetime |
End time of collection |
donation_type |
VARCHAR(20) |
NO |
— |
IN ('whole_blood','apheresis_platelet','apheresis_plasma','autologous','directed') |
Donation type |
volume_ml |
INTEGER |
YES |
— |
> 0 |
Collected volume in mL |
facility_id |
BIGINT |
NO |
— |
FK → facilities.facility_id |
Collection facility |
collection_site |
VARCHAR(100) |
YES |
— |
— |
Site (e.g., "Mobile Unit Dubai Mall") |
collector_id |
BIGINT |
NO |
— |
FK → users.user_id |
Phlebotomist/technologist |
pre_hb_g_dl |
NUMERIC(4,1) |
YES |
— |
0–25 |
Pre-donation hemoglobin |
complications |
TEXT |
YES |
— |
— |
Any adverse events during donation |
screening_pass |
BOOLEAN |
NO |
FALSE |
— |
Donor screening passed for this donation |
infectious_disease_status |
VARCHAR(20) |
NO |
'pending' |
IN ('pending','negative','positive','indeterminate') |
Overall infectious disease result |
infectious_panel_result_id |
BIGINT |
YES |
— |
FK → lab_results.result_id |
Link to LIS panel result |
status |
VARCHAR(20) |
NO |
'collected' |
IN ('scheduled','collected','quarantined','released','discarded') |
Donation lifecycle status |
notes |
TEXT |
YES |
— |
— |
Additional notes |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Created by |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Updated timestamp |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Updated by |
SQL DDL
SQLCREATE TABLE blood_donations (
donation_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
donor_id BIGINT NOT NULL,
donation_number VARCHAR(30) NOT NULL,
donation_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
donation_end_datetime TIMESTAMP,
donation_type VARCHAR(20) NOT NULL,
volume_ml INTEGER,
facility_id BIGINT NOT NULL,
collection_site VARCHAR(100),
collector_id BIGINT NOT NULL,
pre_hb_g_dl NUMERIC(4,1),
complications TEXT,
screening_pass BOOLEAN NOT NULL DEFAULT FALSE,
infectious_disease_status VARCHAR(20) NOT NULL DEFAULT 'pending',
infectious_panel_result_id BIGINT,
status VARCHAR(20) NOT NULL DEFAULT 'collected',
notes TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT uq_blood_donations_number UNIQUE (donation_number),
CONSTRAINT fk_blood_donations_donor
FOREIGN KEY (donor_id) REFERENCES blood_donors(donor_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_blood_donations_facility
FOREIGN KEY (facility_id) REFERENCES facilities(facility_id),
CONSTRAINT fk_blood_donations_collector
FOREIGN KEY (collector_id) REFERENCES users(user_id),
CONSTRAINT fk_blood_donations_lab_result
FOREIGN KEY (infectious_panel_result_id) REFERENCES lab_results(result_id),
CONSTRAINT fk_blood_donations_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
CONSTRAINT fk_blood_donations_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id),
CONSTRAINT chk_blood_donations_type
CHECK (donation_type IN ('whole_blood','apheresis_platelet','apheresis_plasma','autologous','directed')),
CONSTRAINT chk_blood_donations_status
CHECK (status IN ('scheduled','collected','quarantined','released','discarded')),
CONSTRAINT chk_blood_donations_infectious_status
CHECK (infectious_disease_status IN ('pending','negative','positive','indeterminate')),
CONSTRAINT chk_blood_donations_times
CHECK (donation_end_datetime IS NULL OR donation_end_datetime >= donation_datetime),
CONSTRAINT chk_blood_donations_volume
CHECK (volume_ml IS NULL OR volume_ml > 0)
);
CREATE INDEX idx_blood_donations_donor ON blood_donations(donor_id);
CREATE INDEX idx_blood_donations_number ON blood_donations(donation_number);
CREATE INDEX idx_blood_donations_status ON blood_donations(status);
CREATE INDEX idx_blood_donations_infectious ON blood_donations(infectious_disease_status);
CREATE INDEX idx_blood_donations_datetime ON blood_donations(donation_datetime);
Terminology Bindings
| Field |
Terminology |
Example Value |
donation_type |
Local ValueSet (ISBT-aligned) |
whole_blood |
infectious_disease_status |
Local ValueSet |
negative |
status |
Local ValueSet |
quarantined |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
donation_id |
Procedure |
Procedure.id |
donor_id |
RelatedPerson |
Procedure.subject (donor as subject) |
donation_datetime |
Procedure |
performedDateTime |
donation_type |
Procedure |
code (SNOMED CT for blood donation) |
facility_id |
Organization |
Procedure.performer.actor |
infectious_panel_result_id |
DiagnosticReport |
Procedure.partOf / DiagnosticReport.basedOn |
3. blood_components
Purpose: Individual blood component units derived from donations or apheresis, with full traceability, processing attributes, and expiry.
Field Specifications
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
component_id |
BIGINT |
NO |
IDENTITY |
PK |
Component unit identifier |
donation_id |
BIGINT |
NO |
— |
FK → blood_donations.donation_id |
Source donation |
component_number |
VARCHAR(40) |
NO |
— |
UNIQUE |
ISBT 128 product code + donation number |
component_type |
VARCHAR(30) |
NO |
— |
FK → md_blood_component_types.code |
Component type (RBC, FFP, etc.) |
abo_group |
VARCHAR(2) |
NO |
— |
FK → md_abo_groups.code |
ABO group |
rh_type |
VARCHAR(3) |
NO |
— |
FK → md_rh_types.code |
Rh type |
volume_ml |
INTEGER |
YES |
— |
> 0 |
Volume in mL |
collection_datetime |
TIMESTAMP |
NO |
— |
— |
When component created |
expiry_datetime |
TIMESTAMP |
NO |
— |
> collection_datetime |
Expiry date/time |
status |
VARCHAR(20) |
NO |
'quarantined' |
IN ('quarantined','available','reserved','issued','transfused','discarded') |
Component lifecycle status |
irradiated |
BOOLEAN |
NO |
FALSE |
— |
Irradiation flag |
irradiation_datetime |
TIMESTAMP |
YES |
— |
— |
When irradiated |
leukoreduced |
BOOLEAN |
NO |
FALSE |
— |
Leukoreduction flag |
washed |
BOOLEAN |
NO |
FALSE |
— |
Washed flag |
antigen_negative_for |
VARCHAR(200) |
YES |
— |
— |
Comma-separated antigen codes (e.g., "K-,E-") |
special_attributes |
JSONB |
YES |
— |
— |
Additional attributes (CMV-, HbS-, etc.) |
quarantine_reason |
VARCHAR(200) |
YES |
— |
— |
Reason if quarantined |
final_disposition |
VARCHAR(20) |
YES |
— |
IN ('transfused','discarded','returned','split') |
Final outcome |
final_disposition_datetime |
TIMESTAMP |
YES |
— |
— |
When final disposition occurred |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Created by |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Updated timestamp |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Updated by |
SQL DDL
SQLCREATE TABLE blood_components (
component_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
donation_id BIGINT NOT NULL,
component_number VARCHAR(40) NOT NULL,
component_type VARCHAR(30) NOT NULL,
abo_group VARCHAR(2) NOT NULL,
rh_type VARCHAR(3) NOT NULL,
volume_ml INTEGER,
collection_datetime TIMESTAMP NOT NULL,
expiry_datetime TIMESTAMP NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'quarantined',
irradiated BOOLEAN NOT NULL DEFAULT FALSE,
irradiation_datetime TIMESTAMP,
leukoreduced BOOLEAN NOT NULL DEFAULT FALSE,
washed BOOLEAN NOT NULL DEFAULT FALSE,
antigen_negative_for VARCHAR(200),
special_attributes JSONB,
quarantine_reason VARCHAR(200),
final_disposition VARCHAR(20),
final_disposition_datetime TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT uq_blood_components_number UNIQUE (component_number),
CONSTRAINT fk_blood_components_donation
FOREIGN KEY (donation_id) REFERENCES blood_donations(donation_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_blood_components_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
CONSTRAINT fk_blood_components_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id),
CONSTRAINT chk_blood_components_status
CHECK (status IN ('quarantined','available','reserved','issued','transfused','discarded')),
CONSTRAINT chk_blood_components_final_disposition
CHECK (final_disposition IS NULL OR final_disposition IN ('transfused','discarded','returned','split')),
CONSTRAINT chk_blood_components_times
CHECK (expiry_datetime > collection_datetime),
CONSTRAINT chk_blood_components_volume
CHECK (volume_ml IS NULL OR volume_ml > 0)
);
CREATE INDEX idx_blood_components_donation ON blood_components(donation_id);
CREATE INDEX idx_blood_components_status_expiry ON blood_components(status, expiry_datetime);
CREATE INDEX idx_blood_components_group ON blood_components(abo_group, rh_type, component_type, status);
CREATE INDEX idx_blood_components_number ON blood_components(component_number);
Terminology Bindings
| Field |
Terminology |
Example Value |
component_type |
ISBT 128 product codes |
E3080 (RBC, leukoreduced) |
abo_group |
ISBT 128 ABO |
O |
rh_type |
ISBT 128 Rh |
POS |
antigen_negative_for |
ISBT red cell antigen codes |
K-,E- |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
component_id |
BiologicallyDerivedProduct |
BiologicallyDerivedProduct.id |
component_number |
BiologicallyDerivedProduct |
identifier.value |
component_type |
BiologicallyDerivedProduct |
productCategory / productCode |
abo_group, rh_type |
Observation |
Observation.code (LOINC 883-9) + valueCodeableConcept |
status |
BiologicallyDerivedProduct |
status |
expiry_datetime |
BiologicallyDerivedProduct |
expirationDate |
special_attributes |
BiologicallyDerivedProduct |
extension[special-attributes] |
4. blood_component_inventory
Purpose: Current inventory status and physical location of each component unit across facilities, including issuance and return tracking.
Field Specifications
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
inventory_id |
BIGINT |
NO |
IDENTITY |
PK |
Inventory record identifier |
component_id |
BIGINT |
NO |
— |
FK → blood_components.component_id |
Component unit |
facility_id |
BIGINT |
NO |
— |
FK → facilities.facility_id |
Facility where stored |
storage_location |
VARCHAR(100) |
NO |
— |
— |
Fridge/freezer identifier, shelf, bin |
temperature_zone |
VARCHAR(20) |
NO |
— |
FK → md_component_storage_zones.code |
Storage temp category (RBC, FFP, etc.) |
status |
VARCHAR(20) |
NO |
'in_stock' |
IN ('in_stock','reserved','issued','in_transit','returned','discarded') |
Inventory status |
received_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
When received into inventory |
issued_datetime |
TIMESTAMP |
YES |
— |
— |
When issued from inventory |
issued_to_patient_id |
BIGINT |
YES |
— |
FK → patients.patient_id |
Intended recipient |
issued_to_encounter_id |
BIGINT |
YES |
— |
FK → encounters.encounter_id |
Encounter context |
issued_to_location |
VARCHAR(100) |
YES |
— |
— |
Ward/clinic issued to |
issued_by |
BIGINT |
YES |
— |
FK → users.user_id |
User issuing unit |
returned_datetime |
TIMESTAMP |
YES |
— |
— |
When returned to inventory |
returned_condition |
VARCHAR(20) |
YES |
— |
IN ('acceptable','temperature_excursion','damaged','other') |
Condition on return |
current_temperature_c |
NUMERIC(4,1) |
YES |
— |
— |
Last recorded temperature |
in_transit_to_facility_id |
BIGINT |
YES |
— |
FK → facilities.facility_id |
Destination if in transit |
notes |
TEXT |
YES |
— |
— |
Notes (e.g., transport details) |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Created by |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Updated timestamp |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Updated by |
SQL DDL
SQLCREATE TABLE blood_component_inventory (
inventory_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
component_id BIGINT NOT NULL,
facility_id BIGINT NOT NULL,
storage_location VARCHAR(100) NOT NULL,
temperature_zone VARCHAR(20) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'in_stock',
received_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
issued_datetime TIMESTAMP,
issued_to_patient_id BIGINT,
issued_to_encounter_id BIGINT,
issued_to_location VARCHAR(100),
issued_by BIGINT,
returned_datetime TIMESTAMP,
returned_condition VARCHAR(20),
current_temperature_c NUMERIC(4,1),
in_transit_to_facility_id BIGINT,
notes TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT fk_bci_component
FOREIGN KEY (component_id) REFERENCES blood_components(component_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_bci_facility
FOREIGN KEY (facility_id) REFERENCES facilities(facility_id),
CONSTRAINT fk_bci_issued_patient
FOREIGN KEY (issued_to_patient_id) REFERENCES patients(patient_id),
CONSTRAINT fk_bci_issued_encounter
FOREIGN KEY (issued_to_encounter_id) REFERENCES encounters(encounter_id),
CONSTRAINT fk_bci_issued_by
FOREIGN KEY (issued_by) REFERENCES users(user_id),
CONSTRAINT fk_bci_transit_facility
FOREIGN KEY (in_transit_to_facility_id) REFERENCES facilities(facility_id),
CONSTRAINT fk_bci_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
CONSTRAINT fk_bci_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id),
CONSTRAINT chk_bci_status
CHECK (status IN ('in_stock','reserved','issued','in_transit','returned','discarded')),
CONSTRAINT chk_bci_return_condition
CHECK (returned_condition IS NULL OR returned_condition IN ('acceptable','temperature_excursion','damaged','other'))
);
CREATE INDEX idx_bci_component ON blood_component_inventory(component_id);
CREATE INDEX idx_bci_status_facility ON blood_component_inventory(facility_id, status);
CREATE INDEX idx_bci_patient ON blood_component_inventory(issued_to_patient_id);
CREATE INDEX idx_bci_expiry_lookup ON blood_component_inventory(facility_id, status, component_id);
(Expiry is resolved via join to blood_components.expiry_datetime for FEFO logic.)
Terminology Bindings
| Field |
Terminology |
Example Value |
temperature_zone |
Component Storage Requirements master |
RBC_2_6C |
returned_condition |
Local ValueSet |
temperature_excursion |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
inventory_id |
BiologicallyDerivedProduct |
BiologicallyDerivedProduct.extension[inventory-id] |
facility_id |
Organization |
BiologicallyDerivedProduct.storage[0].scale / Organization.id |
storage_location |
BiologicallyDerivedProduct |
storage[0].description |
current_temperature_c |
BiologicallyDerivedProduct |
storage[0].temperature |
issued_to_patient_id |
Patient |
Procedure.subject (transfusion) |
status |
BiologicallyDerivedProduct |
status |
5. blood_type_screen
Purpose: Patient ABO/Rh typing and antibody screening results, including validity window and concordance with historical type. Used for pre-transfusion testing and electronic crossmatch eligibility.
Field Specifications
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
ts_id |
BIGINT |
NO |
IDENTITY |
PK |
Type & screen record ID |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
encounter_id |
BIGINT |
NO |
— |
FK → encounters.encounter_id |
Encounter |
specimen_id |
VARCHAR(40) |
NO |
— |
— |
Specimen/barcode ID |
specimen_collection_datetime |
TIMESTAMP |
YES |
— |
— |
When specimen collected |
abo_group |
VARCHAR(2) |
NO |
— |
FK → md_abo_groups.code |
ABO group |
rh_type |
VARCHAR(3) |
NO |
— |
FK → md_rh_types.code |
Rh type |
forward_group |
VARCHAR(20) |
YES |
— |
— |
Forward grouping result |
reverse_group |
VARCHAR(20) |
YES |
— |
— |
Reverse grouping result |
antibody_screen_result |
VARCHAR(20) |
NO |
'negative' |
IN ('negative','positive','inconclusive') |
Antibody screen outcome |
antibody_specificity |
VARCHAR(200) |
YES |
— |
— |
Identified antibodies (e.g., "anti-K, anti-E") |
auto_control_result |
VARCHAR(20) |
YES |
— |
IN ('negative','positive') |
Auto control |
tested_by |
BIGINT |
NO |
— |
FK → users.user_id |
Technologist |
tested_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
When testing performed |
valid_until |
TIMESTAMP |
NO |
— |
— |
Validity (e.g., 72h) |
historical_type_concordant |
BOOLEAN |
NO |
TRUE |
— |
Concordant with historical type |
historical_ts_id |
BIGINT |
YES |
— |
FK → blood_type_screen.ts_id |
Reference to prior T&S |
comments |
TEXT |
YES |
— |
— |
Comments / discrepancy notes |
result_status |
VARCHAR(20) |
NO |
'final' |
IN ('preliminary','final','amended','cancelled') |
Result status |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Created by |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Updated timestamp |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Updated by |
SQL DDL
SQLCREATE TABLE blood_type_screen (
ts_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id BIGINT NOT NULL,
encounter_id BIGINT NOT NULL,
specimen_id VARCHAR(40) NOT NULL,
specimen_collection_datetime TIMESTAMP,
abo_group VARCHAR(2) NOT NULL,
rh_type VARCHAR(3) NOT NULL,
forward_group VARCHAR(20),
reverse_group VARCHAR(20),
antibody_screen_result VARCHAR(20) NOT NULL DEFAULT 'negative',
antibody_specificity VARCHAR(200),
auto_control_result VARCHAR(20),
tested_by BIGINT NOT NULL,
tested_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
valid_until TIMESTAMP NOT NULL,
historical_type_concordant BOOLEAN NOT NULL DEFAULT TRUE,
historical_ts_id BIGINT,
comments TEXT,
result_status VARCHAR(20) NOT NULL DEFAULT 'final',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT fk_bts_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_bts_encounter
FOREIGN KEY (encounter_id) REFERENCES encounters(encounter_id),
CONSTRAINT fk_bts_tested_by
FOREIGN KEY (tested_by) REFERENCES users(user_id),
CONSTRAINT fk_bts_historical
FOREIGN KEY (historical_ts_id) REFERENCES blood_type_screen(ts_id),
CONSTRAINT fk_bts_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
CONSTRAINT fk_bts_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id),
CONSTRAINT chk_bts_antibody_screen
CHECK (antibody_screen_result IN ('negative','positive','inconclusive')),
CONSTRAINT chk_bts_auto_control
CHECK (auto_control_result IS NULL OR auto_control_result IN ('negative','positive')),
CONSTRAINT chk_bts_result_status
CHECK (result_status IN ('preliminary','final','amended','cancelled')),
CONSTRAINT chk_bts_validity
CHECK (valid_until > tested_datetime)
);
CREATE INDEX idx_bts_patient ON blood_type_screen(patient_id, valid_until);
CREATE INDEX idx_bts_specimen ON blood_type_screen(specimen_id);
CREATE INDEX idx_bts_antibody ON blood_type_screen(antibody_screen_result);
CREATE INDEX idx_bts_encounter ON blood_type_screen(encounter_id);
Terminology Bindings
| Field |
Terminology |
Example Value |
abo_group |
ISBT 128 ABO |
A |
rh_type |
ISBT 128 Rh |
NEG |
antibody_specificity |
ISBT red cell antigen nomenclature |
anti-K |
antibody_screen_result |
Local ValueSet |
positive |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
ts_id |
Observation |
Observation.id |
patient_id |
Patient |
Observation.subject.reference |
encounter_id |
Encounter |
Observation.encounter.reference |
specimen_id |
Specimen |
Observation.specimen.reference |
abo_group, rh_type |
Observation |
code (LOINC 882-1/883-9) + valueCodeableConcept |
antibody_screen_result |
Observation |
interpretation / valueCodeableConcept (LOINC 890-4) |
antibody_specificity |
Observation |
component[antibody].valueCodeableConcept |
tested_datetime |
Observation |
effectiveDateTime |
result_status |
Observation |
status |
6. crossmatch_records
Purpose: Crossmatch testing results linking a specific component to a patient and transfusion order, including electronic vs serological method and validity.
Field Specifications
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
crossmatch_id |
BIGINT |
NO |
IDENTITY |
PK |
Crossmatch record ID |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
component_id |
BIGINT |
NO |
— |
FK → blood_components.component_id |
Component being crossmatched |
ts_id |
BIGINT |
NO |
— |
FK → blood_type_screen.ts_id |
Underlying type & screen |
order_id |
BIGINT |
YES |
— |
FK → transfusion_orders.order_id |
Related transfusion order |
crossmatch_type |
VARCHAR(20) |
NO |
— |
IN ('electronic','immediate_spin','ahg','other') |
Crossmatch method |
result |
VARCHAR(20) |
NO |
— |
IN ('compatible','incompatible','not_performed','cancelled') |
Crossmatch result |
incompatibility_reason |
VARCHAR(200) |
YES |
— |
— |
Reason if incompatible |
performed_by |
BIGINT |
NO |
— |
FK → users.user_id |
Technologist |
performed_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
When performed |
valid_until |
TIMESTAMP |
YES |
— |
— |
Validity of crossmatch |
comments |
TEXT |
YES |
— |
— |
Additional notes |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Created by |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Updated timestamp |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Updated by |
SQL DDL
SQLCREATE TABLE crossmatch_records (
crossmatch_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id BIGINT NOT NULL,
component_id BIGINT NOT NULL,
ts_id BIGINT NOT NULL,
order_id BIGINT,
crossmatch_type VARCHAR(20) NOT NULL,
result VARCHAR(20) NOT NULL,
incompatibility_reason VARCHAR(200),
performed_by BIGINT NOT NULL,
performed_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
valid_until TIMESTAMP,
comments TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT fk_cm_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_cm_component
FOREIGN KEY (component_id) REFERENCES blood_components(component_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_cm_ts
FOREIGN KEY (ts_id) REFERENCES blood_type_screen(ts_id),
CONSTRAINT fk_cm_order
FOREIGN KEY (order_id) REFERENCES transfusion_orders(order_id),
CONSTRAINT fk_cm_performed_by
FOREIGN KEY (performed_by) REFERENCES users(user_id),
CONSTRAINT fk_cm_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
CONSTRAINT fk_cm_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id),
CONSTRAINT chk_cm_type
CHECK (crossmatch_type IN ('electronic','immediate_spin','ahg','other')),
CONSTRAINT chk_cm_result
CHECK (result IN ('compatible','incompatible','not_performed','cancelled')),
CONSTRAINT chk_cm_incompatibility_reason
CHECK (result <> 'incompatible' OR incompatibility_reason IS NOT NULL)
);
CREATE INDEX idx_cm_patient ON crossmatch_records(patient_id);
CREATE INDEX idx_cm_component ON crossmatch_records(component_id);
CREATE INDEX idx_cm_order ON crossmatch_records(order_id);
CREATE INDEX idx_cm_result ON crossmatch_records(result);
Terminology Bindings
| Field |
Terminology |
Example Value |
crossmatch_type |
Crossmatch Eligibility Rules master |
electronic |
result |
Local ValueSet |
compatible |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
crossmatch_id |
Observation |
Observation.id |
patient_id |
Patient |
Observation.subject |
component_id |
BiologicallyDerivedProduct |
Observation.focus |
crossmatch_type |
Observation |
method |
result |
Observation |
valueCodeableConcept |
performed_datetime |
Observation |
effectiveDateTime |
order_id |
ServiceRequest |
Observation.basedOn |
7. transfusion_orders
Purpose: Physician orders for blood transfusion, including requested component type, units, priority, indication, and special requirements. Integrated with CPOE.
Field Specifications
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
order_id |
BIGINT |
NO |
IDENTITY |
PK |
Transfusion order ID |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
encounter_id |
BIGINT |
NO |
— |
FK → encounters.encounter_id |
Encounter |
ordering_provider_id |
BIGINT |
NO |
— |
FK → providers.provider_id |
Ordering physician |
component_type_requested |
VARCHAR(30) |
NO |
— |
FK → md_blood_component_types.code |
Requested component type |
units_requested |
INTEGER |
NO |
1 |
> 0 |
Number of units requested |
priority |
VARCHAR(20) |
NO |
'routine' |
IN ('routine','urgent','stat','massive_transfusion') |
Priority |
indication_code |
VARCHAR(10) |
YES |
— |
FK → md_transfusion_indications.code |
Coded indication |
indication_text |
VARCHAR(500) |
YES |
— |
— |
Free-text indication |
special_requirements |
VARCHAR(200) |
YES |
— |
— |
e.g., "irradiated, CMV-negative" |
target_hb_g_dl |
NUMERIC(4,1) |
YES |
— |
— |
Target hemoglobin if RBCs |
order_status |
VARCHAR(20) |
NO |
'pending' |
IN ('pending','in_progress','completed','cancelled') |
Order status |
order_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
When order placed |
needed_by_datetime |
TIMESTAMP |
YES |
— |
— |
When blood needed |
emergency_release |
BOOLEAN |
NO |
FALSE |
— |
Uncrossmatched/emergency release flag |
massive_transfusion_protocol |
BOOLEAN |
NO |
FALSE |
— |
Part of MTP |
comments |
TEXT |
YES |
— |
— |
Additional comments |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Created by (CPOE user) |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Updated timestamp |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Updated by |
SQL DDL
SQLCREATE TABLE transfusion_orders (
order_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id BIGINT NOT NULL,
encounter_id BIGINT NOT NULL,
ordering_provider_id BIGINT NOT NULL,
component_type_requested VARCHAR(30) NOT NULL,
units_requested INTEGER NOT NULL DEFAULT 1,
priority VARCHAR(20) NOT NULL DEFAULT 'routine',
indication_code VARCHAR(10),
indication_text VARCHAR(500),
special_requirements VARCHAR(200),
target_hb_g_dl NUMERIC(4,1),
order_status VARCHAR(20) NOT NULL DEFAULT 'pending',
order_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
needed_by_datetime TIMESTAMP,
emergency_release BOOLEAN NOT NULL DEFAULT FALSE,
massive_transfusion_protocol BOOLEAN NOT NULL DEFAULT FALSE,
comments TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT fk_to_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_to_encounter
FOREIGN KEY (encounter_id) REFERENCES encounters(encounter_id),
CONSTRAINT fk_to_provider
FOREIGN KEY (ordering_provider_id) REFERENCES providers(provider_id),
CONSTRAINT fk_to_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
CONSTRAINT fk_to_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id),
CONSTRAINT fk_to_indication_code
FOREIGN KEY (indication_code) REFERENCES md_transfusion_indications(code),
CONSTRAINT chk_to_units
CHECK (units_requested > 0),
CONSTRAINT chk_to_priority
CHECK (priority IN ('routine','urgent','stat','massive_transfusion')),
CONSTRAINT chk_to_status
CHECK (order_status IN ('pending','in_progress','completed','cancelled'))
);
CREATE INDEX idx_to_patient ON transfusion_orders(patient_id, order_status);
CREATE INDEX idx_to_encounter ON transfusion_orders(encounter_id);
CREATE INDEX idx_to_provider ON transfusion_orders(ordering_provider_id);
CREATE INDEX idx_to_priority ON transfusion_orders(priority);
CREATE INDEX idx_to_emergency ON transfusion_orders(emergency_release) WHERE emergency_release = TRUE;
Terminology Bindings
| Field |
Terminology |
Example Value |
component_type_requested |
ISBT 128 product codes / master |
RBC_LEUKOREDUCED |
indication_code |
Facility-defined Transfusion Indication Codes |
ANEMIA_HB_LT7 |
priority |
Local ValueSet |
stat |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
order_id |
ServiceRequest |
ServiceRequest.id |
patient_id |
Patient |
ServiceRequest.subject |
encounter_id |
Encounter |
ServiceRequest.encounter |
ordering_provider_id |
Practitioner |
ServiceRequest.requester |
component_type_requested |
ServiceRequest |
code (SNOMED CT for transfusion of RBC/platelets) |
units_requested |
ServiceRequest |
quantity.value |
priority |
ServiceRequest |
priority |
indication_code |
Condition |
ServiceRequest.reasonCode (ICD-10-AM or local) |
emergency_release |
ServiceRequest |
extension[emergency-release].valueBoolean |
8. transfusion_administration
Purpose: Bedside transfusion administration records, including verification, timing, volume, rate, vitals, and whether a reaction occurred.
Field Specifications
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
admin_id |
BIGINT |
NO |
IDENTITY |
PK |
Transfusion administration ID |
order_id |
BIGINT |
NO |
— |
FK → transfusion_orders.order_id |
Related order |
component_id |
BIGINT |
NO |
— |
FK → blood_components.component_id |
Component transfused |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
encounter_id |
BIGINT |
NO |
— |
FK → encounters.encounter_id |
Encounter |
verifier1_id |
BIGINT |
NO |
— |
FK → users.user_id |
First verifier (nurse) |
verifier2_id |
BIGINT |
YES |
— |
FK → users.user_id |
Second verifier |
start_datetime |
TIMESTAMP |
NO |
— |
— |
Start of transfusion |
end_datetime |
TIMESTAMP |
YES |
— |
≥ start_datetime |
End of transfusion |
volume_infused |
INTEGER |
YES |
— |
≥ 0 |
Volume infused (mL) |
rate_ml_per_hr |
INTEGER |
YES |
— |
≥ 0 |
Average rate |
pre_vitals_json |
JSONB |
YES |
— |
— |
Pre-transfusion vitals (structured) |
post_vitals_json |
JSONB |
YES |
— |
— |
Post-transfusion vitals |
intra_vitals_json |
JSONB |
YES |
— |
— |
Vitals during transfusion timeline |
reaction_occurred |
BOOLEAN |
NO |
FALSE |
— |
Whether reaction occurred |
reaction_id |
BIGINT |
YES |
— |
FK → transfusion_reactions.reaction_id |
Linked reaction if any |
status |
VARCHAR(20) |
NO |
'in_progress' |
IN ('planned','in_progress','completed','stopped','cancelled') |
Administration status |
bedside_location |
VARCHAR(100) |
YES |
— |
— |
Bed/room identifier |
product_barcode_scanned |
BOOLEAN |
NO |
FALSE |
— |
Barcode scan completed |
patient_barcode_scanned |
BOOLEAN |
NO |
FALSE |
— |
Wristband scan completed |
notes |
TEXT |
YES |
— |
— |
Additional notes |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Created by |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Updated timestamp |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Updated by |
SQL DDL
SQLCREATE TABLE transfusion_administration (
admin_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
order_id BIGINT NOT NULL,
component_id BIGINT NOT NULL,
patient_id BIGINT NOT NULL,
encounter_id BIGINT NOT NULL,
verifier1_id BIGINT NOT NULL,
verifier2_id BIGINT,
start_datetime TIMESTAMP NOT NULL,
end_datetime TIMESTAMP,
volume_infused INTEGER,
rate_ml_per_hr INTEGER,
pre_vitals_json JSONB,
post_vitals_json JSONB,
intra_vitals_json JSONB,
reaction_occurred BOOLEAN NOT NULL DEFAULT FALSE,
reaction_id BIGINT,
status VARCHAR(20) NOT NULL DEFAULT 'in_progress',
bedside_location VARCHAR(100),
product_barcode_scanned BOOLEAN NOT NULL DEFAULT FALSE,
patient_barcode_scanned BOOLEAN NOT NULL DEFAULT FALSE,
notes TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT fk_ta_order
FOREIGN KEY (order_id) REFERENCES transfusion_orders(order_id),
CONSTRAINT fk_ta_component
FOREIGN KEY (component_id) REFERENCES blood_components(component_id),
CONSTRAINT fk_ta_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id),
CONSTRAINT fk_ta_encounter
FOREIGN KEY (encounter_id) REFERENCES encounters(encounter_id),
CONSTRAINT fk_ta_verifier1
FOREIGN KEY (verifier1_id) REFERENCES users(user_id),
CONSTRAINT fk_ta_verifier2
FOREIGN KEY (verifier2_id) REFERENCES users(user_id),
CONSTRAINT fk_ta_reaction
FOREIGN KEY (reaction_id) REFERENCES transfusion_reactions(reaction_id),
CONSTRAINT fk_ta_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
CONSTRAINT fk_ta_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id),
CONSTRAINT chk_ta_status
CHECK (status IN ('planned','in_progress','completed','stopped','cancelled')),
CONSTRAINT chk_ta_times
CHECK (end_datetime IS NULL OR end_datetime >= start_datetime),
CONSTRAINT chk_ta_volume
CHECK (volume_infused IS NULL OR volume_infused >= 0),
CONSTRAINT chk_ta_rate
CHECK (rate_ml_per_hr IS NULL OR rate_ml_per_hr >= 0)
);
CREATE INDEX idx_ta_patient ON transfusion_administration(patient_id);
CREATE INDEX idx_ta_order ON transfusion_administration(order_id);
CREATE INDEX idx_ta_component ON transfusion_administration(component_id);
CREATE INDEX idx_ta_status ON transfusion_administration(status);
CREATE INDEX idx_ta_reaction ON transfusion_administration(reaction_occurred) WHERE reaction_occurred = TRUE;
Terminology Bindings
Vitals JSON fields should use:
| JSON Path (example) |
Terminology |
Example Value |
pre_vitals_json.bp_systolic.code |
LOINC |
8480-6 |
pre_vitals_json.bp_diastolic.code |
LOINC |
8462-4 |
pre_vitals_json.temperature.code |
LOINC |
8310-5 |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
admin_id |
Procedure |
Procedure.id |
patient_id |
Patient |
Procedure.subject |
encounter_id |
Encounter |
Procedure.encounter |
order_id |
ServiceRequest |
Procedure.basedOn |
component_id |
BiologicallyDerivedProduct |
Procedure.usedReference |
start_datetime, end_datetime |
Procedure |
performedPeriod.start / .end |
status |
Procedure |
status |
pre_vitals_json, post_vitals_json |
Observation |
Separate Observation resources linked via partOf |
9. transfusion_reactions
Purpose: Documentation and investigation of adverse transfusion reactions, including classification, severity, investigation findings, and MOH hemovigilance reporting status.
Field Specifications
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
reaction_id |
BIGINT |
NO |
IDENTITY |
PK |
Reaction record ID |
admin_id |
BIGINT |
NO |
— |
FK → transfusion_administration.admin_id |
Related administration |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
component_id |
BIGINT |
NO |
— |
FK → blood_components.component_id |
Component involved |
reaction_type |
VARCHAR(30) |
NO |
— |
FK → md_transfusion_reaction_types.code |
Reaction type |
severity |
VARCHAR(20) |
NO |
— |
IN ('mild','moderate','severe','life_threatening','death') |
Severity |
symptoms |
TEXT |
NO |
— |
— |
Signs/symptoms description |
onset_time |
TIMESTAMP |
NO |
— |
— |
Time of reaction onset |
investigation_findings |
TEXT |
YES |
— |
— |
Investigation summary |
clerical_check_ok |
BOOLEAN |
NO |
TRUE |
— |
Identity/label check result |
dat_result |
VARCHAR(20) |
YES |
— |
IN ('positive','negative','not_done') |
Direct antiglobulin test |
hemolysis_check |
VARCHAR(20) |
YES |
— |
IN ('present','absent','not_done') |
Hemolysis in plasma/urine |
classification |
VARCHAR(50) |
YES |
— |
— |
Final classification (e.g., "definite hemolytic") |
reported_to_moh |
BOOLEAN |
NO |
FALSE |
— |
Reported to MOH hemovigilance |
moh_report_reference |
VARCHAR(50) |
YES |
— |
— |
MOH report ID/reference |
investigated_by |
BIGINT |
NO |
— |
FK → providers.provider_id |
Pathologist/medical director |
investigation_completed_datetime |
TIMESTAMP |
YES |
— |
— |
When investigation completed |
outcome |
VARCHAR(30) |
YES |
— |
IN ('recovered','ongoing','death','unknown') |
Patient outcome |
related_lab_result_id |
BIGINT |
YES |
— |
FK → lab_results.result_id |
Key post-reaction lab panel |
comments |
TEXT |
YES |
— |
— |
Additional notes |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Created by |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Updated timestamp |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Updated by |
SQL DDL
SQLCREATE TABLE transfusion_reactions (
reaction_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
admin_id BIGINT NOT NULL,
patient_id BIGINT NOT NULL,
component_id BIGINT NOT NULL,
reaction_type VARCHAR(30) NOT NULL,
severity VARCHAR(20) NOT NULL,
symptoms TEXT NOT NULL,
onset_time TIMESTAMP NOT NULL,
investigation_findings TEXT,
clerical_check_ok BOOLEAN NOT NULL DEFAULT TRUE,
dat_result VARCHAR(20),
hemolysis_check VARCHAR(20),
classification VARCHAR(50),
reported_to_moh BOOLEAN NOT NULL DEFAULT FALSE,
moh_report_reference VARCHAR(50),
investigated_by BIGINT NOT NULL,
investigation_completed_datetime TIMESTAMP,
outcome VARCHAR(30),
related_lab_result_id BIGINT,
comments TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT fk_tr_admin
FOREIGN KEY (admin_id) REFERENCES transfusion_administration(admin_id),
CONSTRAINT fk_tr_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id),
CONSTRAINT fk_tr_component
FOREIGN KEY (component_id) REFERENCES blood_components(component_id),
CONSTRAINT fk_tr_investigated_by
FOREIGN KEY (investigated_by) REFERENCES providers(provider_id),
CONSTRAINT fk_tr_lab_result
FOREIGN KEY (related_lab_result_id) REFERENCES lab_results(result_id),
CONSTRAINT fk_tr_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
CONSTRAINT fk_tr_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id),
CONSTRAINT fk_tr_reaction_type
FOREIGN KEY (reaction_type) REFERENCES md_transfusion_reaction_types(code),
CONSTRAINT chk_tr_severity
CHECK (severity IN ('mild','moderate','severe','life_threatening','death')),
CONSTRAINT chk_tr_dat_result
CHECK (dat_result IS NULL OR dat_result IN ('positive','negative','not_done')),
CONSTRAINT chk_tr_hemolysis
CHECK (hemolysis_check IS NULL OR hemolysis_check IN ('present','absent','not_done')),
CONSTRAINT chk_tr_outcome
CHECK (outcome IS NULL OR outcome IN ('recovered','ongoing','death','unknown')),
CONSTRAINT chk_tr_moh_reporting
CHECK (reported_to_moh = FALSE OR moh_report_reference IS NOT NULL)
);
CREATE INDEX idx_tr_patient ON transfusion_reactions(patient_id);
CREATE INDEX idx_tr_type ON transfusion_reactions(reaction_type);
CREATE INDEX idx_tr_moh ON transfusion_reactions(reported_to_moh);
CREATE INDEX idx_tr_admin ON transfusion_reactions(admin_id);
Terminology Bindings
| Field |
Terminology |
Example Value |
reaction_type |
ISBT hemovigilance + local |
FNHTR, ALLERGIC_MILD |
classification |
ISBT hemovigilance |
definite, possible |
severity |
Local ValueSet |
severe |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
reaction_id |
AdverseEvent |
AdverseEvent.id |
patient_id |
Patient |
AdverseEvent.subject |
component_id |
BiologicallyDerivedProduct |
AdverseEvent.suspectEntity.instance |
onset_time |
AdverseEvent |
AdverseEvent.date |
reaction_type |
AdverseEvent |
AdverseEvent.event |
severity |
AdverseEvent |
AdverseEvent.seriousness / severity |
reported_to_moh |
AdverseEvent |
AdverseEvent.outcome / extension[reportedToAuthority] |
investigation_findings |
AdverseEvent |
AdverseEvent.note |
10. blood_quarantine_log
Purpose: Quarantine tracking for units pending testing or under investigation (e.g., positive infectious markers, suspected reaction).
Field Specifications
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
quarantine_id |
BIGINT |
NO |
IDENTITY |
PK |
Quarantine record ID |
component_id |
BIGINT |
NO |
— |
FK → blood_components.component_id |
Component quarantined |
quarantine_reason |
VARCHAR(200) |
NO |
— |
— |
Reason for quarantine |
quarantined_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
When quarantined |
quarantined_by |
BIGINT |
NO |
— |
FK → users.user_id |
User who quarantined |
released_datetime |
TIMESTAMP |
YES |
— |
≥ quarantined_datetime |
When released |
released_by |
BIGINT |
YES |
— |
FK → users.user_id |
User who released |
release_reason |
VARCHAR(200) |
YES |
— |
— |
Reason for release |
final_disposition |
VARCHAR(20) |
YES |
— |
IN ('returned_to_inventory','discarded','transfused') |
Outcome after quarantine |
notes |
TEXT |
YES |
— |
— |
Additional notes |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Created by |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Updated timestamp |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Updated by |
SQL DDL
SQLCREATE TABLE blood_quarantine_log (
quarantine_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
component_id BIGINT NOT NULL,
quarantine_reason VARCHAR(200) NOT NULL,
quarantined_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
quarantined_by BIGINT NOT NULL,
released_datetime TIMESTAMP,
released_by BIGINT,
release_reason VARCHAR(200),
final_disposition VARCHAR(20),
notes TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT fk_bql_component
FOREIGN KEY (component_id) REFERENCES blood_components(component_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_bql_quarantined_by
FOREIGN KEY (quarantined_by) REFERENCES users(user_id),
CONSTRAINT fk_bql_released_by
FOREIGN KEY (released_by) REFERENCES users(user_id),
CONSTRAINT fk_bql_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
CONSTRAINT fk_bql_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id),
CONSTRAINT chk_bql_disposition
CHECK (final_disposition IS NULL OR final_disposition IN ('returned_to_inventory','discarded','transfused')),
CONSTRAINT chk_bql_release_time
CHECK (released_datetime IS NULL OR released_datetime >= quarantined_datetime)
);
CREATE INDEX idx_bql_component ON blood_quarantine_log(component_id);
CREATE INDEX idx_bql_quarantine_time ON blood_quarantine_log(quarantined_datetime);
Terminology Bindings
| Field |
Terminology |
Example Value |
quarantine_reason |
Local ValueSet |
PENDING_INFECTIOUS_RESULTS |
final_disposition |
Local ValueSet |
discarded |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
quarantine_id |
BiologicallyDerivedProduct |
extension[quarantine-log].id |
component_id |
BiologicallyDerivedProduct |
BiologicallyDerivedProduct.id |
quarantine_reason |
BiologicallyDerivedProduct |
extension[quarantine-reason].valueCodeableConcept |
quarantined_datetime |
BiologicallyDerivedProduct |
extension[quarantine-start].valueDateTime |
11. blood_discard_log
Purpose: Documentation of discarded/expired blood products, including reason, method, and witnesses, to support MOH and internal audit requirements.
Field Specifications
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
discard_id |
BIGINT |
NO |
IDENTITY |
PK |
Discard record ID |
component_id |
BIGINT |
NO |
— |
FK → blood_components.component_id |
Component discarded |
discard_reason |
VARCHAR(100) |
NO |
— |
FK → md_discard_reasons.code |
Reason for discard |
discard_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
When discarded |
discarded_by |
BIGINT |
NO |
— |
FK → users.user_id |
User discarding |
witness_id |
BIGINT |
YES |
— |
FK → users.user_id |
Witness |
disposal_method |
VARCHAR(50) |
NO |
— |
IN ('biohazard_incineration','return_to_supplier','other') |
Disposal method |
biohazard_manifest_number |
VARCHAR(50) |
YES |
— |
— |
Manifest/reference number |
notes |
TEXT |
YES |
— |
— |
Additional notes |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created timestamp |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Created by |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Updated timestamp |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Updated by |
SQL DDL
SQLCREATE TABLE blood_discard_log (
discard_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
component_id BIGINT NOT NULL,
discard_reason VARCHAR(100) NOT NULL,
discard_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
discarded_by BIGINT NOT NULL,
witness_id BIGINT,
disposal_method VARCHAR(50) NOT NULL,
biohazard_manifest_number VARCHAR(50),
notes TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by BIGINT,
CONSTRAINT fk_bdl_component
FOREIGN KEY (component_id) REFERENCES blood_components(component_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_bdl_discarded_by
FOREIGN KEY (discarded_by) REFERENCES users(user_id),
CONSTRAINT fk_bdl_witness
FOREIGN KEY (witness_id) REFERENCES users(user_id),
CONSTRAINT fk_bdl_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
CONSTRAINT fk_bdl_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id),
CONSTRAINT fk_bdl_discard_reason
FOREIGN KEY (discard_reason) REFERENCES md_discard_reasons(code),
CONSTRAINT chk_bdl_disposal_method
CHECK (disposal_method IN ('biohazard_incineration','return_to_supplier','other'))
);
CREATE INDEX idx_bdl_component ON blood_discard_log(component_id);
CREATE INDEX idx_bdl_reason ON blood_discard_log(discard_reason);
CREATE INDEX idx_bdl_datetime ON blood_discard_log(discard_datetime);
Terminology Bindings
| Field |
Terminology |
Example Value |
discard_reason |
Local ValueSet (aligned with MOH) |
EXPIRED, INFECTIOUS_POSITIVE |
disposal_method |
Local ValueSet |
biohazard_incineration |
FHIR Resource Mapping
| Table Field |
FHIR Resource |
FHIR Path |
discard_id |
BiologicallyDerivedProduct |
extension[discard-log].id |
component_id |
BiologicallyDerivedProduct |
BiologicallyDerivedProduct.id |
discard_reason |
BiologicallyDerivedProduct |
extension[discard-reason].valueCodeableConcept |
discard_datetime |
BiologicallyDerivedProduct |
extension[discard-datetime].valueDateTime |
Terminology Master Data (Referenced)
These are master tables owned by the Blood Bank module but defined in the master-data specification; referenced here for clarity:
md_abo_groups (ABO/Rh Blood Group Codes)
md_rh_types
md_blood_component_types
md_transfusion_reaction_types
md_donor_deferral_reasons
md_infectious_disease_tests
md_component_storage_zones
md_crossmatch_eligibility_rules
md_transfusion_indications
md_discard_reasons
Data Volume Estimates
Approximate volumes for a 300-bed UAE hospital.
| Table |
Initial Rows (Year 1) |
Annual Growth |
Notes |
blood_donors |
5,000 |
+1,000/year |
Active + deferred donors |
blood_donations |
8,000 |
+8,000/year |
Includes mobile drives |
blood_components |
16,000 |
+16,000/year |
~2 components per donation average |
blood_component_inventory |
20,000 |
+20,000/year |
Includes historical issued/returned records |
blood_type_screen |
40,000 |
+40,000/year |
Inpatients + pre-op + antenatal |
crossmatch_records |
35,000 |
+35,000/year |
Multiple units per order |
transfusion_orders |
20,000 |
+20,000/year |
All components |
transfusion_administration |
25,000 |
+25,000/year |
Includes partially transfused units |
transfusion_reactions |
200 |
+200/year |
~0.5–1% reaction rate |
blood_quarantine_log |
3,000 |
+3,000/year |
Includes temporary quarantines |
blood_discard_log |
2,000 |
+2,000/year |
Expired, damaged, infectious |
Storage planning should allow for at least 10 years of online data for clinical traceability, with appropriate indexing and partitioning (e.g., by year on high-volume tables like blood_type_screen, crossmatch_records, transfusion_administration).
Data Retention Policy (UAE Context)
UAE MOH blood safety regulations and DOH/DHA hemovigilance requirements generally expect long-term traceability from donor to recipient, often 15–30 years. UAE PDPL requires data minimisation and secure archival.
| Table |
Minimum Retention |
Rationale / Notes |
blood_donors |
30 years after last donation |
Donor deferral and look-back investigations |
blood_donations |
30 years |
Traceability of infectious disease investigations |
blood_components |
30 years |
Donor-to-recipient traceability per MOH blood safety |
blood_component_inventory |
15 years |
Inventory and issue/return audit trail |
blood_type_screen |
15 years |
Historical blood group and antibody history for patient safety |
crossmatch_records |
15 years |
Compatibility evidence for transfusions |
transfusion_orders |
15 years |
Clinical decision and medico-legal record |
transfusion_administration |
15 years |
Bedside administration and verification audit |
transfusion_reactions |
30 years |
Hemovigilance, MOH reporting, medico-legal |
blood_quarantine_log |
15 years |
Quarantine decisions and outcomes |
blood_discard_log |
15 years |
Wastage, biohazard disposal audit |
Implementation Notes:
- After the retention period, records should be securely anonymised or destroyed in line with UAE PDPL and facility policy.
- For donor–recipient traceability, consider immutable audit logs and WORM storage for key linkage data (
blood_donations, blood_components, transfusion_administration, transfusion_reactions).
- Archival strategies (cold storage, partitioning) must still support regulatory queries and MOH hemovigilance audits on historical data.