Initial commit

This commit is contained in:
2026-01-12 08:17:17 -07:00
commit de5b6feef4
104 changed files with 12925 additions and 0 deletions

547
docs/api-contract.md Normal file
View File

@@ -0,0 +1,547 @@
# API Contract - ServiceManagerWeb
# Mesa de Ayuda B2B - Especificación de Endpoints
## Base URL
- Desarrollo: `http://localhost:8000`
- Producción: `https://api.servicemanager.aduanasoft.com`
## Versionado
- Todos los endpoints tienen prefijo `/v1/`
- Versionado en URL path (no headers)
## Autenticación
- JWT Bearer Token en header `Authorization: Bearer <token>`
- Refresh token para renovación automática
## Headers Estándar
```
Authorization: Bearer <jwt_token>
Content-Type: application/json
X-Tenant-ID: <tenant_uuid> # Requerido para endpoints multi-tenant
X-Correlation-ID: <uuid> # Opcional para tracking
Accept-Language: es-ES # Para internacionalización
```
## Responses Estándar
### Éxito (2xx)
```json
{
"success": true,
"data": { ... },
"message": "Operación exitosa",
"metadata": {
"page": 1,
"per_page": 20,
"total": 150,
"total_pages": 8
}
}
```
### Error (4xx/5xx)
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Datos inválidos",
"details": [
{
"field": "email",
"message": "Email inválido"
}
]
},
"correlation_id": "uuid"
}
```
---
## DOMINIO: AUTH
### POST /v1/auth/login
**Descripción**: Autenticación de usuario
**Público**: Sí
**Request Body**:
```json
{
"email": "user@example.com",
"password": "password123",
"tenant_slug": "aduanasoft-demo",
"totp_code": "123456" // opcional, solo si 2FA activado
}
```
**Response 200**:
```json
{
"success": true,
"data": {
"access_token": "jwt_token",
"refresh_token": "refresh_token",
"expires_in": 3600,
"user": {
"id": "uuid",
"email": "user@example.com",
"first_name": "Juan",
"last_name": "Pérez",
"role": "AGENT",
"tenant": {
"id": "uuid",
"name": "Aduanasoft Demo",
"slug": "aduanasoft-demo"
}
}
}
}
```
### POST /v1/auth/refresh
**Descripción**: Renovar access token
**Público**: Sí
**Request Body**:
```json
{
"refresh_token": "refresh_token"
}
```
### POST /v1/auth/logout
**Descripción**: Cerrar sesión (revoca refresh token)
**Autenticado**: Sí
### GET /v1/auth/me
**Descripción**: Información del usuario actual
**Autenticado**: Sí
### PUT /v1/auth/me
**Descripción**: Actualizar perfil propio
**Autenticado**: Sí
**Request Body**:
```json
{
"first_name": "Juan",
"last_name": "Pérez",
"language": "es",
"timezone": "America/Mexico_City",
"notifications_email": true
}
```
### POST /v1/auth/change-password
**Descripción**: Cambiar contraseña
**Autenticado**: Sí
### POST /v1/auth/2fa/setup
**Descripción**: Configurar 2FA (solo roles internos)
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER, AGENT, AUDITOR
### POST /v1/auth/2fa/verify
**Descripción**: Verificar código 2FA durante setup
**Autenticado**: Sí
---
## DOMINIO: TENANTS
### GET /v1/tenants/current
**Descripción**: Información del tenant actual
**Autenticado**: Sí
### PUT /v1/tenants/current
**Descripción**: Actualizar tenant (solo CLIENT_ADMIN/ADMIN)
**Autenticado**: Sí, Roles: CLIENT_ADMIN, ADMIN
### GET /v1/tenants (solo ADMIN)
**Descripción**: Listar todos los tenants
**Autenticado**: Sí, Roles: ADMIN
### POST /v1/tenants (solo ADMIN)
**Descripción**: Crear nuevo tenant
**Autenticado**: Sí, Roles: ADMIN
---
## DOMINIO: USERS
### GET /v1/users
**Descripción**: Listar usuarios del tenant
**Autenticado**: Sí
**Roles**: Todos (filtros por rol)
**Query Params**:
```
?page=1&per_page=20&role=AGENT&is_active=true&search=juan
```
**Response 200**:
```json
{
"success": true,
"data": [
{
"id": "uuid",
"email": "agent@example.com",
"first_name": "Juan",
"last_name": "Agente",
"role": "AGENT",
"is_active": true,
"email_verified": true,
"last_login": "2024-01-15T10:30:00Z",
"created_at": "2024-01-01T00:00:00Z"
}
],
"metadata": {
"page": 1,
"per_page": 20,
"total": 150,
"total_pages": 8
}
}
```
### POST /v1/users
**Descripción**: Crear usuario
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER, CLIENT_ADMIN
**Request Body**:
```json
{
"email": "nuevo@example.com",
"first_name": "Nuevo",
"last_name": "Usuario",
"role": "AGENT",
"password": "temporal123", // opcional, se genera automáticamente
"send_welcome_email": true
}
```
### GET /v1/users/{user_id}
**Descripción**: Obtener usuario específico
**Autenticado**: Sí
### PUT /v1/users/{user_id}
**Descripción**: Actualizar usuario
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER, CLIENT_ADMIN
### DELETE /v1/users/{user_id}
**Descripción**: Desactivar usuario (soft delete)
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER, CLIENT_ADMIN
---
## DOMINIO: TICKETS
### GET /v1/tickets
**Descripción**: Listar tickets con filtros
**Autenticado**: Sí
**Query Params**:
```
?page=1&per_page=20
&status=NEW,IN_PROGRESS
&priority=HIGH,URGENT
&assigned_to=uuid
&created_by=uuid
&category_id=uuid
&search=problema+conexion
&sort=created_at_desc
&date_from=2024-01-01
&date_to=2024-01-31
```
**Response 200**:
```json
{
"success": true,
"data": [
{
"id": "uuid",
"ticket_number": "TKT-2024-000001",
"subject": "Problema de conexión",
"status": "IN_PROGRESS",
"priority": "HIGH",
"category": {
"id": "uuid",
"name": "Soporte Técnico"
},
"created_by": {
"id": "uuid",
"first_name": "Juan",
"last_name": "Cliente"
},
"assigned_to": {
"id": "uuid",
"first_name": "Ana",
"last_name": "Soporte"
},
"sla_response_due": "2024-01-15T12:00:00Z",
"sla_resolution_due": "2024-01-16T10:00:00Z",
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-15T11:30:00Z"
}
],
"metadata": {
"page": 1,
"per_page": 20,
"total": 150,
"total_pages": 8
}
}
```
### POST /v1/tickets
**Descripción**: Crear nuevo ticket
**Autenticado**: Sí
**Request Body**:
```json
{
"subject": "Problema de conexión con el sistema",
"description": "Descripción detallada del problema...",
"priority": "HIGH",
"category_id": "uuid",
"affected_system_id": "uuid",
"attachments": [
{
"filename": "screenshot.png",
"content_type": "image/png",
"content_base64": "base64_data"
}
]
}
```
**Response 201**:
```json
{
"success": true,
"data": {
"id": "uuid",
"ticket_number": "TKT-2024-000001",
"subject": "Problema de conexión con el sistema",
"status": "NEW",
"sla_response_due": "2024-01-15T12:00:00Z",
"created_at": "2024-01-15T10:00:00Z"
}
}
```
### GET /v1/tickets/{ticket_id}
**Descripción**: Obtener ticket completo con comentarios
**Autenticado**: Sí
**Response 200**:
```json
{
"success": true,
"data": {
"id": "uuid",
"ticket_number": "TKT-2024-000001",
"subject": "Problema de conexión",
"description": "Descripción completa...",
"status": "IN_PROGRESS",
"priority": "HIGH",
"category": {...},
"affected_system": {...},
"created_by": {...},
"assigned_to": {...},
"attachments": [...],
"comments": [
{
"id": "uuid",
"content": "Comentario del ticket...",
"author": {...},
"is_internal": false,
"attachments": [...],
"created_at": "2024-01-15T11:00:00Z"
}
],
"status_history": [...],
"sla_metrics": {
"response_due": "2024-01-15T12:00:00Z",
"resolution_due": "2024-01-16T10:00:00Z",
"first_response_at": "2024-01-15T11:15:00Z",
"response_sla_met": true,
"resolution_sla_met": null
},
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-15T11:30:00Z"
}
}
```
### PUT /v1/tickets/{ticket_id}
**Descripción**: Actualizar ticket (estado, asignación, etc.)
**Autenticado**: Sí
**Request Body**:
```json
{
"status": "IN_PROGRESS",
"assigned_to": "uuid",
"priority": "URGENT",
"comment": "Escalando por alta prioridad"
}
```
### POST /v1/tickets/{ticket_id}/comments
**Descripción**: Agregar comentario al ticket
**Autenticado**: Sí
**Request Body**:
```json
{
"content": "Comentario con solución propuesta...",
"is_internal": false,
"attachments": [
{
"filename": "solution.pdf",
"content_type": "application/pdf",
"content_base64": "base64_data"
}
]
}
```
### PUT /v1/tickets/{ticket_id}/rating
**Descripción**: Calificar ticket resuelto (solo cliente)
**Autenticado**: Sí, Roles: CLIENT_ADMIN, CLIENT_USER
**Request Body**:
```json
{
"rating": 5,
"comment": "Excelente atención y resolución rápida"
}
```
---
## DOMINIO: CATEGORIES & SYSTEMS
### GET /v1/categories
**Descripción**: Listar categorías del tenant
**Autenticado**: Sí
### POST /v1/categories (solo staff interno)
**Descripción**: Crear categoría
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER
### GET /v1/affected-systems
**Descripción**: Listar sistemas afectados
**Autenticado**: Sí
---
## DOMINIO: NOTIFICATIONS
### GET /v1/notifications/templates (solo staff interno)
**Descripción**: Listar templates de email
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER
### POST /v1/notifications/test-email (solo ADMIN)
**Descripción**: Enviar email de prueba
**Autenticado**: Sí, Roles: ADMIN
---
## DOMINIO: REPORTS & ANALYTICS
### GET /v1/reports/dashboard
**Descripción**: Métricas del dashboard
**Autenticado**: Sí
**Response 200**:
```json
{
"success": true,
"data": {
"tickets": {
"total": 150,
"new": 12,
"in_progress": 45,
"waiting_customer": 8,
"resolved_today": 15
},
"sla": {
"response_rate": 95.5,
"resolution_rate": 87.2
},
"agents": {
"active": 8,
"avg_load": 5.6
},
"csat": {
"average": 4.2,
"total_responses": 89
}
}
}
```
### GET /v1/reports/tickets
**Descripción**: Reporte de tickets con filtros
**Autenticado**: Sí
---
## DOMINIO: AUDIT
### GET /v1/audit/logs (solo AUDITOR/ADMIN)
**Descripción**: Consultar logs de auditoría
**Autenticado**: Sí, Roles: AUDITOR, ADMIN
**Query Params**:
```
?page=1&per_page=50
&action=ticket.create,ticket.assign
&user_id=uuid
&resource_type=ticket
&date_from=2024-01-01
&date_to=2024-01-31
```
---
## CÓDIGOS DE ERROR ESTÁNDAR
- `400` - Bad Request (datos inválidos)
- `401` - Unauthorized (no autenticado)
- `403` - Forbidden (sin permisos)
- `404` - Not Found (recurso no encontrado)
- `409` - Conflict (recurso duplicado)
- `422` - Unprocessable Entity (validación fallida)
- `429` - Too Many Requests (rate limit)
- `500` - Internal Server Error
## RATE LIMITING
- Auth endpoints: 10 req/min por IP
- API endpoints: 100 req/min por usuario
- File uploads: 5 req/min por usuario
## PAGINACIÓN
- Default: `per_page=20`, `max=100`
- Links de navegación en metadata
- Total count incluido cuando sea eficiente
## ORDENAMIENTO
Formato: `?sort=field_direction`
- `created_at_desc` (default)
- `updated_at_desc`
- `priority_desc`
- `status_asc`
## BÚSQUEDA
- Full-text search en `subject` y `description`
- Búsqueda por número de ticket exacto
- Filtros combinables con AND lógico

96
docs/database-schema.md Normal file
View File

@@ -0,0 +1,96 @@
# Modelo de Datos - ServiceManagerWeb
## Resumen del Esquema
El sistema utiliza PostgreSQL con un diseño multi-tenant donde cada cliente (tenant) tiene sus datos aislados pero comparte la misma estructura de base de datos.
## Dominios Principales
### 1. **TENANTS** - Multi-tenancy
- `tenants`: Organizaciones cliente
- Cada tenant tiene configuraciones propias (usuarios max, storage, tipos de archivo)
### 2. **AUTH** - Autenticación y Autorización
- `users`: Usuarios del sistema (internos y clientes)
- `refresh_tokens`: Tokens de refresco para JWT
- Roles: ADMIN, SUPPORT_MANAGER, AGENT, AUDITOR, CLIENT_ADMIN, CLIENT_USER
- Soporte para 2FA (TOTP) opcional para staff interno
### 3. **TICKETS** - Core del Negocio
- `tickets`: Tickets de soporte principales
- `ticket_categories`: Categorías personalizables por tenant
- `affected_systems`: Sistemas afectados por tenant
- `ticket_comments`: Conversación en tickets
- `ticket_attachments`: Archivos adjuntos
- `ticket_status_history`: Historial de cambios de estado
Estados de ticket: NEW → TRIAGE → IN_PROGRESS → WAITING_CUSTOMER → RESOLVED → CLOSED
Prioridades: LOW, MEDIUM, HIGH, URGENT
### 4. **NOTIFICATIONS** - Comunicaciones
- `email_templates`: Templates personalizables por tenant
- `notification_logs`: Historial de emails enviados
- Soporte para variables dinámicas en templates
### 5. **AUDIT** - Bitácora y Compliance
- `audit_logs`: Registro completo de acciones
- Tracking con correlation_id para requests
- Almacena cambios antes/después en JSON
## Características Técnicas
### Índices Estratégicos
- Optimizados para queries por tenant
- Indices compuestos para búsquedas frecuentes
- Índices en campos de fecha para reportes
### Constraints y Validación
- CHECK constraints para valores enum
- Foreign keys con CASCADE apropiados
- UNIQUE constraints compuestos (tenant_id + campo)
### Triggers Automáticos
- `updated_at` se actualiza automáticamente
- Preparado para audit logging automático
### Multi-tenancy
- Todos los datos principales tienen `tenant_id`
- Aislamiento a nivel de aplicación
- Configuraciones por tenant (SLA, categorías, etc.)
## Numeración de Tickets
Formato: `TKT-YYYY-NNNNNN` (ej: TKT-2026-000001)
- Único por tenant
- Año incluido para fácil organización
- 6 dígitos con ceros a la izquierda
## SLA Tracking
- `sla_response_due`: Tiempo límite para primera respuesta
- `sla_resolution_due`: Tiempo límite para resolución
- `first_response_at`: Timestamp de primera respuesta
- Configurables por categoría
## Almacenamiento de Archivos
- Metadata en BD, archivos en filesystem/S3
- Checksums MD5 y SHA256 para integridad
- Validación de tipos MIME
- Límites de tamaño por tenant
## Datos Iniciales
El schema incluye:
- Tenant demo para desarrollo
- Usuario admin por defecto
- Categorías base (Soporte Técnico, Consulta Comercial, Incidente Crítico)
- Sistemas base (Plataforma Web, API, Base de Datos)
- Templates de email básicos
## Escalabilidad
- Preparado para sharding por tenant_id
- Partitioning por fecha en audit_logs
- Índices optimizados para paginación
- Soft deletes donde aplique