diff --git a/schemas/01-dental-schema-ddl.sql b/schemas/01-dental-schema-ddl.sql new file mode 100644 index 0000000..1b791d5 --- /dev/null +++ b/schemas/01-dental-schema-ddl.sql @@ -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 +-- ============================================================================ diff --git a/seeds/fase8/01-dental-catalogos.sql b/seeds/fase8/01-dental-catalogos.sql new file mode 100644 index 0000000..86256cb --- /dev/null +++ b/seeds/fase8/01-dental-catalogos.sql @@ -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;