Scheduling & Bed/OR Management Data Specifications
Shared Entity References
This module owns the canonical encounter entities and references the following shared entities defined in their owning modules.
| Shared Entity |
Owning Module |
Table(s) |
FK Used Here |
| Patients |
ehr-patient-mgmt |
patients, patient_demographics |
patients.patient_id |
| Providers |
ehr-patient-mgmt |
providers, provider_credentials |
providers.provider_id |
| Users & Auth |
ehr-patient-mgmt |
users, roles, permissions |
users.user_id |
| Facilities |
ehr-patient-mgmt |
facilities, departments, locations |
facilities.facility_id |
| Departments |
ehr-patient-mgmt |
departments |
departments.department_id |
| Locations |
ehr-patient-mgmt |
locations |
locations.location_id |
| Payers |
policy-contract-mgmt |
payers, insurance_plans, contracts |
payers.payer_id, insurance_plans.insurance_plan_id |
Entity Relationship Diagram
erDiagram
patients ||--o{ encounters : "has"
patients ||--o{ appointments : "has"
patients ||--o{ bed_assignments : "has"
patients ||--o{ bed_transfers : "has"
patients ||--o{ or_cases : "has"
patients ||--o{ waitlist_entries : "on"
providers ||--o{ encounters : "attending/admitting"
providers ||--o{ appointments : "for"
providers ||--o{ provider_schedules : "owns"
providers ||--o{ or_schedules : "surgeon/anesth"
providers ||--o{ or_cases : "surgeon/anesth"
facilities ||--o{ encounters : "at"
facilities ||--o{ appointments : "at"
facilities ||--o{ beds : "contains"
facilities ||--o{ or_rooms : "contains"
facilities ||--o{ provider_schedules : "at"
facilities ||--o{ scheduling_rules : "for"
departments ||--o{ encounters : "service"
departments ||--o{ appointments : "clinic"
departments ||--o{ beds : "ward"
departments ||--o{ provider_schedules : "clinic"
departments ||--o{ scheduling_rules : "for"
locations ||--o{ beds : "located"
locations ||--o{ encounters : "location"
payers ||--o{ encounters : "covered by"
encounters ||--o{ encounter_details : "has"
encounters ||--o{ appointments : "may originate"
encounters ||--o{ bed_assignments : "uses"
encounters ||--o{ bed_transfers : "moves"
encounters ||--o{ or_cases : "includes"
appointment_types ||--o{ appointments : "typed as"
provider_schedules ||--o{ appointment_slots : "generate"
appointment_slots ||--o{ appointments : "booked in"
scheduling_templates ||--o{ provider_schedules : "instantiates"
scheduling_rules ||--o{ appointments : "govern"
scheduling_rules ||--o{ appointment_slots : "govern"
beds ||--o{ bed_assignments : "assigned"
beds ||--o{ bed_transfers : "from/to"
or_rooms ||--o{ or_schedules : "for"
or_schedules ||--o{ or_cases : "contains"
waitlist_entries ||--o{ appointments : "resolved by"
encounters {
bigint encounter_id PK
bigint patient_id FK
bigint facility_id FK
bigint department_id FK
bigint attending_provider_id FK
bigint admitting_provider_id FK
bigint referring_provider_id FK
bigint payer_id FK
bigint insurance_plan_id FK
}
encounter_details {
bigint detail_id PK
bigint encounter_id FK
bigint bed_id FK
}
appointments {
bigint appointment_id PK
bigint patient_id FK
bigint encounter_id FK
bigint provider_id FK
bigint department_id FK
bigint facility_id FK
bigint appointment_type_id FK
bigint slot_id FK
bigint rescheduled_from FK
bigint waitlist_entry_id FK
bigint created_by FK
}
appointment_slots {
bigint slot_id PK
bigint schedule_template_id FK
bigint provider_id FK
bigint department_id FK
bigint facility_id FK
}
appointment_types {
bigint type_id PK
}
beds {
bigint bed_id PK
bigint location_id FK
bigint facility_id FK
bigint department_id FK
}
bed_assignments {
bigint assignment_id PK
bigint bed_id FK
bigint patient_id FK
bigint encounter_id FK
}
bed_transfers {
bigint transfer_id PK
bigint encounter_id FK
bigint patient_id FK
bigint from_bed_id FK
bigint to_bed_id FK
bigint requested_by FK
bigint approved_by FK
}
or_rooms {
bigint room_id PK
bigint facility_id FK
}
or_schedules {
bigint schedule_id PK
bigint or_room_id FK
bigint surgeon_id FK
bigint anesthesiologist_id FK
}
or_cases {
bigint case_id PK
bigint schedule_id FK
bigint patient_id FK
bigint encounter_id FK
bigint surgeon_id FK
bigint anesthesiologist_id FK
}
waitlist_entries {
bigint entry_id PK
bigint patient_id FK
bigint preferred_provider_id FK
}
provider_schedules {
bigint schedule_id PK
bigint provider_id FK
bigint facility_id FK
bigint department_id FK
}
scheduling_rules {
bigint rule_id PK
bigint facility_id FK
bigint department_id FK
bigint appointment_type_id FK
}
scheduling_templates {
bigint template_id PK
bigint provider_id FK
bigint department_id FK
bigint created_by FK
}
Table Definitions
Note: SQL examples assume PostgreSQL 14+. Adapt types/constraints as needed for the target RDBMS.
1. encounters
Purpose
Canonical encounter record for all patient visits/admissions (outpatient, ED, inpatient, day surgery). Referenced by all clinical and RCM modules.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
encounter_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique encounter identifier |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
encounter_type |
VARCHAR(30) |
NO |
— |
CHECK in controlled set |
Local type (e.g. OPD, ED, IP, DS) |
encounter_class |
VARCHAR(10) |
NO |
— |
HL7 v3 class codes |
AMB, EMER, IMP, OBS, etc. |
encounter_status |
VARCHAR(20) |
NO |
'planned' |
CHECK |
planned, in-progress, onleave, finished, cancelled |
admission_datetime |
TIMESTAMP |
YES |
— |
— |
Admission/visit start (for IP/ED) |
discharge_datetime |
TIMESTAMP |
YES |
— |
CHECK ≥ admission |
Discharge/visit end |
facility_id |
BIGINT |
NO |
— |
FK → facilities.facility_id |
Facility where encounter occurs |
department_id |
BIGINT |
YES |
— |
FK → departments.department_id |
Clinical department/service |
location_id |
BIGINT |
YES |
— |
FK → locations.location_id |
Primary location/ward/clinic |
attending_provider_id |
BIGINT |
YES |
— |
FK → providers.provider_id |
Attending physician |
admitting_provider_id |
BIGINT |
YES |
— |
FK → providers.provider_id |
Admitting physician |
referring_provider_id |
BIGINT |
YES |
— |
FK → providers.provider_id |
Referring physician |
admission_source |
VARCHAR(20) |
YES |
— |
HL7 Table 0023 subset |
e.g. ER, OPD, TRANSFER, BORN |
discharge_disposition |
VARCHAR(20) |
YES |
— |
HL7 Table 0112 + UAE |
e.g. HOME, TRANSFER, AMA, EXPIRED |
payer_id |
BIGINT |
YES |
— |
FK → payers.payer_id |
Primary payer |
insurance_plan_id |
BIGINT |
YES |
— |
FK → insurance_plans.insurance_plan_id |
Plan (e.g. THIQA, Daman) |
expected_los |
NUMERIC(5,2) |
YES |
— |
≥ 0 |
Expected length of stay (days) |
actual_los |
NUMERIC(5,2) |
YES |
— |
≥ 0 |
Calculated LOS (days) |
chief_complaint |
VARCHAR(500) |
YES |
— |
— |
Chief complaint (free text) |
chief_complaint_snomed |
VARCHAR(20) |
YES |
— |
SNOMED CT code |
Structured chief complaint |
uae_emirate_code |
VARCHAR(2) |
YES |
— |
IN ('AD','DU','SH','AJ','RA','FU','UA') |
Emirate of facility |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
User who created encounter |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Last updating user |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Creation timestamp |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Last update timestamp |
SQL DDL
SQLCREATE TABLE encounters (
encounter_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id BIGINT NOT NULL,
encounter_type VARCHAR(30) NOT NULL,
encounter_class VARCHAR(10) NOT NULL,
encounter_status VARCHAR(20) NOT NULL DEFAULT 'planned',
admission_datetime TIMESTAMP NULL,
discharge_datetime TIMESTAMP NULL,
facility_id BIGINT NOT NULL,
department_id BIGINT NULL,
location_id BIGINT NULL,
attending_provider_id BIGINT NULL,
admitting_provider_id BIGINT NULL,
referring_provider_id BIGINT NULL,
admission_source VARCHAR(20) NULL,
discharge_disposition VARCHAR(20) NULL,
payer_id BIGINT NULL,
insurance_plan_id BIGINT NULL,
expected_los NUMERIC(5,2) NULL,
actual_los NUMERIC(5,2) NULL,
chief_complaint VARCHAR(500) NULL,
chief_complaint_snomed VARCHAR(20) NULL,
uae_emirate_code VARCHAR(2) NULL,
created_by BIGINT NOT NULL,
updated_by BIGINT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_encounter_class
CHECK (encounter_class IN ('AMB','EMER','IMP','OBS','VR','HH')),
CONSTRAINT chk_encounter_status
CHECK (encounter_status IN ('planned','in-progress','onleave','finished','cancelled')),
CONSTRAINT chk_encounter_dates
CHECK (discharge_datetime IS NULL OR admission_datetime IS NULL
OR discharge_datetime >= admission_datetime),
CONSTRAINT chk_los_non_negative
CHECK ((expected_los IS NULL OR expected_los >= 0)
AND (actual_los IS NULL OR actual_los >= 0)),
CONSTRAINT chk_uae_emirate_code
CHECK (uae_emirate_code IS NULL OR uae_emirate_code IN ('AD','DU','SH','AJ','RA','FU','UA'))
);
-- FKs
ALTER TABLE encounters
ADD CONSTRAINT fk_enc_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_enc_facility
FOREIGN KEY (facility_id) REFERENCES facilities(facility_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_enc_department
FOREIGN KEY (department_id) REFERENCES departments(department_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_enc_location
FOREIGN KEY (location_id) REFERENCES locations(location_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_enc_attending
FOREIGN KEY (attending_provider_id) REFERENCES providers(provider_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_enc_admitting
FOREIGN KEY (admitting_provider_id) REFERENCES providers(provider_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_enc_referring
FOREIGN KEY (referring_provider_id) REFERENCES providers(provider_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_enc_payer
FOREIGN KEY (payer_id) REFERENCES payers(payer_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_enc_plan
FOREIGN KEY (insurance_plan_id) REFERENCES insurance_plans(insurance_plan_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_enc_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
ADD CONSTRAINT fk_enc_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id);
-- Indexes
CREATE INDEX idx_encounters_patient ON encounters(patient_id);
CREATE INDEX idx_encounters_facility_status ON encounters(facility_id, encounter_status);
CREATE INDEX idx_encounters_admission_dt ON encounters(admission_datetime);
CREATE INDEX idx_encounters_discharge_dt ON encounters(discharge_datetime);
CREATE INDEX idx_encounters_payer ON encounters(payer_id);
CREATE INDEX idx_encounters_attending ON encounters(attending_provider_id);
CREATE INDEX idx_encounters_class_status ON encounters(encounter_class, encounter_status);
CREATE INDEX idx_encounters_active_ip
ON encounters(facility_id, department_id, encounter_status)
WHERE encounter_class IN ('IMP','EMER') AND encounter_status IN ('in-progress','onleave');
Terminology Bindings
| Field |
Terminology |
Example Value |
encounter_class |
HL7 v3 ActCode |
IMP |
encounter_type |
Local + SNOMED CT (where mapped) |
OPD, ED, DAY_SURGERY |
admission_source |
HL7 v2 Table 0023 (subset) |
ER, OPD, TRANSFER |
discharge_disposition |
HL7 v2 Table 0112 + UAE extensions |
HOME, TRANSFER, AMA |
chief_complaint_snomed |
SNOMED CT |
422587007 (Chest pain) |
uae_emirate_code |
Local UAE code set |
DU (Dubai) |
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
encounter_id |
Encounter |
Encounter.id |
patient_id |
Encounter |
Encounter.subject.reference |
encounter_class |
Encounter |
Encounter.class.code |
encounter_type |
Encounter |
Encounter.type[0].coding[0].code |
encounter_status |
Encounter |
Encounter.status |
admission_datetime |
Encounter |
Encounter.period.start |
discharge_datetime |
Encounter |
Encounter.period.end |
facility_id |
Encounter |
Encounter.serviceProvider.reference |
department_id |
Encounter |
Encounter.location[0].extension(department) (extension) |
location_id |
Encounter |
Encounter.location[0].location.reference |
attending_provider_id |
Encounter |
Encounter.participant[type=attender].individual |
admitting_provider_id |
Encounter |
Encounter.participant[type=admitter].individual |
referring_provider_id |
Encounter |
Encounter.participant[type=referrer].individual |
admission_source |
Encounter |
Encounter.hospitalization.admitSource |
discharge_disposition |
Encounter |
Encounter.hospitalization.dischargeDisposition |
payer_id |
Coverage |
Coverage.payor (linked via Encounter.account) |
expected_los |
Encounter |
Extension expectedLengthOfStay |
actual_los |
Encounter |
Derived from Encounter.period |
chief_complaint |
Condition |
Condition.note or Condition.code.text |
chief_complaint_snomed |
Condition |
Condition.code.coding[0].code |
2. encounter_details
Purpose
Extended encounter metadata used for coding, DRG grouping, and bed/isolation context.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
detail_id |
BIGINT |
NO |
IDENTITY |
PK |
Unique detail row |
encounter_id |
BIGINT |
NO |
— |
FK → encounters.encounter_id |
Encounter |
admission_diagnosis_icd10 |
VARCHAR(10) |
YES |
— |
ICD-10-AM |
Admission diagnosis |
principal_diagnosis_icd10 |
VARCHAR(10) |
YES |
— |
ICD-10-AM |
Principal diagnosis |
drg_code |
VARCHAR(10) |
YES |
— |
Local/UAE DRG |
Grouping code |
case_weight |
NUMERIC(6,3) |
YES |
— |
≥ 0 |
DRG weight |
financial_class |
VARCHAR(20) |
YES |
— |
Local code |
SELF, GOV, PRIVATE, etc. |
pre_auth_number |
VARCHAR(50) |
YES |
— |
— |
Insurance pre-authorization |
bed_id |
BIGINT |
YES |
— |
FK → beds.bed_id |
Current bed (for IP) |
room_type_requested |
VARCHAR(20) |
YES |
— |
Local |
PRIVATE, SEMI, WARD, VIP |
isolation_required |
BOOLEAN |
NO |
FALSE |
— |
Isolation flag |
isolation_reason_snomed |
VARCHAR(20) |
YES |
— |
SNOMED CT |
Reason for isolation |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Updated |
SQL DDL
SQLCREATE TABLE encounter_details (
detail_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
encounter_id BIGINT NOT NULL,
admission_diagnosis_icd10 VARCHAR(10) NULL,
principal_diagnosis_icd10 VARCHAR(10) NULL,
drg_code VARCHAR(10) NULL,
case_weight NUMERIC(6,3) NULL,
financial_class VARCHAR(20) NULL,
pre_auth_number VARCHAR(50) NULL,
bed_id BIGINT NULL,
room_type_requested VARCHAR(20) NULL,
isolation_required BOOLEAN NOT NULL DEFAULT FALSE,
isolation_reason_snomed VARCHAR(20) NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_encdet_encounter
FOREIGN KEY (encounter_id) REFERENCES encounters(encounter_id) ON DELETE CASCADE,
CONSTRAINT fk_encdet_bed
FOREIGN KEY (bed_id) REFERENCES beds(bed_id) ON UPDATE CASCADE,
CONSTRAINT chk_encdet_case_weight
CHECK (case_weight IS NULL OR case_weight >= 0),
CONSTRAINT chk_encdet_room_type
CHECK (room_type_requested IS NULL OR room_type_requested IN ('PRIVATE','SEMI','WARD','VIP'))
);
CREATE UNIQUE INDEX ux_encounter_details_encounter
ON encounter_details(encounter_id);
CREATE INDEX idx_encdet_principal_dx ON encounter_details(principal_diagnosis_icd10);
CREATE INDEX idx_encdet_drg ON encounter_details(drg_code);
Terminology Bindings
| Field |
Terminology |
Example Value |
admission_diagnosis_icd10 |
ICD-10-AM |
J18.9 |
principal_diagnosis_icd10 |
ICD-10-AM |
I21.9 |
drg_code |
UAE DRG |
A-04-10 |
financial_class |
Local |
GOV, PRIVATE, SELF |
isolation_reason_snomed |
SNOMED CT |
186747009 (Infectious disease carrier) |
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
encounter_id |
Encounter |
Encounter.id |
admission_diagnosis_icd10 |
Condition |
Condition.code.coding[system=ICD-10-AM].code |
principal_diagnosis_icd10 |
Condition |
Condition.code.coding[system=ICD-10-AM].code |
drg_code |
Encounter |
Encounter.diagnosis[use=DRG].packageCode |
case_weight |
Encounter |
Extension drgCaseWeight |
financial_class |
Encounter |
Encounter.classHistory or Account.type |
bed_id |
Encounter |
Encounter.location[0].physicalType + extension (bed) |
room_type_requested |
Encounter |
Encounter.hospitalization.accommodation (extension) |
isolation_required |
Encounter |
Extension isolationStatus |
3. appointments
Purpose
Stores all outpatient and procedural appointments, including linkage to encounters, waitlist, and check-in.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
appointment_id |
BIGINT |
NO |
IDENTITY |
PK |
Appointment |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
encounter_id |
BIGINT |
YES |
— |
FK → encounters.encounter_id |
Linked encounter (shell or active) |
provider_id |
BIGINT |
NO |
— |
FK → providers.provider_id |
Primary provider |
department_id |
BIGINT |
NO |
— |
FK → departments.department_id |
Clinic department |
facility_id |
BIGINT |
NO |
— |
FK → facilities.facility_id |
Facility |
appointment_type_id |
BIGINT |
NO |
— |
FK → appointment_types.type_id |
Type |
slot_id |
BIGINT |
YES |
— |
FK → appointment_slots.slot_id |
Slot used |
appointment_datetime |
TIMESTAMP |
NO |
— |
— |
Start datetime |
duration_minutes |
INT |
NO |
15 |
> 0 |
Duration |
status |
VARCHAR(20) |
NO |
'booked' |
CHECK |
booked, checked_in, completed, cancelled, no_show |
check_in_time |
TIMESTAMP |
YES |
— |
— |
Check-in timestamp |
reason_for_visit |
VARCHAR(500) |
YES |
— |
— |
Reason (free text) |
reason_for_visit_snomed |
VARCHAR(20) |
YES |
— |
SNOMED CT |
Structured reason |
cancellation_reason |
VARCHAR(50) |
YES |
— |
Local code |
From cancellation master |
rescheduled_from |
BIGINT |
YES |
— |
FK → appointments.appointment_id |
Previous appointment |
waitlist_entry_id |
BIGINT |
YES |
— |
FK → waitlist_entries.entry_id |
Source waitlist |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Booking user |
updated_by |
BIGINT |
YES |
— |
FK → users.user_id |
Last updating user |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Updated |
SQL DDL
SQLCREATE TABLE appointments (
appointment_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id BIGINT NOT NULL,
encounter_id BIGINT NULL,
provider_id BIGINT NOT NULL,
department_id BIGINT NOT NULL,
facility_id BIGINT NOT NULL,
appointment_type_id BIGINT NOT NULL,
slot_id BIGINT NULL,
appointment_datetime TIMESTAMP NOT NULL,
duration_minutes INT NOT NULL DEFAULT 15,
status VARCHAR(20) NOT NULL DEFAULT 'booked',
check_in_time TIMESTAMP NULL,
reason_for_visit VARCHAR(500) NULL,
reason_for_visit_snomed VARCHAR(20) NULL,
cancellation_reason VARCHAR(50) NULL,
rescheduled_from BIGINT NULL,
waitlist_entry_id BIGINT NULL,
created_by BIGINT NOT NULL,
updated_by BIGINT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_appointment_status
CHECK (status IN ('booked','checked_in','completed','cancelled','no_show')),
CONSTRAINT chk_appointment_duration
CHECK (duration_minutes > 0)
);
ALTER TABLE appointments
ADD CONSTRAINT fk_appt_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_appt_encounter
FOREIGN KEY (encounter_id) REFERENCES encounters(encounter_id) ON DELETE SET NULL,
ADD CONSTRAINT fk_appt_provider
FOREIGN KEY (provider_id) REFERENCES providers(provider_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_appt_department
FOREIGN KEY (department_id) REFERENCES departments(department_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_appt_facility
FOREIGN KEY (facility_id) REFERENCES facilities(facility_id) ON UPDATE CASCADE,
ADD CONSTRAINT fk_appt_type
FOREIGN KEY (appointment_type_id) REFERENCES appointment_types(type_id),
ADD CONSTRAINT fk_appt_slot
FOREIGN KEY (slot_id) REFERENCES appointment_slots(slot_id) ON DELETE SET NULL,
ADD CONSTRAINT fk_appt_rescheduled_from
FOREIGN KEY (rescheduled_from) REFERENCES appointments(appointment_id),
ADD CONSTRAINT fk_appt_waitlist
FOREIGN KEY (waitlist_entry_id) REFERENCES waitlist_entries(entry_id),
ADD CONSTRAINT fk_appt_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id),
ADD CONSTRAINT fk_appt_updated_by
FOREIGN KEY (updated_by) REFERENCES users(user_id);
CREATE INDEX idx_appt_patient ON appointments(patient_id, appointment_datetime);
CREATE INDEX idx_appt_provider_date ON appointments(provider_id, appointment_datetime);
CREATE INDEX idx_appt_facility_dept_date ON appointments(facility_id, department_id, appointment_datetime);
CREATE INDEX idx_appt_status ON appointments(status);
CREATE INDEX idx_appt_waitlist ON appointments(waitlist_entry_id);
CREATE INDEX idx_appt_encounter ON appointments(encounter_id);
Terminology Bindings
| Field |
Terminology |
Example Value |
reason_for_visit_snomed |
SNOMED CT |
65363002 (Otitis media) |
cancellation_reason |
Local |
PATIENT_REQUEST, NO_SHOW_POLICY, PROVIDER_UNAVAILABLE |
status |
HL7 / FHIR AppointmentStatus |
booked, cancelled |
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
appointment_id |
Appointment |
Appointment.id |
patient_id |
Appointment |
Appointment.participant[patient].actor |
provider_id |
Appointment |
Appointment.participant[practitioner].actor |
facility_id |
Appointment |
Appointment.serviceProvider |
department_id |
Appointment |
Extension department |
appointment_type_id |
Appointment |
Appointment.appointmentType |
appointment_datetime |
Appointment |
Appointment.start |
duration_minutes |
Appointment |
Appointment.minutesDuration |
status |
Appointment |
Appointment.status |
reason_for_visit |
Appointment |
Appointment.description |
reason_for_visit_snomed |
Appointment |
Appointment.reasonCode[0].coding[0].code |
check_in_time |
Encounter |
Encounter.statusHistory or extension |
encounter_id |
Encounter |
Encounter.id and Appointment.basedOn |
4. appointment_slots
Purpose
Represents discrete bookable slots generated from provider schedules/templates.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
slot_id |
BIGINT |
NO |
IDENTITY |
PK |
Slot |
schedule_template_id |
BIGINT |
YES |
— |
FK → scheduling_templates.template_id |
Source template |
provider_id |
BIGINT |
NO |
— |
FK → providers.provider_id |
Provider |
department_id |
BIGINT |
NO |
— |
FK → departments.department_id |
Department |
facility_id |
BIGINT |
NO |
— |
FK → facilities.facility_id |
Facility |
slot_date |
DATE |
NO |
— |
— |
Date |
start_time |
TIME |
NO |
— |
— |
Start time |
end_time |
TIME |
NO |
— |
CHECK > start |
End time |
slot_type |
VARCHAR(20) |
NO |
'standard' |
Local |
standard, urgent, followup, procedure |
max_overbooking |
INT |
NO |
0 |
≥ 0 |
Allowed overbook count |
current_bookings |
INT |
NO |
0 |
≥ 0 |
Current bookings |
is_blocked |
BOOLEAN |
NO |
FALSE |
— |
Blocked flag |
block_reason |
VARCHAR(200) |
YES |
— |
— |
Reason if blocked |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Updated |
SQL DDL
SQLCREATE TABLE appointment_slots (
slot_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
schedule_template_id BIGINT NULL,
provider_id BIGINT NOT NULL,
department_id BIGINT NOT NULL,
facility_id BIGINT NOT NULL,
slot_date DATE NOT NULL,
start_time TIME NOT NULL,
end_time TIME NOT NULL,
slot_type VARCHAR(20) NOT NULL DEFAULT 'standard',
max_overbooking INT NOT NULL DEFAULT 0,
current_bookings INT NOT NULL DEFAULT 0,
is_blocked BOOLEAN NOT NULL DEFAULT FALSE,
block_reason VARCHAR(200) NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_slot_time
CHECK (end_time > start_time),
CONSTRAINT chk_slot_overbooking
CHECK (max_overbooking >= 0 AND current_bookings >= 0)
);
ALTER TABLE appointment_slots
ADD CONSTRAINT fk_slot_template
FOREIGN KEY (schedule_template_id) REFERENCES scheduling_templates(template_id) ON DELETE SET NULL,
ADD CONSTRAINT fk_slot_provider
FOREIGN KEY (provider_id) REFERENCES providers(provider_id),
ADD CONSTRAINT fk_slot_department
FOREIGN KEY (department_id) REFERENCES departments(department_id),
ADD CONSTRAINT fk_slot_facility
FOREIGN KEY (facility_id) REFERENCES facilities(facility_id);
CREATE INDEX idx_slots_provider_date
ON appointment_slots(provider_id, slot_date, start_time);
CREATE INDEX idx_slots_facility_dept_date
ON appointment_slots(facility_id, department_id, slot_date);
CREATE INDEX idx_slots_availability
ON appointment_slots(provider_id, slot_date)
WHERE is_blocked = FALSE;
Terminology Bindings
| Field |
Terminology |
Example Value |
slot_type |
Local |
standard, urgent |
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
slot_id |
Slot |
Slot.id |
provider_id |
Slot |
Slot.schedule.actor (Practitioner) |
facility_id |
Slot |
Slot.schedule.actor (Organization) |
slot_date + start_time |
Slot |
Slot.start |
slot_date + end_time |
Slot |
Slot.end |
is_blocked |
Slot |
Slot.status (busy-unavailable if TRUE) |
5. appointment_types
Purpose
Master data for appointment categories (new, follow-up, procedure, telehealth) with default durations and flags.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
type_id |
BIGINT |
NO |
IDENTITY |
PK |
Type |
type_name_en |
VARCHAR(100) |
NO |
— |
— |
English name |
type_name_ar |
VARCHAR(100) |
NO |
— |
— |
Arabic name |
default_duration |
INT |
NO |
15 |
> 0 |
Default minutes |
prep_time |
INT |
NO |
0 |
≥ 0 |
Pre-visit buffer (min) |
post_time |
INT |
NO |
0 |
≥ 0 |
Post-visit buffer (min) |
color_code |
VARCHAR(7) |
YES |
— |
#RRGGBB |
UI color |
requires_referral |
BOOLEAN |
NO |
FALSE |
— |
Referral required |
requires_pre_auth |
BOOLEAN |
NO |
FALSE |
— |
Insurance pre-auth required |
is_active |
BOOLEAN |
NO |
TRUE |
— |
Active flag |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Updated |
SQL DDL
SQLCREATE TABLE appointment_types (
type_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
type_name_en VARCHAR(100) NOT NULL,
type_name_ar VARCHAR(100) NOT NULL,
default_duration INT NOT NULL DEFAULT 15,
prep_time INT NOT NULL DEFAULT 0,
post_time INT NOT NULL DEFAULT 0,
color_code VARCHAR(7) NULL,
requires_referral BOOLEAN NOT NULL DEFAULT FALSE,
requires_pre_auth BOOLEAN NOT NULL DEFAULT FALSE,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_appttype_duration
CHECK (default_duration > 0 AND prep_time >= 0 AND post_time >= 0),
CONSTRAINT chk_appttype_color
CHECK (color_code IS NULL OR color_code ~ '^#[0-9A-Fa-f]{6}$')
);
CREATE UNIQUE INDEX ux_appttype_name_en ON appointment_types(type_name_en);
Terminology Bindings
Local master; no external terminology required.
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
type_id |
Appointment |
Appointment.appointmentType.coding.code (local system) |
type_name_en |
Appointment |
Appointment.appointmentType.text |
requires_referral |
Appointment |
Extension requiresReferral |
6. beds
Purpose
Physical bed registry with ward, gender, isolation, and cleaning status; used for real-time bed board.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
bed_id |
BIGINT |
NO |
IDENTITY |
PK |
Bed |
location_id |
BIGINT |
NO |
— |
FK → locations.location_id |
Physical location |
facility_id |
BIGINT |
NO |
— |
FK → facilities.facility_id |
Facility |
department_id |
BIGINT |
NO |
— |
FK → departments.department_id |
Ward/department |
ward_type |
VARCHAR(30) |
NO |
— |
Local |
MEDICAL, SURGICAL, ICU, PICU, etc. |
bed_number |
VARCHAR(20) |
NO |
— |
Unique per ward |
Human-readable bed label |
bed_status |
VARCHAR(20) |
NO |
'available' |
CHECK |
available, occupied, reserved, out_of_service |
is_isolation |
BOOLEAN |
NO |
FALSE |
— |
Isolation-capable |
gender_restriction |
VARCHAR(10) |
YES |
— |
M, F, MIXED |
Gender restriction |
is_active |
BOOLEAN |
NO |
TRUE |
— |
Active bed |
cleaning_status |
VARCHAR(20) |
NO |
'ready' |
CHECK |
ready, needs_cleaning, in_progress |
last_cleaned_at |
TIMESTAMP |
YES |
— |
— |
Last cleaning time |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Updated |
SQL DDL
SQLCREATE TABLE beds (
bed_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
location_id BIGINT NOT NULL,
facility_id BIGINT NOT NULL,
department_id BIGINT NOT NULL,
ward_type VARCHAR(30) NOT NULL,
bed_number VARCHAR(20) NOT NULL,
bed_status VARCHAR(20) NOT NULL DEFAULT 'available',
is_isolation BOOLEAN NOT NULL DEFAULT FALSE,
gender_restriction VARCHAR(10) NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
cleaning_status VARCHAR(20) NOT NULL DEFAULT 'ready',
last_cleaned_at TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_bed_status
CHECK (bed_status IN ('available','occupied','reserved','out_of_service')),
CONSTRAINT chk_bed_cleaning_status
CHECK (cleaning_status IN ('ready','needs_cleaning','in_progress')),
CONSTRAINT chk_bed_gender
CHECK (gender_restriction IS NULL OR gender_restriction IN ('M','F','MIXED'))
);
ALTER TABLE beds
ADD CONSTRAINT fk_bed_location
FOREIGN KEY (location_id) REFERENCES locations(location_id),
ADD CONSTRAINT fk_bed_facility
FOREIGN KEY (facility_id) REFERENCES facilities(facility_id),
ADD CONSTRAINT fk_bed_department
FOREIGN KEY (department_id) REFERENCES departments(department_id);
CREATE UNIQUE INDEX ux_bed_facility_number
ON beds(facility_id, bed_number);
CREATE INDEX idx_beds_status
ON beds(facility_id, department_id, bed_status, cleaning_status);
Terminology Bindings
| Field |
Terminology |
Example Value |
ward_type |
Local |
ICU, MEDICAL |
bed_status |
Local |
available |
cleaning_status |
Local |
needs_cleaning |
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
bed_id |
Location |
Location.id |
bed_number |
Location |
Location.identifier / Location.name |
facility_id |
Location |
Location.partOf |
ward_type |
Location |
Location.type |
bed_status |
Encounter |
Encounter.location.status |
is_isolation |
Location |
Extension isIsolationBed |
gender_restriction |
Location |
Extension genderRestriction |
7. bed_assignments
Purpose
Tracks current and historical bed occupancy per encounter/patient.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
assignment_id |
BIGINT |
NO |
IDENTITY |
PK |
Assignment |
bed_id |
BIGINT |
NO |
— |
FK → beds.bed_id |
Bed |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
encounter_id |
BIGINT |
NO |
— |
FK → encounters.encounter_id |
Encounter |
assigned_datetime |
TIMESTAMP |
NO |
— |
— |
Start |
released_datetime |
TIMESTAMP |
YES |
— |
≥ assigned |
End |
assignment_reason |
VARCHAR(100) |
YES |
— |
— |
Reason (admission, transfer, overflow) |
is_current |
BOOLEAN |
NO |
TRUE |
— |
Current flag |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
SQL DDL
SQLCREATE TABLE bed_assignments (
assignment_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
bed_id BIGINT NOT NULL,
patient_id BIGINT NOT NULL,
encounter_id BIGINT NOT NULL,
assigned_datetime TIMESTAMP NOT NULL,
released_datetime TIMESTAMP NULL,
assignment_reason VARCHAR(100) NULL,
is_current BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_bedassign_dates
CHECK (released_datetime IS NULL OR released_datetime >= assigned_datetime)
);
ALTER TABLE bed_assignments
ADD CONSTRAINT fk_bedassign_bed
FOREIGN KEY (bed_id) REFERENCES beds(bed_id),
ADD CONSTRAINT fk_bedassign_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id),
ADD CONSTRAINT fk_bedassign_encounter
FOREIGN KEY (encounter_id) REFERENCES encounters(encounter_id) ON DELETE CASCADE;
CREATE INDEX idx_bedassign_bed_current
ON bed_assignments(bed_id)
WHERE is_current = TRUE;
CREATE INDEX idx_bedassign_encounter
ON bed_assignments(encounter_id, is_current);
Terminology Bindings
| Field |
Terminology |
Example Value |
assignment_reason |
Local |
ADMISSION, TRANSFER, BOARDING |
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
bed_id |
Encounter |
Encounter.location.location |
assigned_datetime |
Encounter |
Encounter.location.period.start |
released_datetime |
Encounter |
Encounter.location.period.end |
is_current |
Encounter |
Encounter.location.status |
8. bed_transfers
Purpose
Logs transfers between beds/wards, including approvals and ADT message linkage.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
transfer_id |
BIGINT |
NO |
IDENTITY |
PK |
Transfer |
encounter_id |
BIGINT |
NO |
— |
FK → encounters.encounter_id |
Encounter |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
from_bed_id |
BIGINT |
YES |
— |
FK → beds.bed_id |
Source bed |
to_bed_id |
BIGINT |
NO |
— |
FK → beds.bed_id |
Target bed |
transfer_reason |
VARCHAR(200) |
NO |
— |
— |
Reason (clinical, isolation, step-down) |
requested_by |
BIGINT |
NO |
— |
FK → users.user_id |
Requesting user |
approved_by |
BIGINT |
YES |
— |
FK → users.user_id |
Approver |
transfer_datetime |
TIMESTAMP |
NO |
— |
— |
Transfer time |
adt_message_id |
BIGINT |
YES |
— |
FK → integration_message_log.message_id |
ADT A02 reference |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
SQL DDL
SQLCREATE TABLE bed_transfers (
transfer_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
encounter_id BIGINT NOT NULL,
patient_id BIGINT NOT NULL,
from_bed_id BIGINT NULL,
to_bed_id BIGINT NOT NULL,
transfer_reason VARCHAR(200) NOT NULL,
requested_by BIGINT NOT NULL,
approved_by BIGINT NULL,
transfer_datetime TIMESTAMP NOT NULL,
adt_message_id BIGINT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE bed_transfers
ADD CONSTRAINT fk_bedtr_encounter
FOREIGN KEY (encounter_id) REFERENCES encounters(encounter_id) ON DELETE CASCADE,
ADD CONSTRAINT fk_bedtr_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id),
ADD CONSTRAINT fk_bedtr_from_bed
FOREIGN KEY (from_bed_id) REFERENCES beds(bed_id),
ADD CONSTRAINT fk_bedtr_to_bed
FOREIGN KEY (to_bed_id) REFERENCES beds(bed_id),
ADD CONSTRAINT fk_bedtr_requested_by
FOREIGN KEY (requested_by) REFERENCES users(user_id),
ADD CONSTRAINT fk_bedtr_approved_by
FOREIGN KEY (approved_by) REFERENCES users(user_id);
CREATE INDEX idx_bedtr_encounter ON bed_transfers(encounter_id, transfer_datetime);
CREATE INDEX idx_bedtr_patient ON bed_transfers(patient_id, transfer_datetime);
Terminology Bindings
| Field |
Terminology |
Example Value |
transfer_reason |
Local |
STEP_DOWN, ISOLATION, SPECIALTY_CHANGE |
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
from_bed_id |
Encounter |
Encounter.location[0] |
to_bed_id |
Encounter |
Encounter.location[1] |
transfer_datetime |
Encounter |
Encounter.location.period.start |
9. or_rooms
Purpose
Operating room registry with capabilities and cleaning turnaround.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
room_id |
BIGINT |
NO |
IDENTITY |
PK |
OR room |
facility_id |
BIGINT |
NO |
— |
FK → facilities.facility_id |
Facility |
room_name |
VARCHAR(50) |
NO |
— |
— |
Name/number |
room_type |
VARCHAR(20) |
NO |
— |
Local |
MAIN_OR, DAY_SURGERY, CATH_LAB, etc. |
equipment_list |
TEXT |
YES |
— |
JSON or CSV |
Equipment summary |
is_active |
BOOLEAN |
NO |
TRUE |
— |
Active |
max_daily_hours |
INT |
NO |
24 |
0–24 |
Max schedulable hours |
cleaning_turnaround_minutes |
INT |
NO |
30 |
≥ 0 |
Turnover time |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Updated |
SQL DDL
SQLCREATE TABLE or_rooms (
room_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
facility_id BIGINT NOT NULL,
room_name VARCHAR(50) NOT NULL,
room_type VARCHAR(20) NOT NULL,
equipment_list TEXT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
max_daily_hours INT NOT NULL DEFAULT 24,
cleaning_turnaround_minutes INT NOT NULL DEFAULT 30,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_orroom_hours
CHECK (max_daily_hours BETWEEN 0 AND 24),
CONSTRAINT chk_orroom_turnaround
CHECK (cleaning_turnaround_minutes >= 0)
);
ALTER TABLE or_rooms
ADD CONSTRAINT fk_orroom_facility
FOREIGN KEY (facility_id) REFERENCES facilities(facility_id);
CREATE UNIQUE INDEX ux_orroom_facility_name
ON or_rooms(facility_id, room_name);
Terminology Bindings
| Field |
Terminology |
Example Value |
room_type |
Local |
MAIN_OR, DAY_SURGERY |
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
room_id |
Location |
Location.id |
room_name |
Location |
Location.name |
room_type |
Location |
Location.type |
10. or_schedules
Purpose
Daily OR block schedules per room, including surgeon/anesthesiologist block ownership.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
schedule_id |
BIGINT |
NO |
IDENTITY |
PK |
Schedule block |
or_room_id |
BIGINT |
NO |
— |
FK → or_rooms.room_id |
OR room |
schedule_date |
DATE |
NO |
— |
— |
Date |
block_start |
TIME |
NO |
— |
— |
Block start |
block_end |
TIME |
NO |
— |
CHECK > start |
Block end |
surgeon_id |
BIGINT |
YES |
— |
FK → providers.provider_id |
Block owner surgeon |
anesthesiologist_id |
BIGINT |
YES |
— |
FK → providers.provider_id |
Block owner anesth |
status |
VARCHAR(20) |
NO |
'open' |
Local |
open, released, cancelled |
block_type |
VARCHAR(20) |
NO |
— |
Local |
BLOCKED, OPEN, EMERGENCY |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Updated |
SQL DDL
SQLCREATE TABLE or_schedules (
schedule_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
or_room_id BIGINT NOT NULL,
schedule_date DATE NOT NULL,
block_start TIME NOT NULL,
block_end TIME NOT NULL,
surgeon_id BIGINT NULL,
anesthesiologist_id BIGINT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'open',
block_type VARCHAR(20) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_orsched_time
CHECK (block_end > block_start),
CONSTRAINT chk_orsched_status
CHECK (status IN ('open','released','cancelled')),
CONSTRAINT chk_orsched_block_type
CHECK (block_type IN ('BLOCKED','OPEN','EMERGENCY'))
);
ALTER TABLE or_schedules
ADD CONSTRAINT fk_orsched_room
FOREIGN KEY (or_room_id) REFERENCES or_rooms(room_id),
ADD CONSTRAINT fk_orsched_surgeon
FOREIGN KEY (surgeon_id) REFERENCES providers(provider_id),
ADD CONSTRAINT fk_orsched_anesth
FOREIGN KEY (anesthesiologist_id) REFERENCES providers(provider_id);
CREATE INDEX idx_orsched_room_date
ON or_schedules(or_room_id, schedule_date);
Terminology Bindings
Local only.
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
schedule_id |
Schedule |
Schedule.id |
or_room_id |
Schedule |
Schedule.actor (Location) |
schedule_date + times |
Slot |
Slot.start / Slot.end (for OR-specific slots) |
11. or_cases
Purpose
Individual surgical cases scheduled within OR blocks, including CPT procedure codes and timing.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
case_id |
BIGINT |
NO |
IDENTITY |
PK |
Case |
schedule_id |
BIGINT |
NO |
— |
FK → or_schedules.schedule_id |
OR schedule |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
encounter_id |
BIGINT |
NO |
— |
FK → encounters.encounter_id |
Encounter |
surgeon_id |
BIGINT |
NO |
— |
FK → providers.provider_id |
Surgeon |
anesthesiologist_id |
BIGINT |
YES |
— |
FK → providers.provider_id |
Anesthesiologist |
procedure_code_cpt |
VARCHAR(10) |
NO |
— |
CPT |
Primary procedure |
procedure_description |
VARCHAR(255) |
NO |
— |
— |
Description |
estimated_duration |
INT |
NO |
— |
> 0 |
Minutes |
actual_start |
TIMESTAMP |
YES |
— |
— |
Actual start |
actual_end |
TIMESTAMP |
YES |
— |
≥ start |
Actual end |
case_status |
VARCHAR(20) |
NO |
'scheduled' |
Local |
scheduled, in_progress, completed, cancelled, postponed |
pre_op_checklist_complete |
BOOLEAN |
NO |
FALSE |
— |
Pre-op checklist done |
consent_verified |
BOOLEAN |
NO |
FALSE |
— |
Consent verified |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Updated |
SQL DDL
SQLCREATE TABLE or_cases (
case_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
schedule_id BIGINT NOT NULL,
patient_id BIGINT NOT NULL,
encounter_id BIGINT NOT NULL,
surgeon_id BIGINT NOT NULL,
anesthesiologist_id BIGINT NULL,
procedure_code_cpt VARCHAR(10) NOT NULL,
procedure_description VARCHAR(255) NOT NULL,
estimated_duration INT NOT NULL,
actual_start TIMESTAMP NULL,
actual_end TIMESTAMP NULL,
case_status VARCHAR(20) NOT NULL DEFAULT 'scheduled',
pre_op_checklist_complete BOOLEAN NOT NULL DEFAULT FALSE,
consent_verified BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_orcase_duration
CHECK (estimated_duration > 0),
CONSTRAINT chk_orcase_times
CHECK (actual_end IS NULL OR actual_start IS NULL OR actual_end >= actual_start),
CONSTRAINT chk_orcase_status
CHECK (case_status IN ('scheduled','in_progress','completed','cancelled','postponed'))
);
ALTER TABLE or_cases
ADD CONSTRAINT fk_orcase_schedule
FOREIGN KEY (schedule_id) REFERENCES or_schedules(schedule_id) ON DELETE CASCADE,
ADD CONSTRAINT fk_orcase_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id),
ADD CONSTRAINT fk_orcase_encounter
FOREIGN KEY (encounter_id) REFERENCES encounters(encounter_id),
ADD CONSTRAINT fk_orcase_surgeon
FOREIGN KEY (surgeon_id) REFERENCES providers(provider_id),
ADD CONSTRAINT fk_orcase_anesth
FOREIGN KEY (anesthesiologist_id) REFERENCES providers(provider_id);
CREATE INDEX idx_orcase_schedule ON or_cases(schedule_id);
CREATE INDEX idx_orcase_patient ON or_cases(patient_id);
CREATE INDEX idx_orcase_status ON or_cases(case_status);
Terminology Bindings
| Field |
Terminology |
Example Value |
procedure_code_cpt |
CPT |
27447 (Total knee arthroplasty) |
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
case_id |
Procedure |
Procedure.id |
patient_id |
Procedure |
Procedure.subject |
encounter_id |
Procedure |
Procedure.encounter |
procedure_code_cpt |
Procedure |
Procedure.code.coding[system=CPT].code |
procedure_description |
Procedure |
Procedure.code.text |
actual_start |
Procedure |
Procedure.performedPeriod.start |
actual_end |
Procedure |
Procedure.performedPeriod.end |
case_status |
Procedure |
Procedure.status |
12. waitlist_entries
Purpose
Tracks patients waiting for appointments or beds, with priority and resolution.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
entry_id |
BIGINT |
NO |
IDENTITY |
PK |
Waitlist entry |
patient_id |
BIGINT |
NO |
— |
FK → patients.patient_id |
Patient |
waitlist_type |
VARCHAR(20) |
NO |
— |
Local |
APPOINTMENT, BED, OR |
requested_service |
VARCHAR(100) |
NO |
— |
— |
e.g. specialty or procedure |
preferred_provider_id |
BIGINT |
YES |
— |
FK → providers.provider_id |
Preferred provider |
priority |
VARCHAR(20) |
NO |
'routine' |
Local |
urgent, routine |
added_datetime |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Added |
target_date_from |
DATE |
YES |
— |
— |
Earliest acceptable date |
target_date_to |
DATE |
YES |
— |
— |
Latest acceptable date |
status |
VARCHAR(20) |
NO |
'active' |
Local |
active, offered, resolved, cancelled |
offered_count |
INT |
NO |
0 |
≥ 0 |
Number of offers |
resolved_datetime |
TIMESTAMP |
YES |
— |
— |
Resolution time |
resolution_type |
VARCHAR(20) |
YES |
— |
Local |
booked, declined, no_response, cancelled |
notes |
VARCHAR(500) |
YES |
— |
— |
Free text |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Creator |
SQL DDL
SQLCREATE TABLE waitlist_entries (
entry_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
patient_id BIGINT NOT NULL,
waitlist_type VARCHAR(20) NOT NULL,
requested_service VARCHAR(100) NOT NULL,
preferred_provider_id BIGINT NULL,
priority VARCHAR(20) NOT NULL DEFAULT 'routine',
added_datetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
target_date_from DATE NULL,
target_date_to DATE NULL,
status VARCHAR(20) NOT NULL DEFAULT 'active',
offered_count INT NOT NULL DEFAULT 0,
resolved_datetime TIMESTAMP NULL,
resolution_type VARCHAR(20) NULL,
notes VARCHAR(500) NULL,
created_by BIGINT NOT NULL,
CONSTRAINT chk_waitlist_type
CHECK (waitlist_type IN ('APPOINTMENT','BED','OR')),
CONSTRAINT chk_waitlist_priority
CHECK (priority IN ('urgent','routine')),
CONSTRAINT chk_waitlist_status
CHECK (status IN ('active','offered','resolved','cancelled')),
CONSTRAINT chk_waitlist_offered
CHECK (offered_count >= 0)
);
ALTER TABLE waitlist_entries
ADD CONSTRAINT fk_waitlist_patient
FOREIGN KEY (patient_id) REFERENCES patients(patient_id),
ADD CONSTRAINT fk_waitlist_provider
FOREIGN KEY (preferred_provider_id) REFERENCES providers(provider_id),
ADD CONSTRAINT fk_waitlist_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id);
CREATE INDEX idx_waitlist_type_status
ON waitlist_entries(waitlist_type, status, priority, added_datetime);
CREATE INDEX idx_waitlist_patient
ON waitlist_entries(patient_id, status);
Terminology Bindings
Local only.
FHIR Mapping
No direct core resource; can be represented as:
ServiceRequest (for appointment waitlist) with extension waitlistStatus
Appointment with Appointment.status = proposed and extension for priority.
13. provider_schedules
Purpose
Defines provider availability sessions (e.g. Monday 9–13) used to generate slots.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
schedule_id |
BIGINT |
NO |
IDENTITY |
PK |
Provider schedule |
provider_id |
BIGINT |
NO |
— |
FK → providers.provider_id |
Provider |
facility_id |
BIGINT |
NO |
— |
FK → facilities.facility_id |
Facility |
department_id |
BIGINT |
NO |
— |
FK → departments.department_id |
Department |
day_of_week |
INT |
NO |
— |
1–7 |
1=Monday … 7=Sunday |
start_time |
TIME |
NO |
— |
— |
Session start |
end_time |
TIME |
NO |
— |
> start |
Session end |
session_type |
VARCHAR(20) |
NO |
'clinic' |
Local |
clinic, procedure, telehealth |
effective_from |
DATE |
NO |
CURRENT_DATE |
— |
Start date |
effective_to |
DATE |
YES |
— |
≥ from |
End date |
is_recurring |
BOOLEAN |
NO |
TRUE |
— |
Recurring |
max_patients |
INT |
NO |
0 |
≥ 0 |
Capacity |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Updated |
SQL DDL
SQLCREATE TABLE provider_schedules (
schedule_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
provider_id BIGINT NOT NULL,
facility_id BIGINT NOT NULL,
department_id BIGINT NOT NULL,
day_of_week INT NOT NULL,
start_time TIME NOT NULL,
end_time TIME NOT NULL,
session_type VARCHAR(20) NOT NULL DEFAULT 'clinic',
effective_from DATE NOT NULL DEFAULT CURRENT_DATE,
effective_to DATE NULL,
is_recurring BOOLEAN NOT NULL DEFAULT TRUE,
max_patients INT NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_provsched_day
CHECK (day_of_week BETWEEN 1 AND 7),
CONSTRAINT chk_provsched_time
CHECK (end_time > start_time),
CONSTRAINT chk_provsched_dates
CHECK (effective_to IS NULL OR effective_to >= effective_from),
CONSTRAINT chk_provsched_capacity
CHECK (max_patients >= 0)
);
ALTER TABLE provider_schedules
ADD CONSTRAINT fk_provsched_provider
FOREIGN KEY (provider_id) REFERENCES providers(provider_id),
ADD CONSTRAINT fk_provsched_facility
FOREIGN KEY (facility_id) REFERENCES facilities(facility_id),
ADD CONSTRAINT fk_provsched_department
FOREIGN KEY (department_id) REFERENCES departments(department_id);
CREATE INDEX idx_provsched_provider
ON provider_schedules(provider_id, day_of_week, effective_from);
Terminology Bindings
Local only.
FHIR Mapping
| Table Field |
FHIR Resource |
FHIR Path |
schedule_id |
Schedule |
Schedule.id |
provider_id |
Schedule |
Schedule.actor |
facility_id |
Schedule |
Schedule.actor (Org) |
day_of_week |
Schedule |
Schedule.planningHorizon (pattern) |
start_time/end_time |
Schedule |
Schedule.planningHorizon |
14. scheduling_rules
Purpose
Configurable business rules (lead time, overbooking, cancellation policies) applied at facility/department/appointment-type level.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
rule_id |
BIGINT |
NO |
IDENTITY |
PK |
Rule |
rule_type |
VARCHAR(50) |
NO |
— |
Local |
LEAD_TIME, OVERBOOKING, CANCELLATION, etc. |
rule_name |
VARCHAR(100) |
NO |
— |
— |
Human-readable name |
rule_parameters_json |
JSONB |
NO |
'{}' |
— |
Parameters (e.g. { "min_hours_before_booking": 24 }) |
facility_id |
BIGINT |
YES |
— |
FK → facilities.facility_id |
Scope facility |
department_id |
BIGINT |
YES |
— |
FK → departments.department_id |
Scope department |
appointment_type_id |
BIGINT |
YES |
— |
FK → appointment_types.type_id |
Scope appointment type |
is_active |
BOOLEAN |
NO |
TRUE |
— |
Active |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Creator |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Updated |
SQL DDL
SQLCREATE TABLE scheduling_rules (
rule_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
rule_type VARCHAR(50) NOT NULL,
rule_name VARCHAR(100) NOT NULL,
rule_parameters_json JSONB NOT NULL DEFAULT '{}'::jsonb,
facility_id BIGINT NULL,
department_id BIGINT NULL,
appointment_type_id BIGINT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_by BIGINT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE scheduling_rules
ADD CONSTRAINT fk_schrule_facility
FOREIGN KEY (facility_id) REFERENCES facilities(facility_id),
ADD CONSTRAINT fk_schrule_department
FOREIGN KEY (department_id) REFERENCES departments(department_id),
ADD CONSTRAINT fk_schrule_appttype
FOREIGN KEY (appointment_type_id) REFERENCES appointment_types(type_id),
ADD CONSTRAINT fk_schrule_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id);
CREATE INDEX idx_schrule_scope
ON scheduling_rules(facility_id, department_id, appointment_type_id, is_active);
CREATE INDEX idx_schrule_type
ON scheduling_rules(rule_type);
Terminology Bindings
Local only.
FHIR Mapping
Represent as PlanDefinition or Library with extensions; no direct core mapping.
15. scheduling_templates
Purpose
Reusable weekly patterns for provider schedules and slot generation.
Fields
| Field |
Type |
Nullable |
Default |
Constraint |
Description |
template_id |
BIGINT |
NO |
IDENTITY |
PK |
Template |
template_name |
VARCHAR(100) |
NO |
— |
— |
Name |
provider_id |
BIGINT |
YES |
— |
FK → providers.provider_id |
Provider-specific or generic |
department_id |
BIGINT |
YES |
— |
FK → departments.department_id |
Department |
effective_period |
DATERANGE |
NO |
— |
— |
Validity range |
slots_json |
JSONB |
NO |
— |
— |
Definition of slots (day-of-week, start, end, type) |
is_active |
BOOLEAN |
NO |
TRUE |
— |
Active |
created_by |
BIGINT |
NO |
— |
FK → users.user_id |
Creator |
created_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
— |
Created |
updated_at |
TIMESTAMP |
NO |
CURRENT_TIMESTAMP |
ON UPDATE |
Updated |
SQL DDL
SQLCREATE TABLE scheduling_templates (
template_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
template_name VARCHAR(100) NOT NULL,
provider_id BIGINT NULL,
department_id BIGINT NULL,
effective_period DATERANGE NOT NULL,
slots_json JSONB NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_by BIGINT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE scheduling_templates
ADD CONSTRAINT fk_schtemplate_provider
FOREIGN KEY (provider_id) REFERENCES providers(provider_id),
ADD CONSTRAINT fk_schtemplate_department
FOREIGN KEY (department_id) REFERENCES departments(department_id),
ADD CONSTRAINT fk_schtemplate_created_by
FOREIGN KEY (created_by) REFERENCES users(user_id);
CREATE INDEX idx_schtemplate_active
ON scheduling_templates(is_active);
Terminology Bindings
Local only.
FHIR Mapping
No direct mapping; can be represented as Schedule + Slot generation logic.
Data Volume Estimates
Approximate for a 300-bed UAE tertiary hospital.
| Table |
Initial Rows |
Annual Growth |
Notes |
encounters |
0 |
~250,000 |
OPD + ED + IP; 5–10 years retained online |
encounter_details |
0 |
~250,000 |
1:1 with encounters |
appointments |
0 |
~600,000 |
High-volume OPD |
appointment_slots |
~200,000 |
~200,000 |
Rolling 12–18 months |
appointment_types |
~50 |
+5/year |
Low churn |
beds |
~400 |
Stable |
Physical inventory |
bed_assignments |
0 |
~80,000 |
Each admission + transfers |
bed_transfers |
0 |
~20,000 |
Depends on acuity |
or_rooms |
~15 |
Stable |
Physical inventory |
or_schedules |
0 |
~5,000 |
Blocks per OR per day |
or_cases |
0 |
~12,000 |
Surgical volume |
waitlist_entries |
0 |
~50,000 |
Appointments + beds |
provider_schedules |
~500 |
+50/year |
Per provider/site |
scheduling_rules |
~30 |
+5/year |
Configuration |
scheduling_templates |
~200 |
+20/year |
Templates |
Indexes and partitioning (e.g. by year on encounters, appointments) should be considered for installations with >5 years of data.
Data Retention Policy (UAE Context)
Retention must comply with UAE MOH, DOH, DHA regulations and UAE PDPL (Federal Decree-Law No. 45/2021). Policies below are defaults; facility-specific policies may be stricter.
| Table |
Recommended Retention |
Rationale / Notes |
encounters |
Minimum 25 years from last encounter or as per MOH/DOH/DHA guidance |
Core medico-legal record; typically retained long-term. Archive to cold storage after 10 years of inactivity. |
encounter_details |
Same as encounters |
Required for coding, DRG audits, medico-legal review. |
appointments |
10 years |
Supports operational analytics (no-show patterns, access metrics). Older data may be aggregated/anonymised. |
appointment_slots |
5 years |
Operational; can be purged/aggregated once appointment history is stable. |
appointment_types |
Indefinite (with soft-delete) |
Master data; historical versions needed for audit. |
beds |
Indefinite (with soft-delete) |
Physical inventory; do not delete, only inactivate. |
bed_assignments |
Same as encounters |
Part of inpatient care history and bed occupancy audit. |
bed_transfers |
Same as encounters |
Clinical and operational trace of patient movement. |
or_rooms |
Indefinite (with soft-delete) |
Physical inventory; historical references in or_cases. |
or_schedules |
15 years |
Supports OR utilization analysis and medico-legal trace. |
or_cases |
Same as encounters |
Surgical history is core clinical record. |
waitlist_entries |
5–10 years |
Useful for access KPIs; can be anonymised after 5 years. |
provider_schedules |
10 years |
Needed for reconstructing provider workload and medico-legal context. |
scheduling_rules |
10 years |
For audit of historical scheduling decisions. |
scheduling_templates |
10 years |
For audit and analytics. |
PDPL Considerations
- All tables containing patient identifiers (
patient_id) are personal data and must support:
- Data subject access and export (e.g. via FHIR APIs).
- Restriction of processing and erasure requests, subject to healthcare exemptions for medico-legal retention.
- For analytics beyond retention windows, data should be anonymised or pseudonymised (removing
patient_id, provider_id, etc.).
- Access to scheduling and bed data must be role-based (per
roles in module brief) and logged in the central audit trail module.