# Normalización de Tabla Boletas

## Fecha: 2026-01-21

## Resumen
Se normalizó la tabla `boletas` eliminando 11 columnas redundantes que duplicaban información de las tablas `clientes` y `datos_apr`.

---

## Cambios Realizados

### 1. Columnas Eliminadas (Redundantes)

#### Datos del Cliente (4 columnas)
- `nombre` → Ahora se obtiene de `clientes.nombre_cliente`
- `rut` → Ahora se obtiene de `clientes.rut_cliente`
- `direccion` → Ahora se obtiene de `clientes.direccion`
- `ciudad` → Ahora se obtiene de `ciudades.nombre_ciudad`

#### Datos del APR (7 columnas)
- `nombre_servicio` → Ahora se obtiene de `datos_apr.Nombre_servicio`
- `rut_servicio` → Ahora se obtiene de `datos_apr.rut_servicio`
- `direccion_servicio` → Ahora se obtiene de `datos_apr.Direccion`
- `comuna_servicio` → Ahora se obtiene de `comunas.nombre_comuna`
- `fono_oficina_servicio` → Ahora se obtiene de `datos_apr.telefono_oficina`
- `rep_legal_servicio` → Ahora se obtiene de `datos_apr.Representante_legal`
- `fono_contacto_rep_legal` → Ahora se obtiene de `datos_apr.fono_contacto`

### 2. Nueva Tabla: `boletas_snapshot`

Para mantener auditoría histórica, se creó una tabla que guarda un snapshot JSON de los datos al momento de emisión:

```sql
CREATE TABLE boletas_snapshot (
    id_snapshot INT AUTO_INCREMENT PRIMARY KEY,
    id_boleta INT NOT NULL,
    fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    snapshot_datos JSON NOT NULL,
    INDEX idx_boleta (id_boleta),
    FOREIGN KEY (id_boleta) REFERENCES boletas(id_boleta) ON DELETE CASCADE
);
```

**Estructura del JSON:**
```json
{
  "datos_cliente": {
    "nombre": "Juan Pérez",
    "rut": "12345678-9",
    "direccion": "Calle Principal 123",
    "ciudad": "Santiago",
    "sector": "Sector 1",
    "numero_medidor": "M-001"
  },
  "datos_apr": {
    "nombre_servicio": "APR Buenos Aires",
    "rut_servicio": "76.123.456-7",
    "direccion_servicio": "Av. Central 456",
    "comuna_servicio": "Buenos Aires",
    "telefono_oficina": "+56912345678",
    "representante_legal": "María González",
    "telefono_representante": "+56987654321"
  },
  "datos_boleta": {
    "num_boleta": 1234,
    "fecha_emision": "2026-01-21",
    "fecha_vencimiento": "2026-02-21",
    "total_boleta": 15000,
    "estado_pago": 5
  }
}
```

### 3. Stored Procedure Actualizado

El SP `calulo_boleta` fue modificado para:
- **NO insertar** datos redundantes en `boletas`
- **Guardar automáticamente** un snapshot en `boletas_snapshot` al generar cada boleta

### 4. Código Next.js Actualizado

#### Archivos modificados:
- `src/lib/queries.ts` - Queries con JOINs para obtener nombre/rut
- `src/app/api/facturacion/[id]/pdf/route.ts` - Usa snapshot histórico cuando existe

**Lógica de fallback:**
1. Intenta obtener snapshot histórico
2. Si existe, usa datos del snapshot (datos originales)
3. Si no existe, usa datos actuales vía JOIN

---

## Beneficios

### ✅ Normalización
- Eliminadas 11 columnas redundantes
- Datos únicos en sus tablas correspondientes
- Menor duplicación de información

### ✅ Auditoría Histórica
- Snapshots JSON preservan datos exactos al momento de emisión
- Reimprimir boletas antiguas muestra datos originales
- Flexibilidad para agregar campos sin alterar schema

### ✅ Mantenibilidad
- Cambios en clientes/APR no afectan boletas antiguas
- Código más limpio con JOINs explícitos
- Fácil auditoría de diferencias históricas

---

## Migración de Datos Históricos

Se migraron **4907 boletas antiguas** a la tabla `boletas_snapshot`, preservando:
- 365 boletas con diferencias entre datos guardados y actuales
- Registro histórico completo de todas las boletas

---

## Queries Útiles

### Ver snapshot de una boleta
```sql
SELECT 
    id_boleta, 
    JSON_PRETTY(snapshot_datos) 
FROM boletas_snapshot 
WHERE id_boleta = 1234;
```

### Ver diferencias históricas
```sql
SELECT 
    bs.id_boleta,
    JSON_EXTRACT(bs.snapshot_datos, '$.datos_cliente.nombre') as nombre_historico,
    c.nombre_cliente as nombre_actual
FROM boletas_snapshot bs
INNER JOIN boletas b ON bs.id_boleta = b.id_boleta
INNER JOIN clientes c ON b.id_cliente = c.Id_cliente
WHERE JSON_EXTRACT(bs.snapshot_datos, '$.datos_cliente.nombre') != c.nombre_cliente
LIMIT 10;
```

### Contar snapshots
```sql
SELECT COUNT(*) as total_snapshots FROM boletas_snapshot;
```

---

## Archivos de Migración

1. `migrations/001_normalize_boletas.sql` - Backup y nuevo SP
2. `migrations/002_add_boletas_snapshot.sql` - Tabla snapshot y migración de datos
3. `migrations/003_update_procedure_with_snapshot.sql` - SP con guardado automático de snapshot

---

## Rollback (Si es necesario)

### Restaurar desde backup:
```sql
-- Ver backup
SELECT COUNT(*) FROM boletas_backup_20260121;

-- Restaurar (CUIDADO: elimina boletas nuevas)
TRUNCATE TABLE boletas;
INSERT INTO boletas SELECT * FROM boletas_backup_20260121;
```

### Restaurar SP original:
```sql
-- Ejecutar el SP original desde el backup de la base de datos
```

---

## Notas Importantes

- Las columnas redundantes **aún existen** en la tabla `boletas` pero están vacías (NULL) en boletas nuevas
- Se pueden eliminar con el ALTER TABLE comentado en `002_add_boletas_snapshot.sql`
- **Recomendación:** Mantenerlas por un período de prueba antes de eliminarlas definitivamente
- El snapshot JSON es compatible con MySQL 5.6+ (requiere soporte JSON)

---

## Próximos Pasos (Opcional)

1. Monitorear por 1-2 meses que todo funciona correctamente
2. Verificar que no hay queries legacy que usen las columnas redundantes
3. Ejecutar el ALTER TABLE para eliminar las columnas definitivamente
4. Liberar espacio en disco

---

## Contacto

Para dudas o problemas con esta migración, revisar:
- Logs de Next.js: `npm run dev`
- Logs de MySQL: `docker logs aprsolo-mysql56`
- Snapshots: Tabla `boletas_snapshot`
