# Portal de clientes Hormimax

Portal web y API para que los clientes de Hormimax consulten su información comercial: obras,
órdenes de compra, programación, despachos, facturación y estado de cuenta.

Es un proyecto **aparte de SGO**. Lee los datos de SGO solo a través de vistas de la base de datos
(`portal_v_*`) y nunca modifica nada de SGO. El diseño completo está en
`sgo2/docs/portal_clientes/DISENO.md`.

## Estado

| Etapa | Contenido | Estado |
|---|---|---|
| 1 | Tablas, vistas, inicio de sesión, invitaciones, permisos por obra y tema, pantalla de administración en SGO | **Lista en DEV** |
| 2 | Órdenes de compra con saldo, despachos con horas en obra y documento de la guía | **Lista en DEV** |
| 3 | Programación de hormigón: lista por día y obra, calendario mensual y Excel | **Lista en DEV** |
| 4 | Facturación: facturas y notas de crédito con montos oficiales del SII, guías, PDF y Excel | **Lista en DEV** |
| 5 | Estado de cuenta: copia desde KAME 3 veces al día, tramos por antigüedad y Excel | **Lista en DEV** |
| 6 | Camión en ruta: mapa, chofer y hora estimada de llegada, solo mientras va a la obra; aviso a comercial por obras sin geocerca | **Lista en DEV** |
| 7 | Panel del administrador del cliente | Pendiente |

## Carpetas

```
api/index.php      Única entrada de la API (rutas /v1/...)
lib/               Núcleo, acceso, permisos, registro, correo, administración, consultas, planillas Excel,
                   códigos públicos, camión en ruta y página de error
web/               Páginas del portal (lo único que se publica como sitio)
sql/               01 tablas · 02 vistas · 03 usuario de base de datos · instalador de DEV
config/            Ejemplo de configuración (la real vive en /etc/portal_clientes/config.php)
docs/              API, despliegue y condiciones de uso
herramientas/      sellar.php: sella las páginas con la versión vigente del portal
tests/             Pruebas de la API (253 casos)
cron/              aviso_geocercas.php: lista semanal de obras sin geocerca, para comercial
```

En SGO viven dos piezas del portal:

- `modules/portal_clientes/`: la pantalla de administración.
- `cron/cron_portal_cxc.php`: la copia del estado de cuenta desde KAME. Va en SGO porque es SGO quien tiene
  las credenciales de KAME; el portal nunca las ve.

## Reglas que no se rompen

1. **Solo lectura sobre SGO.** La API usa un usuario de base de datos propio (`portal_api`) que solo
   puede leer las vistas `portal_v_*` y escribir en las tablas `portal_*`.
2. **Nada de costos, márgenes ni fórmulas.** Las vistas son el contrato: lo que no está en una vista
   no existe para el portal. Antes de agregar una columna a una vista, revisar que sea información
   que el cliente ya conoce por sus documentos.
3. **Cada consulta se filtra por cliente y por obra** con `pc_contexto()` y `pc_filtro_obras()`.
   Ningún recurso arma su propio filtro.
4. **Una persona puede tener varios clientes, pero trabaja con uno a la vez.** Sus accesos van en
   `portal_usuario_cliente`, cada uno con su rol, obras y temas. La sesión guarda con qué cliente
   está trabajando y ninguna respuesta junta datos de dos clientes.
5. **Los montos de los documentos son los oficiales o no se muestran.** Vienen del Registro de Ventas del
   SII. Nunca se calculan desde las líneas de SGO: no calzan con el documento.
6. **Las claves y los tokens nunca se guardan ni se registran en claro.** En la base solo quedan
   huellas (`password_hash` y SHA-256).
7. **Todo acceso queda en el registro** (`portal_log_acceso`), que la API no puede modificar ni borrar.
8. **"No existe" y "no es tuyo" responden lo mismo**, siempre con `pc_error(404, 'no_existe', ...)`.
   Esa respuesta alimenta el freno de rastreo (`lib/log.php`): quien prueba códigos uno por uno queda
   en pausa, después bloqueado, y Hormimax recibe el aviso. Un recurso nuevo que busque por código
   debe responder igual, nunca con un mensaje distinto para lo ajeno.
9. **Hacia afuera no sale ningún número de SGO.** Todo identificador que entrega la API (documento,
   obra, cliente, línea) va como código público con `pc_codigo()`, y todo el que recibe se lee con
   `pc_codigo_id()` o `pc_codigo_filtro()` (`lib/codigos.php`). Los números de SGO son correlativos:
   se pueden recorrer y dejan estimar cuánto vende Hormimax. La llave `clave_codigos` no se cambia
   después de salir a producción.
10. **Quien abre una dirección en el navegador nunca ve un texto técnico.** Si falla, ve la página de
    error del portal (`web/404.html`, que la API usa como plantilla desde `lib/pagina.php`), y esa
    página no dice por qué falló. `web/404.html` lleva sus estilos adentro: no debe depender de
    `portal.css` ni de `portal.js`, porque se entrega desde cualquier dirección.

## Desarrollo

```bash
# Instalar en DEV (crea tablas, vistas, usuario de base de datos y /etc/portal_clientes/config.php)
sudo php -d short_open_tag=1 sql/instalar_dev.php

# Correr las pruebas (crean sus propios datos "PRUEBA-..." y los borran al terminar)
php -d short_open_tag=1 tests/probar_api.php
```

- Portal en DEV: `http://127.0.0.1/portal_clientes/web/`
- API en DEV: `http://127.0.0.1/portal_clientes/api/index.php/v1`
- Administración en DEV: `http://127.0.0.1/sgo2/modules/portal_clientes/portal_clientes.php` (estando en Hormimax)

### Al cambiar cualquier archivo de web/

```bash
php herramientas/sellar.php
```

El navegador guarda las páginas, `portal.js` y `portal.css`, y los sigue usando aunque hayan cambiado.
El sellado lo resuelve de dos formas:

- Las páginas piden el código con un sello (`portal.js?v=...`) que es la huella de su contenido: si
  cambia, cambia la dirección y el navegador baja el archivo nuevo.
- Cada página lleva la versión del portal y, al abrirse, la compara con `web/version.json`. Si quedó
  atrás, se vuelve a pedir sola al servidor.

Si se olvida sellar, las pruebas fallan.

### Tema claro y tema oscuro

El portal parte en tema claro. Cada persona puede pasarse al oscuro con el botón de la luna o el sol
(arriba, junto a la llave; en las pantallas de acceso va flotando en la esquina). La elección queda en
su navegador (`localStorage`, clave `pc_tema`) y se aplica antes de dibujar la página, para que no
parpadee.

Todos los colores salen de las variables de `web/assets/portal.css`: la paleta clara está en `:root`
y la oscura en `:root[data-tema="oscuro"]`. Una variable nunca se define en función de sí misma
(`--linea: var(--linea)` la deja sin valor). Al agregar una pantalla o un componente se usan esas
variables (`var(--tarjeta)`, `var(--texto)`, `var(--borde)`, etc.), nunca un color escrito a mano,
porque se vería bien en un tema y mal en el otro. Las únicas excepciones son el blanco sobre los
botones rojos y el fondo blanco detrás del logo.

### Correos en desarrollo

En desarrollo el portal **nunca le escribe al destinatario real**. Igual que `enviar_correo()` de SGO,
el correo sale por la cuenta de desarrollo (bloque `correo.dev` de la configuración), con `[DEV]` en
el asunto, y llega solo a la casilla indicada en `solo_a`. Arriba del correo se muestra a quién le
habría llegado en producción. Las direcciones `@...invalid` de las pruebas no se envían.

Documentación: [docs/API.md](docs/API.md) · [docs/DESPLIEGUE.md](docs/DESPLIEGUE.md)
