# Hallazgos y prioridades

Fecha: **2026-09-28**. Las prioridades expresan impacto sobre el flujo actual. Son observaciones del código y de los comandos locales; los escenarios HTTP y de interfaz descritos no fueron reproducidos contra una instalación operativa.

## Prioridad alta

### A1. API de pedidos sin autenticación ni autorización aplicada

**Evidencia:** `app/Config/Routes.php:8-22`, `app/Config/Filters.php:75-111` y salida de `php spark routes`.

El dashboard y estadísticas requieren sesión, pero listar pedidos, completar detalles y borrar detalles no tienen ese filtro. Los controladores tampoco realizan una comprobación equivalente. Proteger la vista no protege sus datos ni las operaciones AJAX.

**Acción sugerida:** definir explícitamente qué operaciones son públicas. Mantener la captura pública si así lo requiere el negocio y aplicar sesión/permisos a lectura y gestión administrativa. Verificar respuestas para usuarios anónimos y autorizados.

### A2. Entrada sin validar y campos internos aceptados

**Evidencia:** `app/Controllers/Order.php:28-49`, `app/Models/OrderModel.php:16-23,38-42`, `app/Models/OrderDetailModel.php:16-25,40-44`.

El controlador llena una entidad con todo el POST. El modelo permite `id`, `status` y fechas, y no tiene reglas de validación. Un `id` suministrado puede cambiar el comportamiento de `save()` de inserción a actualización. No se valida en servidor la longitud del cliente, el catálogo, el rango de cantidades ni una lista no vacía de artículos.

**Acción sugerida:** construir un payload permitido expresamente para la creación y validar cabecera y renglones antes de escribir. Los campos internos deben derivarse en el servidor. Cubrir envíos manipulados además del formulario normal.

### A3. Creación no atómica y manejo parcial de fallos

**Evidencia:** `app/Controllers/Order.php:33-52`.

Se guarda la cabecera antes de insertar los detalles y sin transacción. El `try/catch` solamente cubre `insertBatch()`. No se comprueban retornos falsos de los modelos. Un fallo puede dejar cabeceras sin detalles, y algunos fallos pueden no convertirse en una respuesta de error coherente.

**Acción sugerida:** validar primero y envolver ambas escrituras en una transacción con comprobación del resultado. Probar que un fallo en cualquier detalle no deja una cabecera parcial.

### A4. Tipos de identificadores incompatibles entre captura y almacenamiento

**Evidencia:** migraciones `CreateOrderTable.php:19-22`, `CreateItemsTable.php:18-24` y `CreateOrderDetailTable.php:23-30` (archivos con prefijo de fecha en `app/Database/Migrations/`).

La interfaz trata el cliente como código de ocho dígitos, pero la base lo almacena como INT. Los ceros iniciales no se conservan. El catálogo admite SKU VARCHAR(100), mientras que el detalle usa INT; un SKU alfanumérico o fuera de rango no se representaría fielmente.

**Acción sugerida:** confirmar los formatos reales de cliente/SKU y alinear esquema, validación y serialización mediante una migración compatible con los datos existentes.

### A5. CSRF no habilitado en los filtros

**Evidencia:** `app/Config/Filters.php:75-85`, `app/Config/Security.php:18`, `app/Views/login.php:103-104`.

La protección configurada es por sesión, pero el filtro global está comentado y no se aplica a rutas de negocio. El campo CSRF del login no activa la verificación. Al proteger las operaciones mediante cookies de sesión, debe completarse también el contrato CSRF de los envíos AJAX.

**Acción sugerida:** habilitar el filtro en los flujos correspondientes e incluir/renovar tokens en el frontend, comprobando rechazos y respuestas de éxito.

## Prioridad media

| Hallazgo | Evidencia | Efecto y acción sugerida |
| --- | --- | --- |
| Completar un ID inexistente no comprueba `find()` | `app/Controllers/Order.php:55-60` | Asigna estado a un resultado potencialmente nulo; devolver 404 antes de guardar. |
| Conteo del lote no representa filas modificadas | `app/Controllers/Order.php:63-76` | `count($ids)` incluye repetidos, inexistentes o registros sin cambios; validar/deduplicar IDs y definir qué cuenta la respuesta. |
| Restricciones de estado solamente visuales | `app/Views/dashboard.php:120-135`; `app/Controllers/Order.php:55-105` | El backend permite borrar completados aunque la UI lo deshabilita. Definir y aplicar las transiciones autorizadas. |
| Cabecera y detalles no sincronizan estado | `app/Controllers/Order.php:55-76` | Un pedido puede seguir `pending` con todos sus detalles completados; aclarar si se necesita estado agregado. |
| SKU seleccionado puede quedar obsoleto | `app/Views/form.php:231-235,247-255,285-287` | Se limpia el texto visible después de añadir, pero no el SKU oculto; editar el texto tampoco invalida la selección. Limpiar ambos y exigir selección vigente. |
| SKU duplicados desincronizan carrito y DOM | `app/Views/form.php:294-324,356-373` | Se aceptan duplicados con el mismo ID de fila; borrar filtra todos los elementos del SKU y editar cambia el primero. Consolidar cantidades o usar identidad independiente por renglón. |
| Edición de cantidad no aplica máximo | `app/Views/form.php:362-373` | El handler corrige valores menores que uno, pero no mayores que 99; repetir validación al editar y al guardar. |
| Botón de guardar termina en estado incorrecto | `app/Views/form.php:326-353` | En error queda deshabilitado porque `.done()` no corre; en éxito `.done()` lo habilita incluso con carrito vacío. Recalcular el estado al finalizar la petición. |
| Entrada de búsqueda usada como regex | `app/Views/form.php:258-269` | Una consulta como `[` puede producir una expresión inválida y romper el autocompletado. Usar búsqueda literal o escapar la expresión. |
| Texto editable insertado como HTML | `app/Views/form.php:299-322` | La descripción visible se interpola en una plantilla HTML. Construir nodos de texto o escapar según contexto; alcance de explotación no comprobado. |
| Rutas resource hacia métodos ausentes | `app/Config/Routes.php:22`; `app/Controllers/Order.php` | Se publica un contrato CRUD que no está implementado. Limitar las rutas generadas a las acciones reales. |
| Consulta completa y duplicada del dashboard | `app/Controllers/Dashboard.php:14-18`; `app/Views/dashboard.php:37-41`; `app/Models/OrderModel.php:55-63` | Se consulta al renderizar y nuevamente por AJAX; el frontend procesa todo el historial activo. Quitar la consulta redundante y evaluar paginación del servidor según volumen. |
| Ausencia de pruebas del dominio | `tests/` | Los cinco tests existentes no ejercitan pedidos. Priorizar pruebas de los casos A1–A4 y del borrado transaccional. |
| Instalación inicial incompleta | `app/Database/Seeds/`; archivos Docker/DAMP | Falta carga de catálogo y Compose depende de servicios externos. Documentar el aprovisionamiento reproducible. |

## Prioridad baja / mantenibilidad

- **Estado cancelado:** SQL declara `cancelled`, pero el mapa visual usa `canceled` (`app/Views/dashboard.php:101-108`); la etiqueta prevista no se aplica al valor de base de datos.
- **Acoplamiento de serialización:** `dashboard.php:69-72` asume ID string y fecha con propiedad `date`; cambios de casts o conexión pueden romper la referencia visible.
- **JavaScript embebido y repetido:** formulario y dashboard mezclan presentación y lógica; las funciones de formato de exportación se repiten. Extraer por necesidad, conservando el contrato existente.
- **Contenido pendiente:** estadísticas, métodos vacíos y vista de bienvenida del starter conviven con la aplicación de negocio.
- **Idioma y accesibilidad:** documentos HTML declaran inglés pese a texto español; el viewport restringe el zoom. Revisar junto con etiquetas y mensajes del formulario.
- **Recursos heredados:** muchos scripts y assets corresponden a demos. Medir uso real antes de retirarlos.
- **Documentación original genérica:** el README raíz describe el starter y `tests/README.md` menciona PHPUnit 9.x, mientras el lock instala 10.5.63.
- **Configuración local:** locale, cookie segura, CSP y HTTPS deben revisarse en el entorno de despliegue concreto; los archivos observados describen principalmente desarrollo y pueden ser sobrescritos por variables de entorno.

## Orden de trabajo propuesto

1. Acordar captura pública, permisos administrativos y formatos de identificadores.
2. Corregir acceso, payload permitido, validación y atomicidad con pruebas de regresión.
3. Consolidar estados, respuestas de errores y operaciones por lote.
4. Corregir el carrito y probar interacción completa en navegador.
5. Hacer reproducible la carga inicial y verificar migraciones sobre MySQL.
6. Abordar estadísticas y optimización cuando existan requisitos y volumen conocidos.

## Preguntas de negocio abiertas

- ¿Quién puede registrar pedidos y quién debe consultarlos, completarlos o borrarlos?
- ¿Los clientes y SKU pueden tener ceros iniciales o caracteres no numéricos?
- ¿Un mismo SKU puede repetirse en un pedido o debe consolidarse?
- ¿Completar todos los detalles debe completar la cabecera? ¿Se permiten reaperturas o cancelaciones?
- ¿Cuál es la fuente oficial del catálogo y cómo se actualiza?
- ¿Qué indicadores debe mostrar estadísticas y qué formato de exportación utiliza la operación?
- ¿«Contingencia» requiere funcionamiento sin Internet o solamente un canal alternativo al sistema principal?

No se realizaron correcciones funcionales como parte de este análisis.
