# 🚩 Ejercicio 10 · El error que nadie entiende

**Clase 10 — Validación avanzada, errores y logging**
· Dificultad: 🟠 medio-alto · Tiempo estimado: 60–90 min

---

## La situación

Se cayó la base de datos mientras alguien cargaba un alta. Esto es lo que le
apareció en la pantalla:

```
┌──────────────────────────────────────────────────────────────────────┐
│ SQLSTATE[HY000] [2002] Connection refused (Connection: mysql,        │
│ SQL: insert into `empleados` (`nombre`, `legajo`, `cuit`,            │
│ `password`) values (?, ?, ?, ?))                                     │
└──────────────────────────────────────────────────────────────────────┘
```

Dos problemas, y el segundo es peor que el primero. **No le sirve de nada**: no
entiende qué pasó ni qué hacer. Y **le contaste el motor de base que usás, el
nombre de la tabla y sus columnas**. A cualquiera que sepa mirar, le acabás de
dar el mapa.

Y ahora la otra mitad. Mientras al usuario le contabas de más, al log le
contaste de menos —y también de más, pero de lo que no correspondía:

```
[2026-07-31 11:08] local.INFO: alta de empleado {"nombre":"Mora Sosa",
   "legajo":"L-0500","cuit":"27-33444555-6","password":"ipap2025"}
```

La contraseña de una persona quedó escrita en un archivo de texto. Del error
real —la caída de la base— no quedó **nada**: quien tenga que arreglarlo a las
tres de la mañana no tiene con qué empezar.

## La idea de todo el ejercicio

> **Un mismo hecho tiene que contarse de dos formas muy distintas.**
> A la persona, sin una palabra técnica. Al archivo, con todo el detalle.
> Hoy está exactamente al revés.

## Lo que hace especial a este ejercicio

**Escribe un log de verdad.** No simulado: un archivo en `src/almacen/liquidador.log`
que podés abrir con cualquier editor mientras trabajás. Conviene tenerlo a la
vista, porque es la mitad del ejercicio.

Y el verificador **lee ese archivo**. No mira lo que devolvés: mira **el rastro
que dejás**.

## Tus cinco tareas

| # | Dónde | Qué |
|---|---|---|
| 1 | `app/Reglas/Cuit.php` | La regla propia: verificar el **dígito verificador** |
| 2 | `app/Controllers/EmpleadoController.php` | Que el usuario **no vea nada técnico** |
| 3 | `app/Servicios/Bitacora.php` | …pero que el log **sí guarde el detalle** |
| 4 | idem | Que el log **no guarde la contraseña ni el CUIT** |
| 5 | idem | Que cada suceso quede con **su nivel**: info / warning / error |

## Cómo correrlo

```bash
docker compose run --rm ejercicio
```

Con los cinco pasos en verde aparece tu bandera `ipap{c10-xxxxxxxxxx}`, que
—como siempre— **no está escrita en ningún archivo**.

> **Sin Docker:** `cd src && php index.php`

## Lo que vas a practicar

| Paso | Concepto de la clase |
|------|----------------------|
| 1 | **Reglas de validación propias** (el equivalente de una clase `Rule`) |
| 2 | **Manejo de excepciones**: atrapar y traducir, en vez de dejar pasar |
| 3 | **Logging con contexto**: que el log sirva para diagnosticar |
| 4 | **Qué NO se loguea**: datos personales y credenciales |
| 5 | **Niveles de log** y por qué importan |

## Sobre el dígito verificador

El último dígito de un CUIT no es un número cualquiera: **se calcula a partir de
los otros diez**. Existe para que un error de tipeo se detecte en el momento, y
no tres meses después cuando ARCA rechaza la presentación de todo el organismo.

El algoritmo completo está en el comentario de `Cuit.php`, con un ejemplo
resuelto paso a paso. No hace falta buscarlo afuera.

Lo que el verificador comprueba es justamente el caso que separa una regla de
verdad de una que no lo es: **un CUIT con el formato impecable y el dígito
equivocado**. `27-33444555-1` tiene la forma exacta de un CUIT. No es uno.

## Cómo verifica este ejercicio

**Los pasos 2 y 3 son espejo.** El mismo hecho —la base se cayó— tiene que
producir **dos textos distintos**: uno para la persona, sin nada técnico; otro
para el archivo, con todo. Verificar solo uno dejaría pasar la mitad del error.

**El paso 4 mira los tres caminos.** El alta que sale bien, la que se rechaza y
la que falla. El descuido casi siempre está en uno solo de los tres — es fácil
acordarse de limpiar los datos en el camino feliz y olvidarse en el del error,
que es justo el que más se lee.

**El paso 4 también comprueba que no limpiaste de más.** El `legajo` tiene que
seguir estando: un log sin nada que permita rastrear el caso no sirve para nada.
Tachar todo es tan inútil como no tachar nada.

## Si te trabás

1. **El dígito verificador.** Es un bucle de diez vueltas y una resta. Probá tu
   implementación con el ejemplo del comentario (`20-30111222-0`) antes de
   seguir.
2. **Ojo con el orden.** Si tu regla rechaza un CUIT **válido**, ningún alta
   llega a guardarse y los pasos 2 a 5 no se pueden ni ejecutar. El programa te
   lo avisa cuando detecta ese caso.
3. **El mensaje para el usuario.** El verificador no compara palabra por
   palabra: comprueba que no se filtre nada técnico **y** que digas algo. Una
   respuesta vacía no filtra nada y tampoco sirve. Un mensaje que sirve dice qué
   pasó en términos del usuario y qué puede hacer al respecto.
4. **`limpiar()` hay que escribirla y además usarla.** Hoy existe pero no la
   llama nadie.
5. **Los niveles.** `warning` es "salió mal, pero estaba previsto que pudiera
   pasar". `error` es "salió mal y no estaba previsto". Que alguien tipee mal un
   CUIT no es un error del sistema: es un martes.

---

## Lo interesante

Fijate en la asimetría que acabás de arreglar, porque se repite en todos lados:

**Al usuario le sobraba información y al log le faltaba.** Es el error por
defecto, y no es casual: pasa cuando nadie decide nada. La excepción sale sola
hacia la pantalla porque nadie la atrapó, y al log va lo que había a mano porque
nadie pensó qué hacía falta. **Las dos puntas se arreglan tomando la misma
decisión: quién necesita enterarse de qué.**

Y una que se ve menos. Un log es **un lugar público dentro de tu organización**:
se copia, se rota, se manda por correo cuando hay un problema y termina en
lugares que nadie planeó. Escribí como si cualquiera fuera a leerlo, porque en
algún momento alguien lo va a leer.

> En Laravel esto se escribe casi igual. La regla propia es una clase que
> implementa `ValidationRule`; el manejo centralizado va en `bootstrap/app.php`
> con `->withExceptions()`; y el log es `Log::info/warning/error` con el mismo
> array de contexto. Para no loguear datos sensibles, Laravel trae además
> `$dontFlash` en las excepciones de validación — pero lo que va en el contexto
> de tus propios `Log::` lo elegís vos, cada vez.
