Migración desde clinica-dental/database - Estándar multi-repo v2

Código migrado del proyecto monorepo original

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rckrdmrd 2026-01-16 08:22:51 -06:00
parent 4511fc6a5a
commit 8c61a7e449
2 changed files with 603 additions and 0 deletions

View File

@ -0,0 +1,502 @@
-- ============================================================================
-- DENTAL SCHEMA - Especialización de ERP-Clínicas
-- Clínica Dental
-- ============================================================================
-- Fecha: 2026-01-04
-- Versión: 1.0
-- Hereda de: erp-clinicas FASE-8
-- ============================================================================
-- Schema
CREATE SCHEMA IF NOT EXISTS dental;
-- ============================================================================
-- ENUMS
-- ============================================================================
DO $$ BEGIN
CREATE TYPE dental.estado_pieza AS ENUM (
'sano', 'caries', 'obturacion', 'endodoncia', 'corona',
'puente', 'implante', 'ausente', 'extraccion_indicada',
'diente_temporal', 'fractura', 'movilidad'
);
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
CREATE TYPE dental.cara_dental AS ENUM (
'mesial', 'distal', 'oclusal', 'incisal',
'vestibular', 'bucal', 'lingual', 'palatino'
);
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
CREATE TYPE dental.estado_tratamiento AS ENUM (
'pendiente', 'en_proceso', 'completado', 'cancelado'
);
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
CREATE TYPE dental.tipo_ortodoncia AS ENUM (
'brackets_metalicos', 'brackets_esteticos', 'brackets_linguales',
'alineadores', 'removible', 'retenedor'
);
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
-- ============================================================================
-- CATÁLOGOS
-- ============================================================================
-- Piezas dentales
CREATE TABLE IF NOT EXISTS dental.piezas_dentales (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
numero VARCHAR(10) NOT NULL UNIQUE, -- '11', '12', ... '48', '51'...'85'
nombre VARCHAR(50) NOT NULL,
cuadrante INTEGER NOT NULL CHECK (cuadrante BETWEEN 1 AND 8),
es_temporal BOOLEAN DEFAULT false,
descripcion TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE dental.piezas_dentales IS 'Catálogo de piezas dentales (nomenclatura FDI)';
-- Tratamientos dentales
CREATE TABLE IF NOT EXISTS dental.tratamientos_catalogo (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
codigo VARCHAR(20) NOT NULL,
nombre VARCHAR(100) NOT NULL,
categoria VARCHAR(50), -- 'prevencion', 'restauracion', 'endodoncia', etc.
descripcion TEXT,
duracion_minutos INTEGER DEFAULT 30,
precio_base NUMERIC(10,2),
requiere_rx BOOLEAN DEFAULT false,
requiere_anestesia BOOLEAN DEFAULT false,
active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT uq_tratamientos_tenant_codigo UNIQUE(tenant_id, codigo)
);
COMMENT ON TABLE dental.tratamientos_catalogo IS 'Catálogo de tratamientos dentales';
-- ============================================================================
-- TABLAS PRINCIPALES
-- ============================================================================
-- Odontograma
CREATE TABLE IF NOT EXISTS dental.odontogramas (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
patient_id UUID NOT NULL, -- Referencia a clinica.patients
fecha_creacion DATE NOT NULL DEFAULT CURRENT_DATE,
fecha_actualizacion DATE,
notas TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE dental.odontogramas IS 'Odontogramas de pacientes';
-- Estado de piezas dentales por odontograma
CREATE TABLE IF NOT EXISTS dental.odontograma_piezas (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
odontograma_id UUID NOT NULL REFERENCES dental.odontogramas(id) ON DELETE CASCADE,
pieza_id UUID NOT NULL REFERENCES dental.piezas_dentales(id),
-- Estado general
estado dental.estado_pieza DEFAULT 'sano',
-- Estados por cara (JSONB para flexibilidad)
caras_afectadas JSONB, -- {"mesial": "caries", "oclusal": "obturacion"}
-- Notas
observaciones TEXT,
-- Control
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT uq_odontograma_pieza UNIQUE(odontograma_id, pieza_id)
);
COMMENT ON TABLE dental.odontograma_piezas IS 'Estado de cada pieza dental en el odontograma';
-- Tratamientos de paciente
CREATE TABLE IF NOT EXISTS dental.tratamientos_paciente (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
patient_id UUID NOT NULL,
odontograma_id UUID REFERENCES dental.odontogramas(id),
tratamiento_id UUID REFERENCES dental.tratamientos_catalogo(id),
odontologo_id UUID, -- Referencia a clinica.doctors
consultation_id UUID, -- Referencia a clinica.consultations
-- Pieza(s) tratada(s)
pieza_id UUID REFERENCES dental.piezas_dentales(id),
caras_tratadas dental.cara_dental[],
-- Datos del tratamiento
fecha_inicio DATE NOT NULL DEFAULT CURRENT_DATE,
fecha_fin DATE,
estado dental.estado_tratamiento DEFAULT 'pendiente',
-- Costo
precio NUMERIC(10,2),
descuento NUMERIC(5,2) DEFAULT 0,
precio_final NUMERIC(10,2),
-- Notas
notas TEXT,
-- Control
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE dental.tratamientos_paciente IS 'Tratamientos realizados a pacientes';
-- Ortodoncia
CREATE TABLE IF NOT EXISTS dental.ortodoncia (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
patient_id UUID NOT NULL,
odontologo_id UUID,
-- Tipo de tratamiento
tipo dental.tipo_ortodoncia NOT NULL,
marca VARCHAR(100),
-- Fechas
fecha_inicio DATE NOT NULL,
fecha_estimada_fin DATE,
fecha_real_fin DATE,
-- Estado
estado dental.estado_tratamiento DEFAULT 'en_proceso',
meses_estimados INTEGER,
-- Costo
costo_total NUMERIC(10,2),
enganche NUMERIC(10,2),
mensualidad NUMERIC(10,2),
-- Notas
notas TEXT,
-- Control
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE dental.ortodoncia IS 'Tratamientos de ortodoncia';
-- Citas de ortodoncia (seguimiento)
CREATE TABLE IF NOT EXISTS dental.ortodoncia_citas (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
ortodoncia_id UUID NOT NULL REFERENCES dental.ortodoncia(id) ON DELETE CASCADE,
appointment_id UUID, -- Referencia a clinica.appointments
-- Datos de la cita
fecha DATE NOT NULL,
numero_cita INTEGER,
-- Procedimiento
procedimiento TEXT,
arco_superior VARCHAR(50),
arco_inferior VARCHAR(50),
ligas VARCHAR(50),
-- Observaciones
observaciones TEXT,
proxima_cita DATE,
-- Control
created_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE dental.ortodoncia_citas IS 'Citas de seguimiento de ortodoncia';
-- Prótesis
CREATE TABLE IF NOT EXISTS dental.protesis (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
patient_id UUID NOT NULL,
odontologo_id UUID,
-- Tipo
tipo VARCHAR(50) NOT NULL, -- 'corona', 'puente', 'parcial', 'total', 'implante'
-- Piezas involucradas
piezas_involucradas TEXT[], -- ['11', '12', '13']
-- Laboratorio
laboratorio_id UUID, -- Referencia a proveedor
fecha_envio_lab DATE,
fecha_recepcion_lab DATE,
-- Material
material VARCHAR(100),
color VARCHAR(50),
-- Estado
estado dental.estado_tratamiento DEFAULT 'en_proceso',
fecha_colocacion DATE,
-- Garantía
tiene_garantia BOOLEAN DEFAULT false,
meses_garantia INTEGER,
-- Costo
costo_laboratorio NUMERIC(10,2),
precio_paciente NUMERIC(10,2),
-- Notas
notas TEXT,
-- Control
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE dental.protesis IS 'Registro de prótesis dentales';
-- Radiografías
CREATE TABLE IF NOT EXISTS dental.radiografias (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
patient_id UUID NOT NULL,
consultation_id UUID,
-- Tipo
tipo VARCHAR(50) NOT NULL, -- 'periapical', 'panoramica', 'cefalometrica', 'oclusal'
pieza_id UUID REFERENCES dental.piezas_dentales(id),
-- Archivo
fecha DATE NOT NULL DEFAULT CURRENT_DATE,
url_imagen VARCHAR(255),
-- Interpretación
interpretacion TEXT,
-- Control
created_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE dental.radiografias IS 'Registro de radiografías dentales';
-- Presupuestos
CREATE TABLE IF NOT EXISTS dental.presupuestos (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
patient_id UUID NOT NULL,
odontologo_id UUID,
-- Datos
numero VARCHAR(20),
fecha DATE NOT NULL DEFAULT CURRENT_DATE,
fecha_vencimiento DATE,
-- Estado
estado VARCHAR(20) DEFAULT 'pendiente', -- 'pendiente', 'aprobado', 'rechazado', 'vencido'
-- Totales
subtotal NUMERIC(12,2) DEFAULT 0,
descuento_porcentaje NUMERIC(5,2) DEFAULT 0,
descuento_monto NUMERIC(12,2) DEFAULT 0,
total NUMERIC(12,2) DEFAULT 0,
-- Plan de pago
requiere_financiamiento BOOLEAN DEFAULT false,
enganche NUMERIC(12,2),
numero_pagos INTEGER,
monto_pago NUMERIC(12,2),
-- Notas
notas TEXT,
-- Control
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE dental.presupuestos IS 'Presupuestos de tratamiento';
-- Líneas de presupuesto
CREATE TABLE IF NOT EXISTS dental.presupuesto_lineas (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
presupuesto_id UUID NOT NULL REFERENCES dental.presupuestos(id) ON DELETE CASCADE,
tratamiento_id UUID REFERENCES dental.tratamientos_catalogo(id),
-- Pieza
pieza_id UUID REFERENCES dental.piezas_dentales(id),
descripcion TEXT,
-- Cantidades
cantidad INTEGER DEFAULT 1,
precio_unitario NUMERIC(10,2),
descuento NUMERIC(5,2) DEFAULT 0,
subtotal NUMERIC(10,2),
-- Secuencia
sequence INTEGER DEFAULT 10,
-- Control
created_at TIMESTAMPTZ DEFAULT NOW()
);
COMMENT ON TABLE dental.presupuesto_lineas IS 'Líneas de presupuesto';
-- ============================================================================
-- EXTENSIONES A TABLAS DE ERP-CLINICAS
-- ============================================================================
DO $$
BEGIN
-- Extensión a clinica.patients
IF EXISTS (SELECT 1 FROM information_schema.tables
WHERE table_schema = 'clinica' AND table_name = 'patients') THEN
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_schema = 'clinica' AND table_name = 'patients'
AND column_name = 'odontograma_activo_id') THEN
ALTER TABLE clinica.patients ADD COLUMN odontograma_activo_id UUID;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_schema = 'clinica' AND table_name = 'patients'
AND column_name = 'tiene_ortodoncia') THEN
ALTER TABLE clinica.patients ADD COLUMN tiene_ortodoncia BOOLEAN DEFAULT false;
END IF;
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_schema = 'clinica' AND table_name = 'patients'
AND column_name = 'tiene_protesis') THEN
ALTER TABLE clinica.patients ADD COLUMN tiene_protesis BOOLEAN DEFAULT false;
END IF;
END IF;
END $$;
-- ============================================================================
-- SEED: Piezas dentales (catálogo global)
-- ============================================================================
INSERT INTO dental.piezas_dentales (numero, nombre, cuadrante, es_temporal) VALUES
-- Cuadrante 1 - Superior derecho (permanentes)
('18', 'Tercer molar superior derecho', 1, false),
('17', 'Segundo molar superior derecho', 1, false),
('16', 'Primer molar superior derecho', 1, false),
('15', 'Segundo premolar superior derecho', 1, false),
('14', 'Primer premolar superior derecho', 1, false),
('13', 'Canino superior derecho', 1, false),
('12', 'Incisivo lateral superior derecho', 1, false),
('11', 'Incisivo central superior derecho', 1, false),
-- Cuadrante 2 - Superior izquierdo (permanentes)
('21', 'Incisivo central superior izquierdo', 2, false),
('22', 'Incisivo lateral superior izquierdo', 2, false),
('23', 'Canino superior izquierdo', 2, false),
('24', 'Primer premolar superior izquierdo', 2, false),
('25', 'Segundo premolar superior izquierdo', 2, false),
('26', 'Primer molar superior izquierdo', 2, false),
('27', 'Segundo molar superior izquierdo', 2, false),
('28', 'Tercer molar superior izquierdo', 2, false),
-- Cuadrante 3 - Inferior izquierdo (permanentes)
('31', 'Incisivo central inferior izquierdo', 3, false),
('32', 'Incisivo lateral inferior izquierdo', 3, false),
('33', 'Canino inferior izquierdo', 3, false),
('34', 'Primer premolar inferior izquierdo', 3, false),
('35', 'Segundo premolar inferior izquierdo', 3, false),
('36', 'Primer molar inferior izquierdo', 3, false),
('37', 'Segundo molar inferior izquierdo', 3, false),
('38', 'Tercer molar inferior izquierdo', 3, false),
-- Cuadrante 4 - Inferior derecho (permanentes)
('41', 'Incisivo central inferior derecho', 4, false),
('42', 'Incisivo lateral inferior derecho', 4, false),
('43', 'Canino inferior derecho', 4, false),
('44', 'Primer premolar inferior derecho', 4, false),
('45', 'Segundo premolar inferior derecho', 4, false),
('46', 'Primer molar inferior derecho', 4, false),
('47', 'Segundo molar inferior derecho', 4, false),
('48', 'Tercer molar inferior derecho', 4, false),
-- Cuadrante 5 - Superior derecho (temporales)
('55', 'Segundo molar temporal superior derecho', 5, true),
('54', 'Primer molar temporal superior derecho', 5, true),
('53', 'Canino temporal superior derecho', 5, true),
('52', 'Incisivo lateral temporal superior derecho', 5, true),
('51', 'Incisivo central temporal superior derecho', 5, true),
-- Cuadrante 6 - Superior izquierdo (temporales)
('61', 'Incisivo central temporal superior izquierdo', 6, true),
('62', 'Incisivo lateral temporal superior izquierdo', 6, true),
('63', 'Canino temporal superior izquierdo', 6, true),
('64', 'Primer molar temporal superior izquierdo', 6, true),
('65', 'Segundo molar temporal superior izquierdo', 6, true),
-- Cuadrante 7 - Inferior izquierdo (temporales)
('71', 'Incisivo central temporal inferior izquierdo', 7, true),
('72', 'Incisivo lateral temporal inferior izquierdo', 7, true),
('73', 'Canino temporal inferior izquierdo', 7, true),
('74', 'Primer molar temporal inferior izquierdo', 7, true),
('75', 'Segundo molar temporal inferior izquierdo', 7, true),
-- Cuadrante 8 - Inferior derecho (temporales)
('81', 'Incisivo central temporal inferior derecho', 8, true),
('82', 'Incisivo lateral temporal inferior derecho', 8, true),
('83', 'Canino temporal inferior derecho', 8, true),
('84', 'Primer molar temporal inferior derecho', 8, true),
('85', 'Segundo molar temporal inferior derecho', 8, true)
ON CONFLICT (numero) DO NOTHING;
-- ============================================================================
-- ÍNDICES
-- ============================================================================
CREATE INDEX IF NOT EXISTS idx_tratamientos_catalogo_tenant ON dental.tratamientos_catalogo(tenant_id);
CREATE INDEX IF NOT EXISTS idx_tratamientos_catalogo_categoria ON dental.tratamientos_catalogo(tenant_id, categoria);
CREATE INDEX IF NOT EXISTS idx_odontogramas_tenant ON dental.odontogramas(tenant_id);
CREATE INDEX IF NOT EXISTS idx_odontogramas_patient ON dental.odontogramas(patient_id);
CREATE INDEX IF NOT EXISTS idx_odontograma_piezas_odontograma ON dental.odontograma_piezas(odontograma_id);
CREATE INDEX IF NOT EXISTS idx_odontograma_piezas_pieza ON dental.odontograma_piezas(pieza_id);
CREATE INDEX IF NOT EXISTS idx_tratamientos_paciente_tenant ON dental.tratamientos_paciente(tenant_id);
CREATE INDEX IF NOT EXISTS idx_tratamientos_paciente_patient ON dental.tratamientos_paciente(patient_id);
CREATE INDEX IF NOT EXISTS idx_tratamientos_paciente_estado ON dental.tratamientos_paciente(tenant_id, estado);
CREATE INDEX IF NOT EXISTS idx_ortodoncia_tenant ON dental.ortodoncia(tenant_id);
CREATE INDEX IF NOT EXISTS idx_ortodoncia_patient ON dental.ortodoncia(patient_id);
CREATE INDEX IF NOT EXISTS idx_ortodoncia_estado ON dental.ortodoncia(tenant_id, estado);
CREATE INDEX IF NOT EXISTS idx_ortodoncia_citas_ortodoncia ON dental.ortodoncia_citas(ortodoncia_id);
CREATE INDEX IF NOT EXISTS idx_protesis_tenant ON dental.protesis(tenant_id);
CREATE INDEX IF NOT EXISTS idx_protesis_patient ON dental.protesis(patient_id);
CREATE INDEX IF NOT EXISTS idx_radiografias_tenant ON dental.radiografias(tenant_id);
CREATE INDEX IF NOT EXISTS idx_radiografias_patient ON dental.radiografias(patient_id);
CREATE INDEX IF NOT EXISTS idx_presupuestos_tenant ON dental.presupuestos(tenant_id);
CREATE INDEX IF NOT EXISTS idx_presupuestos_patient ON dental.presupuestos(patient_id);
CREATE INDEX IF NOT EXISTS idx_presupuestos_estado ON dental.presupuestos(tenant_id, estado);
CREATE INDEX IF NOT EXISTS idx_presupuesto_lineas_presupuesto ON dental.presupuesto_lineas(presupuesto_id);
-- ============================================================================
-- RLS
-- ============================================================================
ALTER TABLE dental.tratamientos_catalogo ENABLE ROW LEVEL SECURITY;
ALTER TABLE dental.odontogramas ENABLE ROW LEVEL SECURITY;
ALTER TABLE dental.odontograma_piezas ENABLE ROW LEVEL SECURITY;
ALTER TABLE dental.tratamientos_paciente ENABLE ROW LEVEL SECURITY;
ALTER TABLE dental.ortodoncia ENABLE ROW LEVEL SECURITY;
ALTER TABLE dental.ortodoncia_citas ENABLE ROW LEVEL SECURITY;
ALTER TABLE dental.protesis ENABLE ROW LEVEL SECURITY;
ALTER TABLE dental.radiografias ENABLE ROW LEVEL SECURITY;
ALTER TABLE dental.presupuestos ENABLE ROW LEVEL SECURITY;
ALTER TABLE dental.presupuesto_lineas ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS tenant_isolation_tratamientos_cat ON dental.tratamientos_catalogo;
CREATE POLICY tenant_isolation_tratamientos_cat ON dental.tratamientos_catalogo
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_odontogramas ON dental.odontogramas;
CREATE POLICY tenant_isolation_odontogramas ON dental.odontogramas
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_odontograma_piezas ON dental.odontograma_piezas;
CREATE POLICY tenant_isolation_odontograma_piezas ON dental.odontograma_piezas
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_tratamientos_pac ON dental.tratamientos_paciente;
CREATE POLICY tenant_isolation_tratamientos_pac ON dental.tratamientos_paciente
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_ortodoncia ON dental.ortodoncia;
CREATE POLICY tenant_isolation_ortodoncia ON dental.ortodoncia
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_ortodoncia_citas ON dental.ortodoncia_citas;
CREATE POLICY tenant_isolation_ortodoncia_citas ON dental.ortodoncia_citas
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_protesis ON dental.protesis;
CREATE POLICY tenant_isolation_protesis ON dental.protesis
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_radiografias ON dental.radiografias;
CREATE POLICY tenant_isolation_radiografias ON dental.radiografias
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_presupuestos ON dental.presupuestos;
CREATE POLICY tenant_isolation_presupuestos ON dental.presupuestos
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_presupuesto_lineas ON dental.presupuesto_lineas;
CREATE POLICY tenant_isolation_presupuesto_lineas ON dental.presupuesto_lineas
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
-- ============================================================================
-- FIN DENTAL SCHEMA
-- ============================================================================

View File

@ -0,0 +1,101 @@
-- ============================================================================
-- SEED DATA: Catálogos de Clínica Dental
-- Especialización de ERP-Clínicas
-- ============================================================================
-- NOTA: Ejecutar después de SET app.current_tenant_id = 'UUID-DEL-TENANT';
-- ============================================================================
-- Tratamientos dentales
INSERT INTO dental.tratamientos_catalogo (tenant_id, codigo, nombre, categoria, duracion_minutos, precio_base, requiere_rx, requiere_anestesia)
SELECT current_setting('app.current_tenant_id', true)::UUID, codigo, nombre, categoria, duracion, precio, rx, anestesia
FROM (VALUES
-- Prevención
('PREV-001', 'Limpieza dental básica', 'prevencion', 45, 600.00, false, false),
('PREV-002', 'Limpieza dental profunda', 'prevencion', 60, 1200.00, false, true),
('PREV-003', 'Aplicación de flúor', 'prevencion', 15, 300.00, false, false),
('PREV-004', 'Sellador de fosetas', 'prevencion', 20, 400.00, false, false),
-- Restauración
('REST-001', 'Resina simple (1 cara)', 'restauracion', 30, 800.00, true, true),
('REST-002', 'Resina compuesta (2 caras)', 'restauracion', 45, 1000.00, true, true),
('REST-003', 'Resina compleja (3+ caras)', 'restauracion', 60, 1300.00, true, true),
('REST-004', 'Incrustación de resina', 'restauracion', 90, 2500.00, true, true),
('REST-005', 'Incrustación de porcelana', 'restauracion', 90, 4000.00, true, true),
-- Endodoncia
('ENDO-001', 'Endodoncia unirradicular', 'endodoncia', 60, 3000.00, true, true),
('ENDO-002', 'Endodoncia birradicular', 'endodoncia', 90, 4000.00, true, true),
('ENDO-003', 'Endodoncia multirradicular', 'endodoncia', 120, 5000.00, true, true),
('ENDO-004', 'Retratamiento endodóntico', 'endodoncia', 120, 5500.00, true, true),
-- Periodoncia
('PERIO-001', 'Raspado y alisado radicular (cuadrante)', 'periodoncia', 60, 1500.00, true, true),
('PERIO-002', 'Cirugía periodontal', 'periodoncia', 120, 6000.00, true, true),
-- Cirugía
('CIRUG-001', 'Extracción simple', 'cirugia', 30, 800.00, true, true),
('CIRUG-002', 'Extracción de tercer molar', 'cirugia', 60, 3500.00, true, true),
('CIRUG-003', 'Extracción quirúrgica compleja', 'cirugia', 90, 5000.00, true, true),
-- Prótesis
('PROT-001', 'Corona de porcelana', 'protesis', 60, 6000.00, true, true),
('PROT-002', 'Corona de zirconia', 'protesis', 60, 8000.00, true, true),
('PROT-003', 'Puente fijo (3 unidades)', 'protesis', 120, 18000.00, true, true),
('PROT-004', 'Prótesis parcial removible', 'protesis', 120, 8000.00, true, false),
('PROT-005', 'Prótesis total', 'protesis', 180, 12000.00, true, false),
-- Implantes
('IMPL-001', 'Implante dental (sin corona)', 'implantes', 90, 15000.00, true, true),
('IMPL-002', 'Corona sobre implante', 'implantes', 60, 8000.00, false, false),
-- Ortodoncia
('ORTO-001', 'Brackets metálicos (tratamiento completo)', 'ortodoncia', 60, 35000.00, true, false),
('ORTO-002', 'Brackets estéticos (tratamiento completo)', 'ortodoncia', 60, 45000.00, true, false),
('ORTO-003', 'Alineadores invisibles', 'ortodoncia', 45, 60000.00, true, false),
('ORTO-004', 'Control mensual ortodoncia', 'ortodoncia', 30, 800.00, false, false),
('ORTO-005', 'Retenedor fijo', 'ortodoncia', 45, 3000.00, false, false),
-- Estética
('ESTE-001', 'Blanqueamiento en consultorio', 'estetica', 90, 5000.00, false, false),
('ESTE-002', 'Blanqueamiento casero', 'estetica', 30, 3000.00, false, false),
('ESTE-003', 'Carilla de resina', 'estetica', 60, 3500.00, false, false),
('ESTE-004', 'Carilla de porcelana', 'estetica', 60, 8000.00, false, false),
-- Diagnóstico
('DIAG-001', 'Radiografía periapical', 'diagnostico', 5, 100.00, false, false),
('DIAG-002', 'Radiografía panorámica', 'diagnostico', 10, 500.00, false, false),
('DIAG-003', 'Radiografía cefalométrica', 'diagnostico', 10, 400.00, false, false),
('DIAG-004', 'Tomografía dental', 'diagnostico', 15, 1500.00, false, false)
) AS t(codigo, nombre, categoria, duracion, precio, rx, anestesia)
WHERE current_setting('app.current_tenant_id', true) IS NOT NULL
AND current_setting('app.current_tenant_id', true) != ''
ON CONFLICT (tenant_id, codigo) DO NOTHING;
-- Skills específicos dentales
INSERT INTO hr.skills (tenant_id, skill_type_id, name, requiere_cedula)
SELECT
current_setting('app.current_tenant_id', true)::UUID,
st.id,
unnest(ARRAY[
'Odontología General',
'Ortodoncia',
'Endodoncia',
'Periodoncia',
'Cirugía Maxilofacial',
'Odontopediatría',
'Prostodoncia',
'Implantología',
'Estética Dental',
'Rehabilitación Oral'
]),
true
FROM hr.skill_types st
WHERE st.name = 'Especialidad Médica'
AND st.tenant_id = current_setting('app.current_tenant_id', true)::UUID
ON CONFLICT DO NOTHING;
-- Ubicaciones (unidades dentales)
INSERT INTO hr.work_locations (tenant_id, name, tipo_consultorio, capacidad, equipamiento)
SELECT current_setting('app.current_tenant_id', true)::UUID, name, tipo, capacidad, equipamiento::TEXT[]
FROM (VALUES
('Unidad Dental 1', 'especialidad', 1, ARRAY['sillon', 'rayos_x', 'ultrasonido']),
('Unidad Dental 2', 'especialidad', 1, ARRAY['sillon', 'rayos_x', 'ultrasonido']),
('Unidad Dental 3', 'especialidad', 1, ARRAY['sillon', 'rayos_x']),
('Quirófano Dental', 'quirofano', 1, ARRAY['sillon', 'rayos_x', 'equipo_cirugia']),
('Sala de Rayos X', 'laboratorio', 2, ARRAY['panoramico', 'cefalometrico']),
('Laboratorio Dental', 'laboratorio', 2, ARRAY['modelos', 'protesis'])
) AS t(name, tipo, capacidad, equipamiento)
WHERE current_setting('app.current_tenant_id', true) IS NOT NULL
AND current_setting('app.current_tenant_id', true) != ''
ON CONFLICT DO NOTHING;