# Arquitectura unificada

La aplicación usa PHP 8.2+, PDO y un único archivo SQLite. El catálogo público y la gestión interna comparten configuración, sesión, CSRF, autenticación, tablas y sistema visual.

## Entradas

- `index.php`: catálogo público.
- `producto.php`: detalle por slug.
- `carrito.php`: revisión del carrito.
- `api/catalogo.php` y `api/categorias.php`: lectura pública.
- `api/pedido.php`: validación transaccional y evento de WhatsApp.
- `admin/index.php`: enrutador autenticado de todos los módulos internos.
- `install.php`, `login.php`, `logout.php`: instalación y autenticación.
- `router.php`: rutas amigables en el servidor integrado.

## Capas

- `app/bootstrap.php`: configuración, sesión, migraciones, cabeceras y errores.
- `app/functions.php`: conexión SQLite, seguridad, URLs, slugs, fotos y consultas públicas.
- `app/services.php`: transacciones contables y reglas FIFO.
- `modules/`: controladores y vistas administrativas.
- `partials/`: layouts públicos y administrativos.
- `assets/css/app.css`: variables y componentes visuales compartidos.

## Relaciones principales

```text
categorias ─ productos ─ lotes ─ ingreso_detalles ─ ingresos
                     └─ venta_detalles ─ ventas ─ clientes
                     └─ eventos_pedido (referencia en JSON, sin FK ni datos personales)

proveedores ─ productos
           ├─ ingresos
           ├─ pagos
           └─ liquidaciones

usuarios ─ auditoria
configuracion ─ preferencias internas y públicas
```

## Stock e importes

Los importes se guardan como centavos enteros. El precio público es `productos.precio_venta_centavos`. El stock se calcula como la suma de `lotes.cantidad_disponible` no anulados.

Cada ingreso crea un lote con costo histórico. Las ventas consumen lotes por FIFO. Las devoluciones revierten importe, costo y ganancia en proporción al detalle original. Un cierre de carrito no descuenta stock porque es una intención de compra pendiente de confirmación.

## Compatibilidad

`app/migrations.php` conserva instalaciones previas. Agrega columnas y tablas solamente cuando faltan, completa slugs únicos y registra cada versión en `migraciones`. Los nombres internos históricos `entregas_hermana` y `entregado_hermana_centavos` se mantienen para evitar una migración destructiva.
