# Referencia detallada de la API — demoRendiciones

**Versión del contrato:** 1.2.0  
**Base path:** `/api/v1`  
**Swagger UI:** `/api-docs`  
**OpenAPI JSON:** `/api/openapi.json` · fuente `docs/api/openapi.json`

Esta guía describe el contrato real implementado en `src/routes/v1.js`.  
No documenta endpoints inexistentes. Para explorar interactivamente usá Swagger UI.

---

## 1. Principios

| Principio | Detalle |
|-----------|---------|
| API-first | Front e integradores consumen la misma API |
| Todo en services | Controllers finos; negocio en `*Service` |
| Multiempresa | Toda operación scoped por `companyId` / `scopeCompanyIds` del token |
| Versionado | Prefijo `/api/v1`; breaking → `/api/v2` |
| Envelope | `{ success, message, data, meta? }` |

### Autenticación

| Mecanismo | Header | Notas |
|-----------|--------|-------|
| JWT | `Authorization: Bearer <token>` | Obtenido en `POST /auth/login` |
| API Key | `X-API-Key: <key>` | Ligada a empresa; seed demo solo no-prod |

Casi todos los endpoints (excepto login y health) exigen uno de los dos.

### Roles

| Rol | Uso típico |
|-----|------------|
| `employee` | Crear/editar propias, firma, submit APSV, misiones |
| `approver` | Decidir nivel de workflow / liquidaciones persona |
| `admin` | CRUD empresas, users, templates, observe/liquidate, pay |
| `administrative` | Tarifas, cantidades, liquidación circuito Gastos |

### Health (fuera de `/api/v1`)

```
GET /health → { status: "ok", service: "demo-rendiciones", ts }
```

---

## 2. Estados

### Rendiciones (`statusId` numérico)

| Id | Nombre | Circuito |
|----|--------|----------|
| 0 | DISMISSED | Ambos |
| 1 | PENDING | **standard** |
| 2 | PENDING_PAYMENT | standard |
| 3 | PAID | standard |
| 4 | DRAFT | **viaticos_apsv** |
| 5 | SUBMITTED | APSV |
| 6 | APPROVED | APSV |
| 7 | PENDING_SETTLEMENT | APSV |
| 8 | SETTLED | APSV |
| 9 | OBSERVED | APSV |
| 10 | LIQUIDATED | APSV |

Empresas `formCode=standard` no usan 4–10 en el flujo diario.  
Settlement / observe / liquidate rechazan si `formCode ≠ viaticos_apsv`.

### Circuito Gastos / missions (string, aparte)

`DRAFT` → `PENDING` → `PENDING_AMOUNTS` / `OBSERVED` → `LIQUIDATED` (también `DISMISSED`).

---

## 3. Flujos resumidos

### Standard

```
POST /reimbursements (multipart)
  → PENDING + workflow N niveles
POST /reimbursements/:id/approve (por nivel)
  → (rechazo) DISMISSED
  → (todos OK) listo para firma
POST /reimbursements/:id/signature
  → PENDING_PAYMENT
POST /reimbursements/:id/pay
  → PAID
```

### Viáticos APSV

```
POST /reimbursements → DRAFT
POST /reimbursements/:id/update (editable)
POST /reimbursements/:id/submit → SUBMITTED + workflow
POST /reimbursements/:id/approve × N
  → APPROVED → PENDING_SETTLEMENT
POST /reimbursements/:id/settlement → SETTLED
POST /reimbursements/:id/observe → OBSERVED (opcional; re-settlement)
POST /reimbursements/:id/liquidate → LIQUIDATED
```

### Circuito Gastos

```
POST /missions → DRAFT
POST /missions/:id/submit → genera person-settlements PENDING
POST /person-settlements/:id/approve
PATCH /person-settlements/:id/quantities
POST /person-settlements/:id/liquidate
```

---

## 4. Catálogo de endpoints

### Auth

| Método | Ruta | Auth | Descripción |
|--------|------|------|-------------|
| POST | `/auth/login` | No | `{ email, password }` → `{ token, user }` |

### Companies & form-config

| Método | Ruta | Roles | Descripción |
|--------|------|-------|-------------|
| GET | `/companies` | * | `?tree=1` lista árbol del scope |
| POST | `/companies` | admin | Crear raíz o subempresa (`parentId`) |
| GET | `/companies/:id` | * | Detalle |
| PATCH | `/companies/:id` | admin | Actualizar |
| DELETE | `/companies/:id` | admin | Soft delete |
| GET | `/companies/:id/tree` | * | Subárbol |
| GET | `/companies/:id/form-config` | * | `formCode` + schemas |
| PUT | `/companies/:id/form-config` | admin | Upsert config |

### Users & menú

| Método | Ruta | Roles | Descripción |
|--------|------|-------|-------------|
| GET | `/users` | admin, approver, employee | Listado paginado |
| POST | `/users` | admin | Alta unitaria |
| POST | `/users/import` | admin | Array JSON o `{ users }` |
| POST | `/users/import/file` | admin | CSV/XLSX multipart campo `file` |
| GET | `/users/:id/menu` | admin | Menú del usuario |
| PUT | `/users/:id/menu` | admin | Actualizar menú |
| GET | `/menu-catalog` | admin | Catálogo de ítems |

**Import:** respuesta `{ created, updated, skipped, errors[{ line, reason }] }`.  
Flags: `continueOnError` (default true), `upsert` (default true).

### Reimbursements

| Método | Ruta | Notas |
|--------|------|-------|
| GET | `/reimbursements` | Filtros: statusId, userId, companyId, from, to, inbox=1, page |
| GET | `/reimbursements/export` | Mismos filtros; `format=xlsx\|csv` |
| GET | `/reimbursements/:id` | Detalle + adjuntos + workflow + lines |
| GET | `/reimbursements/:id/pdf` | PDF planillas / comprobante |
| GET | `/reimbursements/:id/events` | Historial |
| GET | `/reimbursements/:id/attachments/:attachmentId` | Descarga |
| POST | `/reimbursements` | Multipart create |
| POST | `/reimbursements/:id/update` | Multipart/JSON edit |
| POST | `/reimbursements/:id/submit` | APSV DRAFT→SUBMITTED |
| POST | `/reimbursements/:id/settlement` | APSV etapa 2 |
| POST | `/reimbursements/:id/observe` | admin → OBSERVED |
| POST | `/reimbursements/:id/liquidate` | admin → LIQUIDATED |
| POST | `/reimbursements/:id/cancel` | → DISMISSED |
| DELETE | `/reimbursements/:id` | Cancelar / borrar si DISMISSED |
| POST | `/reimbursements/:id/approve` | `{ approve, motive, level?, detailId? }` |
| POST | `/reimbursements/:id/pay` | admin + comprobantes opcionales |
| POST | `/reimbursements/:id/signature` | `{ signature }` |

#### Create multipart (campos frecuentes)

| Campo | Tipo | Notas |
|-------|------|-------|
| `date` | date | Obligatorio en standard |
| `amount` | number | Obligatorio en standard; nullable en DRAFT APSV |
| `observation` | string | Obligatorio en standard |
| `currency` | string | Default `ARS` |
| `merchantName` | string | Opcional |
| `lines` | JSON string | Array de TKTs; con `files` 1:1 recalcula cabecera |
| `companyId` | int | Default = company del token |
| `userId` | int | **Requerido con API Key** |
| `destination`, `motive`, `startAt`, `endAt`, … | APSV | Según schema |
| `files` | file[] | Máx. 10; allowlist mime |

### Workflow templates & person workflows

| Método | Ruta | Roles |
|--------|------|-------|
| GET/POST | `/workflow-templates` | * / admin |
| GET/PATCH/DELETE | `/workflow-templates/:id` | * / admin |
| GET | `/person-workflows` | admin |
| GET/PUT/DELETE | `/person-workflows/person/:userId` | admin |

### Notifications

| Método | Ruta | Descripción |
|--------|------|-------------|
| GET | `/notifications` | `?unreadOnly=1` + paginación |
| POST | `/notifications/:id/read` | Marcar leída |
| POST | `/notifications/read-all` | Todas leídas |

Email SMTP es fire-and-forget: fallo de email **no** revierte la TX de negocio (`emailStatus` en registro).

### Reports §11

| Método | Ruta | Roles |
|--------|------|-------|
| GET | `/reports` | admin, approver — catálogo |
| GET | `/reports/:code/export` | Excel scoped tenant |
| GET | `/reports/person-month` | JSON circuito Gastos |
| GET | `/reports/person-month/export` | Excel persona/mes |

Códigos típicos: `solicitudes_aprobadas`, `solicitudes_rechazadas`, `solicitudes_pendientes`, `rendiciones_pendientes`, `rendiciones_liquidadas`.

### AI

| Método | Ruta | Descripción |
|--------|------|-------------|
| POST | `/ai/analyze` | Suggest OCR; `suggestion` + `retry_suggested` |
| POST | `/ai/analyze-and-create` | Auto-create si confidence OK |

Persistencia **solo** vía `ReimbursementService`. Env: `AI_ENABLED`, `AI_PROVIDER`, `AI_MIN_CONFIDENCE`, n8n URL, etc.  
Detalle OCR: `docs/ai-scanner-contract.md`.

### Circuito Gastos

| Método | Ruta | Notas |
|--------|------|-------|
| GET/POST | `/expense-types` | Catálogo tipos |
| PUT | `/expense-types/:id` | Admin |
| GET/PUT | `/expense-types/monthly-rates` | admin/administrative |
| GET | `/delegations` | Catálogo |
| GET/POST | `/missions` | Listar / crear DRAFT |
| GET | `/missions/:id` | Detalle |
| POST | `/missions/:id/update` | Solo DRAFT |
| POST | `/missions/:id/submit` | A aprobación |
| GET | `/person-settlements` | Filtros status/year/month |
| GET | `/person-settlements/:id` | Detalle |
| POST | `/person-settlements/:id/approve` | `{ approve, motive }` |
| PATCH | `/person-settlements/:id/quantities` | `{ lines:[{ expenseTypeId, quantity }] }` |
| POST | `/person-settlements/:id/attachments` | Multipart |
| GET | `/person-settlements/:id/attachments/:attachmentId` | Descarga |
| POST | `/person-settlements/:id/observe` | Observar |
| POST | `/person-settlements/:id/liquidate` | Liquidar |
| POST | `/person-settlements/:id/unliquidate` | Deshacer |
| GET | `/person-settlements/:id/pdf` | PDF |
| GET | `/persons` | Personas del circuito |
| POST | `/signatures/certify` | Stub de firma |

---

## 5. Errores HTTP habituales

| Código | Cuándo |
|--------|--------|
| 400 | Validación / regla de negocio (estado incorrecto, sin plantilla, etc.) |
| 401 | Sin token / API Key inválida |
| 403 | Rol insuficiente o `companyId` fuera de scope (IDOR tenant) |
| 404 | Recurso inexistente o fuera de alcance |
| 422 | OCR ilegible (AI) |
| 503 | Proveedor AI deshabilitado / no configurado |

---

## 6. Ejemplos mínimos

### Login

```http
POST /api/v1/auth/login
Content-Type: application/json

{ "email": "empleado@sur.demo", "password": "Demo123!" }
```

### Crear rendición standard

```http
POST /api/v1/reimbursements
Authorization: Bearer <token>
Content-Type: multipart/form-data

date=2026-08-01
amount=1500.50
observation=Almuerzo cliente
files=<comprobante.jpg>
```

### Aprobar nivel

```http
POST /api/v1/reimbursements/42/approve
Authorization: Bearer <token>
Content-Type: application/json

{ "approve": true }
```

### Import personas

```http
POST /api/v1/users/import
Authorization: Bearer <token>
Content-Type: application/json

{
  "upsert": true,
  "continueOnError": true,
  "users": [
    {
      "email": "nuevo@sur.demo",
      "firstName": "Nuevo",
      "lastName": "Empleado",
      "role": "employee",
      "companyId": 2,
      "password": "Demo123!"
    }
  ]
}
```

---

## 7. Seed / credenciales demo

Ver `database/seeds/README.md`. Password demo documentada: `Demo123!`.  
API Key demo holding (solo no-prod): documentada en seed / wiki.

---

## 8. Artefactos relacionados

| Artefacto | Ruta |
|-----------|------|
| OpenAPI 3.0.3 | `docs/api/openapi.json` |
| Swagger UI | `/api-docs` |
| Wiki técnica | `docs/wiki.html` |
| Postman | `docs/postman/demoRendiciones.postman_collection.json` |
| Contrato OCR | `docs/ai-scanner-contract.md` |
| DER | `docs/schema.dbml` |
