629 lines
24 KiB
SQL
629 lines
24 KiB
SQL
-- ============================================================================
|
|
-- TABLAS CLÍNICAS - ERP Clínicas
|
|
-- ============================================================================
|
|
-- Módulos: CL-001 (Pacientes), CL-002 (Citas), CL-003 (Expediente)
|
|
-- Versión: 1.0.0
|
|
-- Fecha: 2025-12-09
|
|
-- ============================================================================
|
|
-- PREREQUISITOS:
|
|
-- 1. ERP-Core instalado (auth.tenants, auth.users, core.partners)
|
|
-- 2. Schema clinica creado
|
|
-- ============================================================================
|
|
|
|
-- ============================================================================
|
|
-- TYPES (ENUMs)
|
|
-- ============================================================================
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE clinica.appointment_status AS ENUM (
|
|
'scheduled', 'confirmed', 'in_progress', 'completed', 'cancelled', 'no_show'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE clinica.patient_gender AS ENUM (
|
|
'male', 'female', 'other', 'prefer_not_to_say'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE clinica.blood_type AS ENUM (
|
|
'A+', 'A-', 'B+', 'B-', 'AB+', 'AB-', 'O+', 'O-', 'unknown'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE clinica.consultation_status AS ENUM (
|
|
'draft', 'in_progress', 'completed', 'cancelled'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
-- ============================================================================
|
|
-- CATÁLOGOS BASE
|
|
-- ============================================================================
|
|
|
|
-- Tabla: specialties (Especialidades médicas)
|
|
CREATE TABLE IF NOT EXISTS clinica.specialties (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
consultation_duration INTEGER DEFAULT 30, -- minutos
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT uq_specialties_code UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
-- Tabla: doctors (Médicos - extiende hr.employees)
|
|
CREATE TABLE IF NOT EXISTS clinica.doctors (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
employee_id UUID, -- FK a hr.employees (ERP Core)
|
|
user_id UUID REFERENCES auth.users(id),
|
|
specialty_id UUID NOT NULL REFERENCES clinica.specialties(id),
|
|
license_number VARCHAR(50) NOT NULL, -- Cédula profesional
|
|
license_expiry DATE,
|
|
secondary_specialties UUID[], -- Array de specialty_ids
|
|
consultation_fee DECIMAL(12,2),
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT uq_doctors_license UNIQUE (tenant_id, license_number)
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- PACIENTES (CL-001)
|
|
-- ============================================================================
|
|
|
|
-- Tabla: patients (Pacientes - extiende core.partners)
|
|
CREATE TABLE IF NOT EXISTS clinica.patients (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
partner_id UUID REFERENCES core.partners(id), -- Vinculo a partner
|
|
|
|
-- Identificación
|
|
patient_number VARCHAR(30) NOT NULL,
|
|
first_name VARCHAR(100) NOT NULL,
|
|
last_name VARCHAR(100) NOT NULL,
|
|
middle_name VARCHAR(100),
|
|
|
|
-- Datos personales
|
|
birth_date DATE,
|
|
gender clinica.patient_gender,
|
|
curp VARCHAR(18),
|
|
|
|
-- Contacto
|
|
email VARCHAR(255),
|
|
phone VARCHAR(20),
|
|
mobile VARCHAR(20),
|
|
|
|
-- Dirección
|
|
street VARCHAR(255),
|
|
city VARCHAR(100),
|
|
state VARCHAR(100),
|
|
zip_code VARCHAR(10),
|
|
country VARCHAR(100) DEFAULT 'México',
|
|
|
|
-- Datos médicos básicos
|
|
blood_type clinica.blood_type DEFAULT 'unknown',
|
|
allergies TEXT[],
|
|
chronic_conditions TEXT[],
|
|
|
|
-- Seguro médico
|
|
has_insurance BOOLEAN DEFAULT FALSE,
|
|
insurance_provider VARCHAR(100),
|
|
insurance_policy VARCHAR(50),
|
|
|
|
-- Control
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
last_visit_date DATE,
|
|
|
|
-- Auditoría
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id),
|
|
|
|
CONSTRAINT uq_patients_number UNIQUE (tenant_id, patient_number)
|
|
);
|
|
|
|
-- Tabla: patient_contacts (Contactos de emergencia)
|
|
CREATE TABLE IF NOT EXISTS clinica.patient_contacts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
patient_id UUID NOT NULL REFERENCES clinica.patients(id) ON DELETE CASCADE,
|
|
contact_name VARCHAR(200) NOT NULL,
|
|
relationship VARCHAR(50), -- Parentesco
|
|
phone VARCHAR(20),
|
|
mobile VARCHAR(20),
|
|
email VARCHAR(255),
|
|
is_primary BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
updated_by UUID REFERENCES auth.users(id)
|
|
);
|
|
|
|
-- Tabla: patient_insurance (Información de seguros)
|
|
CREATE TABLE IF NOT EXISTS clinica.patient_insurance (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
patient_id UUID NOT NULL REFERENCES clinica.patients(id) ON DELETE CASCADE,
|
|
insurance_provider VARCHAR(100) NOT NULL,
|
|
policy_number VARCHAR(50) NOT NULL,
|
|
group_number VARCHAR(50),
|
|
holder_name VARCHAR(200),
|
|
holder_relationship VARCHAR(50),
|
|
coverage_type VARCHAR(50),
|
|
valid_from DATE,
|
|
valid_until DATE,
|
|
is_primary BOOLEAN DEFAULT TRUE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
updated_by UUID REFERENCES auth.users(id)
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- CITAS (CL-002)
|
|
-- ============================================================================
|
|
|
|
-- Tabla: appointment_slots (Horarios disponibles)
|
|
CREATE TABLE IF NOT EXISTS clinica.appointment_slots (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
doctor_id UUID NOT NULL REFERENCES clinica.doctors(id),
|
|
day_of_week INTEGER NOT NULL CHECK (day_of_week BETWEEN 0 AND 6), -- 0=Domingo
|
|
start_time TIME NOT NULL,
|
|
end_time TIME NOT NULL,
|
|
slot_duration INTEGER DEFAULT 30, -- minutos
|
|
max_appointments INTEGER DEFAULT 1,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT chk_slot_times CHECK (end_time > start_time)
|
|
);
|
|
|
|
-- Tabla: appointments (Citas médicas)
|
|
CREATE TABLE IF NOT EXISTS clinica.appointments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Referencias
|
|
patient_id UUID NOT NULL REFERENCES clinica.patients(id),
|
|
doctor_id UUID NOT NULL REFERENCES clinica.doctors(id),
|
|
specialty_id UUID REFERENCES clinica.specialties(id),
|
|
|
|
-- Programación
|
|
appointment_date DATE NOT NULL,
|
|
start_time TIME NOT NULL,
|
|
end_time TIME NOT NULL,
|
|
duration INTEGER DEFAULT 30, -- minutos
|
|
|
|
-- Estado
|
|
status clinica.appointment_status NOT NULL DEFAULT 'scheduled',
|
|
|
|
-- Detalles
|
|
reason TEXT, -- Motivo de consulta
|
|
notes TEXT,
|
|
is_first_visit BOOLEAN DEFAULT FALSE,
|
|
is_follow_up BOOLEAN DEFAULT FALSE,
|
|
follow_up_to UUID REFERENCES clinica.appointments(id),
|
|
|
|
-- Recordatorios
|
|
reminder_sent BOOLEAN DEFAULT FALSE,
|
|
reminder_sent_at TIMESTAMPTZ,
|
|
|
|
-- Confirmación
|
|
confirmed_at TIMESTAMPTZ,
|
|
confirmed_by UUID REFERENCES auth.users(id),
|
|
|
|
-- Cancelación
|
|
cancelled_at TIMESTAMPTZ,
|
|
cancelled_by UUID REFERENCES auth.users(id),
|
|
cancellation_reason TEXT,
|
|
|
|
-- Auditoría
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
|
|
CONSTRAINT chk_appointment_times CHECK (end_time > start_time)
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- EXPEDIENTE CLÍNICO (CL-003)
|
|
-- ============================================================================
|
|
|
|
-- Tabla: medical_records (Expediente clínico electrónico)
|
|
-- NOTA: Datos sensibles según NOM-024-SSA3-2012
|
|
CREATE TABLE IF NOT EXISTS clinica.medical_records (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
patient_id UUID NOT NULL REFERENCES clinica.patients(id),
|
|
|
|
-- Número de expediente
|
|
record_number VARCHAR(30) NOT NULL,
|
|
|
|
-- Antecedentes
|
|
family_history TEXT,
|
|
personal_history TEXT,
|
|
surgical_history TEXT,
|
|
|
|
-- Hábitos
|
|
smoking_status VARCHAR(50),
|
|
alcohol_status VARCHAR(50),
|
|
exercise_status VARCHAR(50),
|
|
diet_notes TEXT,
|
|
|
|
-- Gineco-obstétricos (si aplica)
|
|
obstetric_history JSONB,
|
|
|
|
-- Notas generales
|
|
notes TEXT,
|
|
|
|
-- Control de acceso
|
|
is_confidential BOOLEAN DEFAULT TRUE,
|
|
access_restricted BOOLEAN DEFAULT FALSE,
|
|
|
|
-- Auditoría
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
|
|
CONSTRAINT uq_medical_records_number UNIQUE (tenant_id, record_number),
|
|
CONSTRAINT uq_medical_records_patient UNIQUE (patient_id)
|
|
);
|
|
|
|
-- Tabla: consultations (Consultas realizadas)
|
|
CREATE TABLE IF NOT EXISTS clinica.consultations (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Referencias
|
|
medical_record_id UUID NOT NULL REFERENCES clinica.medical_records(id),
|
|
appointment_id UUID REFERENCES clinica.appointments(id),
|
|
doctor_id UUID NOT NULL REFERENCES clinica.doctors(id),
|
|
|
|
-- Fecha/hora
|
|
consultation_date DATE NOT NULL,
|
|
start_time TIMESTAMPTZ,
|
|
end_time TIMESTAMPTZ,
|
|
|
|
-- Estado
|
|
status clinica.consultation_status DEFAULT 'draft',
|
|
|
|
-- Motivo de consulta
|
|
chief_complaint TEXT NOT NULL, -- Motivo principal
|
|
present_illness TEXT, -- Padecimiento actual
|
|
|
|
-- Exploración física
|
|
physical_exam JSONB, -- Estructurado por sistemas
|
|
|
|
-- Plan
|
|
treatment_plan TEXT,
|
|
follow_up_instructions TEXT,
|
|
next_appointment_days INTEGER,
|
|
|
|
-- Notas
|
|
notes TEXT,
|
|
private_notes TEXT, -- Solo visible para el médico
|
|
|
|
-- Auditoría
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
updated_by UUID REFERENCES auth.users(id)
|
|
);
|
|
|
|
-- Tabla: vital_signs (Signos vitales)
|
|
CREATE TABLE IF NOT EXISTS clinica.vital_signs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
consultation_id UUID NOT NULL REFERENCES clinica.consultations(id) ON DELETE CASCADE,
|
|
|
|
-- Signos vitales
|
|
weight_kg DECIMAL(5,2),
|
|
height_cm DECIMAL(5,2),
|
|
bmi DECIMAL(4,2) GENERATED ALWAYS AS (
|
|
CASE WHEN height_cm > 0 THEN weight_kg / ((height_cm/100) * (height_cm/100)) END
|
|
) STORED,
|
|
temperature_c DECIMAL(4,2),
|
|
blood_pressure_systolic INTEGER,
|
|
blood_pressure_diastolic INTEGER,
|
|
heart_rate INTEGER, -- latidos por minuto
|
|
respiratory_rate INTEGER, -- respiraciones por minuto
|
|
oxygen_saturation INTEGER, -- porcentaje
|
|
|
|
-- Fecha/hora de medición
|
|
measured_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
measured_by UUID REFERENCES auth.users(id),
|
|
|
|
notes TEXT,
|
|
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id)
|
|
);
|
|
|
|
-- Tabla: diagnoses (Diagnósticos - CIE-10)
|
|
CREATE TABLE IF NOT EXISTS clinica.diagnoses (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
consultation_id UUID NOT NULL REFERENCES clinica.consultations(id) ON DELETE CASCADE,
|
|
|
|
-- Código CIE-10
|
|
icd10_code VARCHAR(10) NOT NULL,
|
|
icd10_description VARCHAR(255),
|
|
|
|
-- Tipo
|
|
diagnosis_type VARCHAR(20) NOT NULL DEFAULT 'primary', -- primary, secondary, differential
|
|
|
|
-- Detalles
|
|
notes TEXT,
|
|
is_chronic BOOLEAN DEFAULT FALSE,
|
|
onset_date DATE,
|
|
|
|
-- Orden
|
|
sequence INTEGER DEFAULT 1,
|
|
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id)
|
|
);
|
|
|
|
-- Tabla: prescriptions (Recetas médicas)
|
|
CREATE TABLE IF NOT EXISTS clinica.prescriptions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
consultation_id UUID NOT NULL REFERENCES clinica.consultations(id),
|
|
|
|
-- Número de receta
|
|
prescription_number VARCHAR(30) NOT NULL,
|
|
prescription_date DATE NOT NULL DEFAULT CURRENT_DATE,
|
|
|
|
-- Médico
|
|
doctor_id UUID NOT NULL REFERENCES clinica.doctors(id),
|
|
|
|
-- Instrucciones generales
|
|
general_instructions TEXT,
|
|
|
|
-- Vigencia
|
|
valid_until DATE,
|
|
|
|
-- Estado
|
|
is_printed BOOLEAN DEFAULT FALSE,
|
|
printed_at TIMESTAMPTZ,
|
|
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
|
|
CONSTRAINT uq_prescriptions_number UNIQUE (tenant_id, prescription_number)
|
|
);
|
|
|
|
-- Tabla: prescription_items (Líneas de receta)
|
|
CREATE TABLE IF NOT EXISTS clinica.prescription_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
prescription_id UUID NOT NULL REFERENCES clinica.prescriptions(id) ON DELETE CASCADE,
|
|
|
|
-- Medicamento
|
|
product_id UUID, -- FK a inventory.products (ERP Core)
|
|
medication_name VARCHAR(255) NOT NULL,
|
|
presentation VARCHAR(100), -- Tabletas, jarabe, etc.
|
|
|
|
-- Dosificación
|
|
dosage VARCHAR(100) NOT NULL, -- "1 tableta"
|
|
frequency VARCHAR(100) NOT NULL, -- "cada 8 horas"
|
|
duration VARCHAR(100), -- "por 7 días"
|
|
quantity INTEGER, -- Cantidad a surtir
|
|
|
|
-- Instrucciones
|
|
instructions TEXT,
|
|
|
|
-- Orden
|
|
sequence INTEGER DEFAULT 1,
|
|
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id)
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- ÍNDICES
|
|
-- ============================================================================
|
|
|
|
-- Specialties
|
|
CREATE INDEX IF NOT EXISTS idx_specialties_tenant ON clinica.specialties(tenant_id);
|
|
|
|
-- Doctors
|
|
CREATE INDEX IF NOT EXISTS idx_doctors_tenant ON clinica.doctors(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_doctors_specialty ON clinica.doctors(specialty_id);
|
|
CREATE INDEX IF NOT EXISTS idx_doctors_user ON clinica.doctors(user_id);
|
|
|
|
-- Patients
|
|
CREATE INDEX IF NOT EXISTS idx_patients_tenant ON clinica.patients(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_patients_partner ON clinica.patients(partner_id);
|
|
CREATE INDEX IF NOT EXISTS idx_patients_name ON clinica.patients(last_name, first_name);
|
|
CREATE INDEX IF NOT EXISTS idx_patients_curp ON clinica.patients(curp);
|
|
|
|
-- Patient contacts
|
|
CREATE INDEX IF NOT EXISTS idx_patient_contacts_tenant ON clinica.patient_contacts(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_patient_contacts_patient ON clinica.patient_contacts(patient_id);
|
|
|
|
-- Patient insurance
|
|
CREATE INDEX IF NOT EXISTS idx_patient_insurance_tenant ON clinica.patient_insurance(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_patient_insurance_patient ON clinica.patient_insurance(patient_id);
|
|
|
|
-- Appointment slots
|
|
CREATE INDEX IF NOT EXISTS idx_appointment_slots_tenant ON clinica.appointment_slots(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_appointment_slots_doctor ON clinica.appointment_slots(doctor_id);
|
|
|
|
-- Appointments
|
|
CREATE INDEX IF NOT EXISTS idx_appointments_tenant ON clinica.appointments(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_appointments_patient ON clinica.appointments(patient_id);
|
|
CREATE INDEX IF NOT EXISTS idx_appointments_doctor ON clinica.appointments(doctor_id);
|
|
CREATE INDEX IF NOT EXISTS idx_appointments_date ON clinica.appointments(appointment_date);
|
|
CREATE INDEX IF NOT EXISTS idx_appointments_status ON clinica.appointments(status);
|
|
|
|
-- Medical records
|
|
CREATE INDEX IF NOT EXISTS idx_medical_records_tenant ON clinica.medical_records(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_medical_records_patient ON clinica.medical_records(patient_id);
|
|
|
|
-- Consultations
|
|
CREATE INDEX IF NOT EXISTS idx_consultations_tenant ON clinica.consultations(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_consultations_record ON clinica.consultations(medical_record_id);
|
|
CREATE INDEX IF NOT EXISTS idx_consultations_doctor ON clinica.consultations(doctor_id);
|
|
CREATE INDEX IF NOT EXISTS idx_consultations_date ON clinica.consultations(consultation_date);
|
|
|
|
-- Vital signs
|
|
CREATE INDEX IF NOT EXISTS idx_vital_signs_tenant ON clinica.vital_signs(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_vital_signs_consultation ON clinica.vital_signs(consultation_id);
|
|
|
|
-- Diagnoses
|
|
CREATE INDEX IF NOT EXISTS idx_diagnoses_tenant ON clinica.diagnoses(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_diagnoses_consultation ON clinica.diagnoses(consultation_id);
|
|
CREATE INDEX IF NOT EXISTS idx_diagnoses_icd10 ON clinica.diagnoses(icd10_code);
|
|
|
|
-- Prescriptions
|
|
CREATE INDEX IF NOT EXISTS idx_prescriptions_tenant ON clinica.prescriptions(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_prescriptions_consultation ON clinica.prescriptions(consultation_id);
|
|
|
|
-- Prescription items
|
|
CREATE INDEX IF NOT EXISTS idx_prescription_items_tenant ON clinica.prescription_items(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_prescription_items_prescription ON clinica.prescription_items(prescription_id);
|
|
|
|
-- ============================================================================
|
|
-- ROW LEVEL SECURITY
|
|
-- ============================================================================
|
|
|
|
ALTER TABLE clinica.specialties ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.doctors ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.patients ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.patient_contacts ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.patient_insurance ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.appointment_slots ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.appointments ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.medical_records ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.consultations ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.vital_signs ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.diagnoses ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.prescriptions ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE clinica.prescription_items ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Políticas de aislamiento por tenant
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_specialties ON clinica.specialties;
|
|
CREATE POLICY tenant_isolation_specialties ON clinica.specialties
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_doctors ON clinica.doctors;
|
|
CREATE POLICY tenant_isolation_doctors ON clinica.doctors
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_patients ON clinica.patients;
|
|
CREATE POLICY tenant_isolation_patients ON clinica.patients
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_patient_contacts ON clinica.patient_contacts;
|
|
CREATE POLICY tenant_isolation_patient_contacts ON clinica.patient_contacts
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_patient_insurance ON clinica.patient_insurance;
|
|
CREATE POLICY tenant_isolation_patient_insurance ON clinica.patient_insurance
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_appointment_slots ON clinica.appointment_slots;
|
|
CREATE POLICY tenant_isolation_appointment_slots ON clinica.appointment_slots
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_appointments ON clinica.appointments;
|
|
CREATE POLICY tenant_isolation_appointments ON clinica.appointments
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_medical_records ON clinica.medical_records;
|
|
CREATE POLICY tenant_isolation_medical_records ON clinica.medical_records
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_consultations ON clinica.consultations;
|
|
CREATE POLICY tenant_isolation_consultations ON clinica.consultations
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_vital_signs ON clinica.vital_signs;
|
|
CREATE POLICY tenant_isolation_vital_signs ON clinica.vital_signs
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_diagnoses ON clinica.diagnoses;
|
|
CREATE POLICY tenant_isolation_diagnoses ON clinica.diagnoses
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_prescriptions ON clinica.prescriptions;
|
|
CREATE POLICY tenant_isolation_prescriptions ON clinica.prescriptions
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_prescription_items ON clinica.prescription_items;
|
|
CREATE POLICY tenant_isolation_prescription_items ON clinica.prescription_items
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
-- ============================================================================
|
|
-- COMENTARIOS
|
|
-- ============================================================================
|
|
|
|
COMMENT ON TABLE clinica.specialties IS 'Catálogo de especialidades médicas';
|
|
COMMENT ON TABLE clinica.doctors IS 'Médicos y especialistas - extiende hr.employees';
|
|
COMMENT ON TABLE clinica.patients IS 'Registro de pacientes - extiende core.partners';
|
|
COMMENT ON TABLE clinica.patient_contacts IS 'Contactos de emergencia del paciente';
|
|
COMMENT ON TABLE clinica.patient_insurance IS 'Información de seguros médicos';
|
|
COMMENT ON TABLE clinica.appointment_slots IS 'Horarios disponibles por médico';
|
|
COMMENT ON TABLE clinica.appointments IS 'Citas médicas programadas';
|
|
COMMENT ON TABLE clinica.medical_records IS 'Expediente clínico electrónico (NOM-024-SSA3)';
|
|
COMMENT ON TABLE clinica.consultations IS 'Consultas médicas realizadas';
|
|
COMMENT ON TABLE clinica.vital_signs IS 'Signos vitales del paciente';
|
|
COMMENT ON TABLE clinica.diagnoses IS 'Diagnósticos según CIE-10';
|
|
COMMENT ON TABLE clinica.prescriptions IS 'Recetas médicas';
|
|
COMMENT ON TABLE clinica.prescription_items IS 'Medicamentos en receta';
|
|
|
|
-- ============================================================================
|
|
-- FIN TABLAS CLÍNICAS
|
|
-- Total: 13 tablas, 4 ENUMs
|
|
-- ============================================================================
|