# Migraciones de Base de Datos — SGETECBA

Todas las migraciones van en orden y dentro de una transacción (`BEGIN / COMMIT`).
Si algo falla, se hace rollback completo y la BD queda sin cambios.

---

## migration_001.sql
**Fecha:** 2026-06-15
**Precondición:** Schema base de SGETECBA ya aplicado (tablas originales del sistema).

### Qué hace

**Tablas nuevas de catálogo:**
- `planes_pago` — tipos de plan que puede tener un alumno: MENSUAL, AL CONTADO, ANUAL, SEMESTRAL
- `estados_pago` — estado actual del pago de una inscripción: PENDIENTE, AL DÍA, MORA, PAGADO
- `tipos_beca` — tipos de beca con porcentaje de descuento configurable (ej. BECADO 50%)
- `becas_estudiante` — registro de becas otorgadas, con período, fechas y quién aprobó
- `convalidaciones` — proceso de reconocimiento de materias cursadas en otra institución
- `detalle_convalidaciones` — las materias específicas dentro de cada convalidación
- `secuencias_codigo_estudiante` — tabla auxiliar para generar códigos únicos por carrera y año (ej. INF-26-001)

**Modificaciones a `estudiantes`:**
- `turno_id` — turno en que estudia (FK a `turnos`)
- Datos de bachillerato: `nombre_colegio`, `tipo_colegio`, `ciudad_colegio`, `anio_bachiller`, `anio_diploma`, `es_bth`
- Datos laborales: `trabaja`, `lugar_trabajo`
- Datos del apoderado: `apoderado_nombre`, `apoderado_relacion`, `apoderado_direccion`, `apoderado_ci`, `apoderado_telefono`

**Modificaciones a `inscripciones`:**
- `plan_pago_id` — FK al plan de pago elegido para esa inscripción
- `estado_pago_id` — FK al estado actual del pago

**Modificaciones a `ofertas_asignaturas`:**
- `tipo_periodo` — texto libre para indicar el tipo de período (ej. "1-1", "2-2")

**Función y trigger:**
- `fn_generar_codigo_estudiante()` — genera automáticamente el código del alumno al insertar (ej. INF-26-001)
- Trigger `trg_estudiantes_generar_codigo` en `estudiantes` BEFORE INSERT

**Vista:**
- `vw_horario_estudiante` — horario completo del alumno: asignatura, turno, día, hora, período

**Fix:**
- Índice único en `estudiantes.correo` (solo registros activos)

---

## migration_002.sql
**Fecha:** 2026-06-15
**Precondición:** migration_001 ya aplicada.

### Qué hace

**Reparación de datos:**
- Elimina (soft delete) estudiantes de prueba sin carrera ni código
- Asigna `carrera_id` a estudiantes seed usando el prefijo de su código (ej. `INF-2024-001` → carrera INF)
- Asigna modalidad a períodos académicos y ofertas que quedaron sin ella durante el desarrollo

**Índices faltantes (20 columnas de FK sin índice):**
Agrega índices en: `abandonos_estudiantes`, `becas_estudiante`, `clases_planificadas`, `convalidaciones`, `detalle_asistencia`, `estudiantes`, `faltas`, `horario_clases`, `inscripciones`, `lotes_importacion`, `pensum`, `sesiones_asistencia`, `staging_estudiantes_unificados`.
Sin estos índices los JOINs y deletes en cascada hacen seq-scan en tablas grandes.

**NOT NULL en columnas críticas:**
- `carreras.modalidad_academica_id`
- `inscripciones.estado_inscripcion_id`
- `periodos_academicos.modalidad_academica_id`
- `ofertas_asignaturas.carrera_id` y `modalidad_academica_id`
- `estudiantes.carrera_id`

**Mejoras a `pensum`:**
- Agrega `actualizado_en` y `eliminado_en` (faltaban las columnas de auditoría)
- `nivel_academico_id` pasa a NOT NULL
- Trigger `trg_pensum_actualizado_en` para mantener la fecha de actualización

**Fix:**
- Elimina índice único duplicado en `periodos_academicos.codigo`
- Agrega unique index en `horario_clases` para evitar el mismo día dos veces en la misma oferta

---

## migration_003.sql
**Fecha:** 2026-06-15
**Precondición:** migration_001 y migration_002 ya aplicadas.

### Qué hace

**1. Pensum versionado**
Antes solo podía existir un pensum por asignatura+carrera+nivel (una única versión para siempre).
Ahora el año forma parte del unique: `(asignatura_id, carrera_id, nivel_academico_id, anio)`.
Esto permite tener pensum-2024, pensum-2025, pensum-2026 en paralelo.

Columnas nuevas en `pensum`:
- `anio smallint` — año al que pertenece esta versión del pensum (default 2025)
- `es_obligatoria boolean` — distingue materias obligatorias de optativas (default true)

**2. Tabla `pensum_prerequisitos`**
Registra qué materia del pensum debe estar aprobada antes de poder cursar otra.
Ejemplo: "para cursar Programación II (pensum_id=5) primero debes tener aprobada Programación I (prerequisito_pensum_id=3)".

**3. Tabla `documentos_estudiante`**
Almacena rutas a archivos subidos por el alumno al momento de inscribirse:
- `FOTO_CI` — foto del carnet de identidad
- `FOTO_DIPLOMA` — foto del diploma de bachiller
- `FOTO_PERFIL` — foto del estudiante
- `OTRO` — cualquier otro documento

**4. Tabla `estudiante_carreras`**
Para alumnos inscriptos en más de una carrera simultáneamente (doble carrera).
La carrera principal sigue en `estudiantes.carrera_id`. Esta tabla registra las adicionales, con su propio turno, grupo, período de ingreso y código de estudiante.

**5. Tabla `historial_carreras`**
Registro permanente de cada vez que un alumno cambia de carrera. No se borra, queda como historial. Incluye carrera anterior, carrera nueva, fecha y motivo.

**6. Columnas nuevas en `estudiantes`**
- `emision_ci` — departamento de emisión del carnet (LP, CBB, SC, OR, etc.)
- `direccion` — dirección del estudiante
- `periodo_ingreso_id` — FK al período académico en que se inscribió (ej. Semestre I 2025)
- `grupo_id` — FK al grupo al que pertenece (A, B, C, D)
- `anio_pensum` — año del pensum que sigue el alumno (se deriva del período de ingreso)
- `tipo_inscripcion` — NUEVO / REINGRESO / CAMBIO_CARRERA / REINSCRIPCION
- `fecha_egreso` — fecha en que completó el pensum y egresó

Los datos existentes se actualizan automáticamente: `periodo_ingreso_id` se infiere de `fecha_inscripcion` contra el rango de los períodos, y `anio_pensum` se deriva del año del período encontrado.

**7. Estado CONVALIDADA en `estados_inscripcion`**
Cuando una materia se aprueba por convalidación desde otra institución, se crea una inscripción con este estado para que quede en el historial académico del alumno.
Se agrega también `inscripciones.convalidacion_id` (FK a `convalidaciones`) para vincular la inscripción con el proceso que la originó.

**8. Estado EGRESADO en `estados_estudiante`**
Para marcar a los alumnos que completaron el pensum y obtuvieron su título.

**9. `sesiones_asistencia.tipo_sesion`**
Distingue el tipo de clase registrada:
- `NORMAL` — clase del horario habitual
- `RECUPERACION` — repone una clase que fue cancelada
- `ESPECIAL` — taller, evaluación o evento puntual fuera del horario

**10. `asignaturas.horas_semanales` y `asignaturas.creditos`**
Carga horaria por materia. Necesario para validar que una oferta tenga las horas correctas y para generar reportes de cumplimiento del docente.

**11. `asignaciones_docente.tipo`**
- `TITULAR` — docente responsable oficial de la materia
- `INTERINO` — cubre el cargo de forma temporal
- `SUPLENTE` — reemplaza puntualmente al titular

**12. `ofertas_asignaturas.horas_planificadas_total`**
Total de horas que debería tener la oferta en el período. Se compara con las sesiones de asistencia registradas para generar el reporte de horas ejecutadas vs planificadas por docente.

**13. Trigger de choque de horario — estudiante**
`trg_inscripciones_choque_horario` en `inscripciones` BEFORE INSERT.
Si el alumno ya tiene otra materia activa que comparte día y franja horaria con la nueva inscripción, el INSERT falla con error `CHOQUE_HORARIO_ESTUDIANTE`.
No aplica a inscripciones con estado BAJA, ABANDONO o CONVALIDADA.

**14. Trigger de choque de horario — docente**
`trg_asignaciones_choque_horario` en `asignaciones_docente` BEFORE INSERT.
Si el docente ya tiene otra asignación activa en el mismo día y franja horaria, el INSERT falla con error `CHOQUE_HORARIO_DOCENTE`.

**15. Plan de pago BECADO**
Agrega el quinto plan a `planes_pago`. Cuando el alumno tiene este plan, el descuento aplicado se calcula desde su registro en `becas_estudiante`.

---

## migration_004.sql
**Fecha:** 2026-06-16
**Precondición:** migration_001..003 aplicadas. Pensada para correr sobre BD limpia (solo login).

### Qué hace
Alinea el esquema con las reglas de negocio confirmadas. Es idempotente y sin IDs hardcodeados (a diferencia de migration_002).

**1. Código único por estudiante**
El código del alumno vive solo en `estudiantes.codigo_estudiante` (uno por estudiante). Un estudiante puede estar en 1..N carreras vía `estudiante_carreras`, que **deja de tener código propio** (se elimina la columna `codigo_estudiante` de esa tabla).

**2. Ofertas REGULAR vs LIBRE**
Agrega `ofertas_asignaturas.tipo_oferta` (`REGULAR` | `LIBRE`):
- `REGULAR` — exclusiva de una carrera (`carrera_id` obligatorio).
- `LIBRE` — inscripción abierta, sin carrera (`carrera_id` puede ser NULL).
`carrera_id` pasa a NULLABLE y un CHECK (`ck_oferta_carrera_por_tipo`) obliga a que las REGULAR tengan carrera. Índice `ix_ofertas_tipo_oferta` para filtrar las libres.

**3. Si trabaja, dónde**
CHECK `ck_estudiantes_trabaja_lugar`: si `trabaja = true`, `lugar_trabajo` es obligatorio y no vacío.

---

## migration_005.sql
**Fecha:** 2026-06-16
**Precondición:** migration_001..004 aplicadas.
Correcciones y optimización de la auditoría estructural del 2026-06-16. Idempotente, sin IDs hardcodeados.

**1. Trigger de código de estudiante**
Mantiene el formato `CARRERA-YYNNN` (ej. `CIA-25126`: carrera + año de 2 dígitos + correlativo, un solo guión, como la imagen). Mejora: ahora hace `RAISE EXCEPTION` si la carrera no tiene código (antes devolvía NULL en silencio).

**2. Inconsistencia de tipo**
`carreras.modalidad_academica_id` era `bigint` y la PK referida `modalidades_academicas.id` es `smallint`. Se unifica a `smallint` (FK preservada sin `ON DELETE`).

**3. 14 índices FK faltantes**
Índices en columnas FK que hacían seq-scan en JOIN/cascada: `carreras`, `periodos_academicos`, `archivos_importacion`, `auditoria_logs`, `conflictos_datos` (x2), `detalle_convalidaciones`, `estudiante_carreras` (x3), `historial_carreras` (x3), `mapa_fuentes_estudiante`. Tras esto, **0 FK sin índice**.

**4. Choque de horario en UPDATE**
`trg_inscripciones_choque_horario` y `trg_asignaciones_choque_horario` ahora disparan en `INSERT OR UPDATE OF oferta_asignatura_id` (antes solo INSERT).

> Estado post-005: 11/11 pruebas de integridad. Ver `BD/MD/AUDITORIA_BD_16-06-2026.md`.

---

## migration_006.sql
**Fecha:** 2026-06-16
**Precondición:** migration_001..005 aplicadas.
Corrección estructural: el plan y estado de pago pasan de ser por materia a ser por matrícula.

**1. Nueva tabla `matriculaciones`**
Modela la matrícula real: un alumno se inscribe a una carrera en un período académico. Columnas: `estudiante_id`, `carrera_id`, `periodo_academico_id`, `plan_pago_id`, `estado_pago_id`, `tipo_inscripcion` (NUEVO/REINGRESO/CAMBIO_CARRERA/REINSCRIPCION), `turno_id`, `grupo_id`, `fecha_matricula`, `observacion`, soft-delete y auditoría. Unicidad: un alumno no puede tener dos matrículas activas en la misma carrera+período. 8 índices de soporte en FK.

**2. Reestructura `inscripciones`**
Se eliminan las columnas `plan_pago_id` y `estado_pago_id` de `inscripciones` (el pago ya no es por materia). Se agrega `matriculacion_id` (FK a `matriculaciones`) para vincular la inscripción por materia a su matrícula correspondiente.

**3. Vista `vw_reporte_pagos`**
Permite consultar "¿quién pagó?": estudiante, carrera, período, plan de pago, estado de pago y tipo de inscripción, con filtro de soft-delete.

> Estado final: la BD pasa 13/13 pruebas de integridad y el dump `BD/MD/sgetecba_16-06-2026.sql` recarga en una base nueva sin errores. Ver `BD/MD/AUDITORIA_BD_16-06-2026.md`.

---

## migration_013.sql
**Fecha:** 2026-06-18
**Precondición:** migration_001..012 aplicadas.

Agrega soporte para que una misma asignatura pueda tener **códigos distintos por carrera**.

**Columna nueva en `pensum`:**
- `codigo_en_carrera VARCHAR(50) NULL` — código específico de la asignatura dentro de la carrera (ej: `INF-MAT-01` en Informática y `MKT-MAT-01` en Marketing para la misma "Álgebra Lineal"). Si es NULL se usa el código global del catálogo `asignaturas`.

> Cambio puramente aditivo: columna nullable, sin impacto en datos existentes.
