# Arquitectura y flujos

## Vista general

```text
Navegador
  ├─ HTML + bibliotecas locales/CDN
  └─ Peticiones AJAX
        ↓
Caddy / FrankenPHP → public/index.php
        ↓
CodeIgniter: rutas + filtros + Shield
        ↓
Controladores → modelos / Query Builder → MySQL
        ├─ entidades → respuestas API
        └─ vistas PHP → HTML + JavaScript
```

Es un monolito MVC renderizado en servidor con interacciones AJAX. No se localizaron colas, servicios de negocio propios, tareas programadas ni comunicación con un ERP. Los nombres de scripts de ejemplo de la plantilla no representan integraciones activas.

## Mapa del repositorio

| Ruta | Responsabilidad |
| --- | --- |
| `app/Config/` | Configuración del framework, base de datos, rutas, autenticación y filtros. |
| `app/Controllers/` | Coordinación HTTP; acceso al catálogo y operaciones sobre pedidos. |
| `app/Models/` | Persistencia y consulta de pedidos y detalles. |
| `app/Entities/` | Entidades CodeIgniter con tratamiento de fechas; sin reglas de negocio propias. |
| `app/Database/Migrations/` | Evolución del esquema de negocio. |
| `app/Database/Seeds/` | Solamente `.gitkeep`; no hay carga inicial propia. |
| `app/Views/` | Formulario, login, dashboard, layout, estadísticas y páginas de error. |
| `app/Filters/`, `Helpers/`, `Libraries/` | Sin implementaciones propias localizadas. |
| `public/` | Raíz web, recursos de plantilla, iconos y manifiesto. |
| `tests/` | Pruebas y soporte de ejemplo del starter. |
| `vendor/` | Dependencias instaladas por Composer; ignoradas por Git. |
| `writable/` | Archivos de ejecución: sesiones, caché, logs y otros datos temporales. |
| `Docs/` | Análisis y contexto técnico del proyecto. |

`app/Config/Services.php` y `app/Common.php` conservan sus estructuras base sin lógica específica. `builds` es un script del starter que cambia la rama/versionado del framework; no es el proceso de despliegue de la aplicación. `preload.php` es el punto de configuración de precarga PHP disponible en el repositorio.

## 1. Captura del pedido

1. `GET /` llama a `Home::index()`.
2. El controlador consulta todos los artículos con Query Builder.
3. `form.php` serializa el catálogo a JavaScript e inicializa Typeahead.
4. El cliente y los renglones se acumulan en `globalOrder` en memoria del navegador.
5. `saveOrder()` envía el objeto mediante jQuery a `POST /order` como formulario.
6. `Order::create()` llena una entidad con el POST, guarda la cabecera y hace `insertBatch()` de los detalles.
7. La API devuelve HTTP 201 y un mensaje; el navegador limpia el pedido.

No hay persistencia del borrador ni soporte offline localizado. El backend no replica las validaciones de cliente y cantidad del formulario.

## 2. Consulta y gestión

1. `GET /dashboard` pasa por el filtro `session` de Shield.
2. `Dashboard::main()` consulta pedidos y entrega la vista.
3. La vista no utiliza esa colección inicial: DataTables hace otra consulta a `GET /order`.
4. `getOrdersWithDetails()` hace un `LEFT JOIN`, excluye filas eliminadas y devuelve entidades `Order` con atributos adicionales del detalle.
5. La tabla pagina, busca y exporta en el navegador; no usa paginación del servidor.
6. Las acciones individuales y por lote envían los IDs de detalles y recargan la tabla al terminar.

La referencia visible de orden se construye en JavaScript: prefijo `BHLXCC`, fecha `ddmmaa`, ID rellenado a cuatro caracteres y sufijo `A`. No existe una columna independiente para esa referencia.

## 3. Estados y borrado

- Cabeceras y detalles tienen estados independientes.
- Las acciones de finalización únicamente modifican `order_details.status`.
- La interfaz deshabilita acciones individuales sobre renglones completados, pero el backend no impone esa restricción para el borrado.
- Al borrar, se abre una transacción, se hace soft delete del detalle y se cuenta el resto de detalles activos. Si no queda ninguno, se hace soft delete del pedido.
- Las marcas temporales se delegan principalmente a MySQL: ambos modelos tienen `useTimestamps = false`.

## 4. Autenticación y configuración transversal

- `service('auth')->routes($routes)` registra rutas de Shield.
- La aplicación carga los helpers `auth` y `setting` desde `app/Config/Autoload.php`.
- El alias de filtro `session` lo aporta `vendor/codeigniter4/shield/src/Config/Registrar.php:33-47`; no depende del import sin uso `Shield\SessionFilter` presente en el archivo local de filtros.
- Se usa autenticación de sesión con correo y contraseña; registro y enlaces mágicos están deshabilitados en `Auth.php`.
- Hay grupos y permisos genéricos de Shield, pero no permisos específicos de pedidos aplicados a las rutas.
- Las sesiones usan archivos en `writable/session`, con expiración configurada de 7.200 segundos.
- Zona horaria: `America/Mexico_City`; la configuración de conexión declara `-06:00`.
- Locale predeterminado `es_MX.utf8`, locales soportados `['en']` y HTML `lang="en"`: configuración lingüística poco homogénea.

El filtro CSRF está comentado en los filtros globales. Incluir `csrf_field()` en el login no activa por sí solo la validación del filtro.

## Recursos externos

| Recurso | Uso |
| --- | --- |
| Google Fonts | Fuente Inter. |
| jsDelivr | Notyf 3, CSS y JavaScript. |
| Unilever Solutions | Imagen del logotipo. |
| Matomo | Seguimiento de páginas y enlaces mediante `app/Views/matomo.php`. |
| Notion | Enlace documental externo en el pie de página; su contenido no fue revisado. |

Existe un manifiesto web con nombres genéricos, pero no se localizó registro de service worker. El formulario depende del servidor y de algunos recursos externos incluso siendo una herramienta de contingencia.
