# Portal de Gestión POS — `/manager`

Documentación del requerimiento, arquitectura, modelo de datos y API del portal de
gestión del POS (punto de venta / facturación electrónica) construido sobre la API
de Hacienda de CRLibre.

---

## 1. Objetivo

Construir un portal web (`React`) servido en la ruta **`/manager`** del proyecto, que
permita a un **administrador** del POS:

- Iniciar sesión.
- Ver y gestionar todas las APIs / módulos del backend (catálogo de endpoints).
- Ver **todo**: todos los clientes, sus usuarios y todas las facturas.
- **Crear usuarios para clientes** que, al iniciar sesión, **solo puedan ver sus
  propias facturas y sus estados**.

Es decir, dos roles claramente separados:

| Rol | Qué puede ver / hacer |
|---|---|
| **Admin** | Login, dashboard, catálogo de APIs, CRUD de clientes (y sus usuarios), ver todas las facturas de todos los clientes, cambiar estados. |
| **Cliente** | Login, ver **solo** sus facturas y el estado de cada una. |

---

## 2. Contexto y hallazgos del backend existente

La API usa el patrón `?w=<módulo>&r=<ruta>` con `sessionKey` para autenticación.
Se identificaron **115 endpoints** repartidos entre:

- **Módulos core** (`api/modules/`): `users`, `cala`, `crypto`, `db`, `files`,
  `geoloc`, `wirez` (30 rutas).
- **Módulos contrib** (`api/contrib/`): `facturador`, `genXML`, `send`, `token`,
  etc. (85 rutas).

### 2.1 Arquitectura multi-tenant del `facturador`

El módulo `facturador` implementa un modelo multi-tenant **por prefijo de tabla**:
cada cliente/empresa (`idMasterUser`) tiene su propio juego de tablas creadas a
partir de plantillas `master_*`:

```
{idMasterUser}_master_users
{idMasterUser}_master_sessions
{idMasterUser}_master_vouchers
{idMasterUser}_master_receiver
{idMasterUser}_master_sucursales
{idMasterUser}_master_terminales
{idMasterUser}_master_consecutive
{idMasterUser}_master_config_companny
...
```

Estas se generan con el endpoint `copy_master_tables`, que hace
`CREATE TABLE {id}_master_* LIKE master_*`.

### 2.2 Problema detectado (importante)

En este fork, **varias tablas plantilla `master_*` no existen** (solo hay stubs con
la PK: `master_companyusers`, `master_rol`, `master_permission`, `master_claves`,
`master_inventario`) y **faltan por completo** las que `copy_master_tables` necesita
(`master_vouchers`, `master_receiver`, `master_sucursales`, `master_terminales`,
`master_consecutive`, `master_config_companny`, `master_logs`, etc.).

Consecuencia: `facturador` no es funcional tal cual está en este fork.

### 2.3 Decisión de diseño

Para no acoplar el portal a un módulo incompleto, el portal **`manager`** se
construye como un módulo **autocontenido y limpio**:

- **Reutiliza** el módulo core `users` (tabla `users` + `sessions`) para la
  autenticación.
- **Añade** un pequeño esquema propio (`manager_clients`, `manager_accounts`,
  `manager_invoices`) que modela el POS sin depender del `facturador`.

Esto permite arrancar hoy y, en el futuro, integrar el flujo real de factura
electrónica (genXML → firma → envío a Hacienda) sobre las mismas tablas.

---

## 3. Modelo de datos

Se añaden 3 tablas nuevas (script `recursos/sql/manager.sql`):

### `manager_clients`
Cada **cliente del POS** (la empresa que recibe el servicio de facturación).

| Columna | Tipo | Descripción |
|---|---|---|
| `idClient` | INT PK AI | Identificador del cliente |
| `idUser` | INT UNIQUE | FK lógica a `users.idUser` (login del cliente) |
| `razonSocial` | VARCHAR(255) | Razón social |
| `nombreComercial` | VARCHAR(255) | Nombre comercial |
| `cedula` | VARCHAR(30) | Identificación |
| `email` | VARCHAR(100) | Correo |
| `telefono` | VARCHAR(30) | Teléfono |
| `status` | TINYINT | 1 activo / 0 inactivo |
| `createdAt` | TIMESTAMP | Fecha de alta |

### `manager_accounts`
Mapea un usuario core a un **rol** (admin o cliente) y, para clientes, a su `idClient`.

| Columna | Tipo | Descripción |
|---|---|---|
| `idAccount` | INT PK AI | |
| `idUser` | INT UNIQUE | FK lógica a `users.idUser` |
| `role` | ENUM('admin','client') | Rol |
| `idClient` | INT NULL | FK a `manager_clients.idClient` (NULL para admin) |
| `status` | TINYINT | 1 activo / 0 inactivo |
| `createdAt` | TIMESTAMP | |

### `manager_invoices`
Las **facturas / comprobantes** del cliente, con su estado.

| Columna | Tipo | Descripción |
|---|---|---|
| `idInvoice` | INT PK AI | |
| `idClient` | INT FK | A qué cliente pertenece |
| `clave` | VARCHAR(50) | Clave numérica de Hacienda |
| `consecutivo` | VARCHAR(30) | Consecutivo |
| `tipoDocumento` | VARCHAR(10) | FE, NC, ND, TE, ... |
| `estado` | VARCHAR(30) | Estado de la factura |
| `total` | DECIMAL(18,2) | Monto |
| `moneda` | VARCHAR(3) | CRC / USD |
| `respuestaMH` | VARCHAR(50) | Respuesta de Hacienda |
| `fechaEmision` | DATETIME | Fecha de emisión |
| `createdAt` | TIMESTAMP | Fecha de registro |

**Estados de factura** (convención usada por la UI):

`pendiente`, `enviado`, `aceptado`, `rechazado`, `error`.

---

## 4. API del módulo `manager`

Módulo ubicado en `api/contrib/manager/`. Se invoca como:

```
GET/POST /api.php?w=manager&r=<ruta>&sessionKey=...&<params>
```

### Endpoints

| `r` | Acceso | Params | Retorna |
|---|---|---|---|
| `manager_login` | open | `userName`, `pwd` | `{sessionKey, role, idClient, idUser, fullName}` |
| `manager_logout` | auth | `sessionKey` | ok |
| `manager_me` | auth | `sessionKey` | identidad del usuario actual (role + cliente) |
| `manager_dashboard` | **admin** | — | `{totalClients, totalInvoices, byStatus, recent}` |
| `manager_api_catalog` | **admin** | — | lista de módulos + rutas (los 115 endpoints) |
| `manager_list_clients` | **admin** | — | lista de clientes (+ usuario + conteo de facturas) |
| `manager_create_client` | **admin** | `razonSocial, nombreComercial, cedula, email, telefono, userName, pwd` | credenciales del cliente creado |
| `manager_update_client` | **admin** | `idClient`, campos a actualizar | ok |
| `manager_delete_client` | **admin** | `idClient` | soft-delete (status=0) |
| `manager_list_invoices` | **admin** | `idClient?` (filtro opcional) | todas las facturas (o las de un cliente) |
| `manager_create_invoice` | **admin** | `idClient, consecutivo, clave, tipoDocumento, estado, total, ...` | factura creada |
| `manager_update_invoice_status` | **admin** | `idInvoice, estado, respuestaMH?` | ok |
| `manager_client_invoices` | **cliente** | — | **solo** las facturas del cliente autenticado |

### Control de acceso

- `manager_openAccess` → siempre permite.
- `manager_adminAccess` → valida `sessionKey` en `sessions`, luego exige
  `manager_accounts.role = 'admin'`.
- `manager_clientAccess` → valida `sessionKey`, exige `role = 'client'` y **fija
  `idClient` automáticamente desde la sesión** (no se confía en el parámetro del
  cliente). Así se garantiza el aislamiento: un cliente jamás ve facturas ajenas.

> Nota de seguridad: en los endpoints de cliente, el `idClient` **nunca** se toma
> del request; se deriva de la sesión autenticada.

---

## 5. Frontend (React + Vite)

App SPA en `manager/`, servida en `/manager` (misma origin que `api.php`).

- **Vite + React** + `react-router-dom` (routing por **hash** para no requerir
  config de rewrite en Apache).
- `base: '/manager/'` para que los assets resuelvan bajo `/manager`.

### Estructura

```
manager/
├─ package.json
├─ vite.config.js
├─ index.html
└─ src/
   ├─ main.jsx
   ├─ App.jsx            (router + guard de sesión)
   ├─ api.js             (cliente HTTP hacia /api.php)
   ├─ auth.jsx           (contexto de autenticación, guarda sessionKey en localStorage)
   ├─ index.css
   └─ pages/
      ├─ Login.jsx
      ├─ AdminLayout.jsx
      ├─ Dashboard.jsx      (estadísticas)
      ├─ ApiCatalog.jsx     (ver/gestionar APIs — catálogo de endpoints)
      ├─ Clients.jsx        (listar + crear clientes y sus usuarios)
      ├─ Invoices.jsx       (todas las facturas, filtrar por cliente, cambiar estado)
      └─ ClientPortal.jsx   (solo sus facturas y estados)
```

### Flujos

- **Login** → `manager_login` → guarda `sessionKey` + `role` + `idClient` en
  `localStorage`.
- **Admin** → `Dashboard`, `ApiCatalog`, `Clients`, `Invoices`.
- **Cliente** → `ClientPortal` (facturas propias con badge de estado).

---

## 6. Cómo levantar, compilar y probar

```bash
# 1) Aplicar el esquema (una sola vez)
docker exec -i crlibre-mariadb mysql -uroot -prootpwd api_hacienda_db < recursos/sql/manager.sql

# 2) Crear el usuario administrador (una sola vez) — ver sección 7
docker exec crlibre-app php /var/www/html/api/contrib/manager/seed_admin.php

# 3) Compilar el frontend (requiere Node 18+; se usa un contenedor desechable)
docker run --rm -v "$(pwd)/manager:/app" -w /app node:20-alpine sh -c "npm install && npm run build"

# 4) Levantar (el compose ya monta manager/dist en /manager)
docker compose up -d --build

# 5) Abrir
#    http://localhost:8081/manager/
```

En desarrollo del frontend (hot reload, requiere Node 18+ instalado):

```bash
cd manager && npm install && npm run dev   # Vite proxya /api.php a http://localhost:8081
```

---

## 7. Administrador inicial

El módulo incluye `seed_admin.php` que crea (si no existe) un usuario core con rol
admin. Credenciales por defecto (cambiar tras el primer login):

- **userName:** `admin`
- **pwd:** `admin123` (definible por variable de entorno `MANAGER_ADMIN_PWD`)

---

## 8. Próximos pasos / Roadmap

1. Integrar el flujo real de factura electrónica (genXML → firmar → `send` a
   Hacienda) para que `manager_invoices` se alimente desde los comprobantes reales.
2. Módulo de receptores/clientes finales por cada cliente del POS.
3. Roles granulares por empresa (varios usuarios por cliente, no solo uno).
4. Filtros, paginación y exportación (CSV/PDF) de facturas.
5. Restablecimiento de contraseña y bloqueo de cuentas.
