feat: Add SaaS products architecture and alignment analysis

Analysis and Documentation:
- Add ANALISIS-ALINEACION-WORKSPACE-2025-12-08.md with comprehensive gap analysis
- Document SIMCO v3.2 system with 20+ directives
- Identify alignment gaps between orchestration and projects

New SaaS Products Structure:
- Create apps/products/pos-micro/ - Ultra basic POS (~100 MXN/month)
  - Target: Mexican informal market (street vendors, small stores)
  - Features: Offline-first PWA, WhatsApp bot, minimal DB (~10 tables)
- Create apps/products/erp-basico/ - Austere ERP (~300-500 MXN/month)
  - Target: SMBs needing full ERP without complexity
  - Features: Inherits from erp-core, modular pricing

SaaS Layer:
- Create apps/saas/ structure (billing, portal, admin, onboarding)
- Add README.md and CONTEXTO-SAAS.md documentation

Vertical Alignment:
- Verify HERENCIA-ERP-CORE.md exists in all verticals
- Add HERENCIA-SPECS-CORE.md to verticals
- Update orchestration inventories

Updates:
- Update WORKSPACE-STATUS.md with new products and analysis
- Update suite inventories with new structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rckrdmrd 2025-12-08 11:34:35 -06:00
parent d30fe4d644
commit 2781837d9e
97 changed files with 26437 additions and 149 deletions

View File

@ -74,6 +74,29 @@ core/
│ ├── CONTEXTO-NIVEL-SUITE-CORE.md # 🆕 Template para core de suite
│ └── CONTEXTO-NIVEL-VERTICAL.md # 🆕 Template para verticales
├── patrones/ # PATRONES DE CÓDIGO
│ ├── MAPEO-TIPOS-DDL-TYPESCRIPT.md # Mapeo PostgreSQL ↔ TypeScript
│ ├── PATRON-VALIDACION.md # Validación con class-validator/Zod
│ ├── PATRON-EXCEPTION-HANDLING.md # Manejo de errores y excepciones
│ ├── PATRON-TESTING.md # Patrones de testing
│ ├── PATRON-LOGGING.md # Logging estructurado
│ ├── PATRON-CONFIGURACION.md # Variables de entorno y config
│ ├── PATRON-SEGURIDAD.md # Seguridad y OWASP
│ ├── PATRON-PERFORMANCE.md # Optimización y caching
│ ├── PATRON-TRANSACCIONES.md # Transacciones de BD
│ ├── ANTIPATRONES.md # Lo que NUNCA hacer
│ └── NOMENCLATURA-UNIFICADA.md # Convenciones de nombres
├── impactos/ # IMPACTO DE CAMBIOS
│ ├── IMPACTO-CAMBIOS-DDL.md # Cascada de cambios en BD
│ ├── IMPACTO-CAMBIOS-BACKEND.md # Sincronización Backend↔Frontend
│ ├── IMPACTO-CAMBIOS-ENTITY.md # Cambios en Entities TypeORM
│ ├── IMPACTO-CAMBIOS-API.md # Cambios en endpoints REST
│ └── MATRIZ-DEPENDENCIAS.md # Matriz completa de dependencias
├── procesos/ # PROCESOS DE TRABAJO
│ └── ORDEN-IMPLEMENTACION.md # DDL-First, orden de capas
├── _historico/
│ └── MAPA-CONTEXTO-AGENTE.md # Trazabilidad (histórico)

View File

@ -0,0 +1,669 @@
# IMPACTO DE CAMBIOS EN API
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** OBLIGATORIA - Consultar antes de modificar endpoints
**Sistema:** SIMCO + CAPVED
---
## PROPOSITO
Documentar el impacto de modificar endpoints REST (Controllers), rutas, metodos HTTP, y contratos de API en el sistema.
---
## 1. CLASIFICACION DE CAMBIOS API
```
╔══════════════════════════════════════════════════════════════════════╗
║ TIPOS DE CAMBIOS EN API ║
╠══════════════════════════════════════════════════════════════════════╣
║ ║
║ BREAKING CHANGES (⚠️ ROMPEN CLIENTES): ║
║ • Eliminar endpoint ║
║ • Cambiar ruta de endpoint ║
║ • Cambiar metodo HTTP ║
║ • Eliminar campo de response ║
║ • Cambiar tipo de campo en response ║
║ • Agregar campo requerido a request ║
║ • Cambiar formato de error ║
║ ║
║ NON-BREAKING CHANGES (✅ COMPATIBLES): ║
║ • Agregar endpoint nuevo ║
║ • Agregar campo opcional a request ║
║ • Agregar campo a response ║
║ • Agregar nuevo codigo de error ║
║ • Mejorar mensaje de error ║
║ ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## 2. CAMBIOS EN RUTAS (BREAKING)
### 2.1 Cambiar Ruta de Endpoint
```typescript
// ANTES
@Get('users/by-email/:email')
// DESPUES
@Get('users/search/email/:email')
```
**Impacto:**
| Componente | Impacto | Accion Requerida |
|------------|---------|------------------|
| Frontend Service | ROMPE | Actualizar URL |
| Frontend Hooks | ROMPE | Actualizar llamadas |
| Documentacion | Desactualizada | Actualizar |
| Clientes externos | ROMPE | Notificar + migrar |
| Tests e2e | ROMPE | Actualizar URLs |
**Proceso de Migracion:**
```
OPCION A: Migracion Inmediata (proyectos internos)
═══════════════════════════════════════════════════════════════
1. Actualizar Frontend primero (cambiar URL)
2. Actualizar Backend (cambiar ruta)
3. Deploy coordinado
OPCION B: Deprecacion Gradual (APIs publicas)
═══════════════════════════════════════════════════════════════
1. Agregar nueva ruta (mantener vieja)
2. Marcar vieja como @Deprecated
3. Notificar clientes
4. Periodo de gracia (30-90 dias)
5. Eliminar ruta vieja
```
**Implementacion Deprecacion:**
```typescript
// Mantener ambas rutas temporalmente
@Get('users/by-email/:email')
@ApiOperation({
summary: 'Buscar por email (DEPRECATED)',
deprecated: true,
description: 'Use GET /users/search/email/:email instead'
})
async findByEmailDeprecated(@Param('email') email: string) {
return this.findByEmail(email);
}
@Get('users/search/email/:email')
@ApiOperation({ summary: 'Buscar usuario por email' })
async findByEmail(@Param('email') email: string) {
return this.userService.findByEmail(email);
}
```
### 2.2 Cambiar Prefijo de Controller
```typescript
// ANTES
@Controller('products')
// DESPUES
@Controller('catalog/products')
```
**Impacto:** TODOS los endpoints del controller cambian de ruta.
```
ANTES:
GET /api/products
POST /api/products
GET /api/products/:id
DESPUES:
GET /api/catalog/products
POST /api/catalog/products
GET /api/catalog/products/:id
```
**Checklist:**
```
[ ] 1. Buscar en frontend: grep -rn "/products" apps/
[ ] 2. Actualizar TODOS los services que usan estos endpoints
[ ] 3. Actualizar tests e2e
[ ] 4. Actualizar documentacion
[ ] 5. Verificar Swagger refleja cambios
```
---
## 3. CAMBIOS EN METODO HTTP (BREAKING)
### Cambiar Metodo
```typescript
// ANTES: Actualizar con POST
@Post(':id/update')
async update() {}
// DESPUES: Actualizar con PUT (RESTful correcto)
@Put(':id')
async update() {}
```
**Impacto Frontend:**
```typescript
// ANTES
const response = await api.post(`/users/${id}/update`, data);
// DESPUES
const response = await api.put(`/users/${id}`, data);
```
**Checklist:**
```
[ ] 1. Identificar todos los lugares que llaman al endpoint
[ ] 2. Actualizar metodo HTTP en frontend service
[ ] 3. Actualizar tests
[ ] 4. Si API publica: periodo de deprecacion
```
---
## 4. CAMBIOS EN REQUEST DTO
### 4.1 Agregar Campo Requerido (BREAKING)
```typescript
// ANTES
export class CreateOrderDto {
productId: string;
quantity: number;
}
// DESPUES - Nuevo campo requerido
export class CreateOrderDto {
productId: string;
quantity: number;
@IsNotEmpty()
deliveryAddress: string; // ← BREAKING: requerido
}
```
**Impacto:**
| Componente | Impacto | Accion |
|------------|---------|--------|
| Frontend Form | ROMPE | Agregar campo |
| Frontend Zod | ROMPE | Agregar validacion |
| Clientes existentes | ROMPE | Falla validacion |
| Tests | ROMPE | Actualizar fixtures |
**Mitigacion:**
```typescript
// OPCION 1: Hacer opcional con default
@IsOptional()
@IsString()
deliveryAddress?: string = 'Por definir';
// OPCION 2: Migrar en fases
// Fase 1: Agregar como opcional
// Fase 2: Poblar datos existentes
// Fase 3: Hacer requerido
```
### 4.2 Agregar Campo Opcional (NON-BREAKING)
```typescript
// Agregar campo opcional - NO rompe
export class CreateOrderDto {
productId: string;
quantity: number;
@IsOptional()
@IsString()
notes?: string; // ← NON-BREAKING: opcional
}
```
**Impacto:**
| Componente | Impacto | Accion |
|------------|---------|--------|
| Frontend | Ninguno inmediato | Agregar si se quiere usar |
| Clientes existentes | Ninguno | Siguen funcionando |
| Tests | Ninguno | Siguen pasando |
### 4.3 Eliminar Campo de Request (BREAKING si requerido)
```typescript
// ANTES
export class CreateUserDto {
email: string;
password: string;
legacyCode: string; // ← Se va a eliminar
}
// DESPUES
export class CreateUserDto {
email: string;
password: string;
// legacyCode eliminado
}
```
**Proceso:**
```
1. Hacer campo opcional primero (si era requerido)
2. Marcar como @Deprecated en Swagger
3. Ignorar en backend (no procesar)
4. Notificar clientes
5. Eliminar despues de periodo de gracia
```
### 4.4 Cambiar Validacion (Puede ser BREAKING)
```typescript
// ANTES: email cualquier formato
@IsString()
email: string;
// DESPUES: email debe ser valido
@IsEmail()
email: string;
```
**Impacto:** Requests que antes pasaban ahora fallan.
**Mitigacion:**
```typescript
// Agregar transformacion para casos edge
@IsEmail()
@Transform(({ value }) => value?.toLowerCase().trim())
email: string;
```
---
## 5. CAMBIOS EN RESPONSE DTO
### 5.1 Eliminar Campo de Response (BREAKING)
```typescript
// ANTES
export class UserResponseDto {
id: string;
email: string;
legacyCode: string; // ← Se elimina
}
// DESPUES
export class UserResponseDto {
id: string;
email: string;
// legacyCode eliminado
}
```
**Impacto Frontend:**
```typescript
// Si frontend usa el campo, ROMPE
const UserCard = ({ user }) => {
return <div>{user.legacyCode}</div>; // ← TypeError: undefined
};
```
**Proceso Seguro:**
```
1. Buscar uso en frontend: grep -rn "legacyCode" apps/
2. Eliminar uso en frontend primero
3. Luego eliminar de ResponseDto
4. Deploy coordinado
```
### 5.2 Agregar Campo a Response (NON-BREAKING)
```typescript
// Agregar campo - NO rompe
export class UserResponseDto {
id: string;
email: string;
createdAt: Date; // ← Nuevo campo
}
```
**Impacto:**
- Frontend puede ignorar campos nuevos
- TypeScript mostrara warning si interface no coincide
- Actualizar interface en frontend eventualmente
### 5.3 Cambiar Tipo de Campo (BREAKING)
```typescript
// ANTES
export class ProductResponseDto {
price: number; // 99
}
// DESPUES
export class ProductResponseDto {
price: string; // "99.00"
}
```
**Impacto Frontend:**
```typescript
// ANTES funcionaba
const total = product.price * quantity;
// DESPUES rompe (string * number = NaN)
const total = product.price * quantity; // NaN!
// Debe cambiar a
const total = parseFloat(product.price) * quantity;
```
### 5.4 Cambiar Estructura de Response (BREAKING)
```typescript
// ANTES: Array directo
@Get()
async findAll(): Promise<UserResponseDto[]> {
return this.users;
}
// Response: [{ id: 1 }, { id: 2 }]
// DESPUES: Objeto paginado
@Get()
async findAll(): Promise<PaginatedResponse<UserResponseDto>> {
return { data: this.users, total: 100, page: 1 };
}
// Response: { data: [...], total: 100, page: 1 }
```
**Impacto Frontend:**
```typescript
// ANTES
const users = await api.get('/users');
users.map(u => ...);
// DESPUES
const response = await api.get('/users');
response.data.map(u => ...); // Acceder a .data
```
---
## 6. CAMBIOS EN CODIGOS DE ERROR
### 6.1 Agregar Nuevo Codigo (NON-BREAKING)
```typescript
// Agregar nueva excepcion - generalmente no rompe
throw new ConflictException('Email already registered');
// HTTP 409 - nuevo codigo
```
**Impacto:** Frontend deberia manejar, pero no rompe si no lo hace.
### 6.2 Cambiar Codigo Existente (BREAKING)
```typescript
// ANTES: 400 para duplicado
throw new BadRequestException('Email exists');
// DESPUES: 409 para duplicado (correcto)
throw new ConflictException('Email exists');
```
**Impacto:** Frontend que verifica status code especifico rompe.
```typescript
// Frontend que rompe
if (error.status === 400 && error.message.includes('Email')) {
// Ya no entra aqui
}
// Frontend robusto
if (error.status === 409 || error.message.includes('Email')) {
// Maneja ambos casos
}
```
### 6.3 Cambiar Formato de Error (BREAKING)
```typescript
// ANTES
{
"statusCode": 400,
"message": "Validation failed"
}
// DESPUES
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [...]
}
}
```
**Impacto:** TODO el manejo de errores en frontend debe cambiar.
---
## 7. VERSIONADO DE API
### Cuando Usar Versionado
```
USA VERSIONADO CUANDO:
• API es consumida por clientes externos
• Cambios breaking frecuentes
• Necesitas mantener compatibilidad largo plazo
NO NECESITAS VERSIONADO CUANDO:
• Solo frontend interno consume la API
• Puedes coordinar deploys
• Equipo pequeno con comunicacion directa
```
### Implementacion de Versiones
```typescript
// Opcion 1: En URL
@Controller('v1/users')
export class UsersV1Controller {}
@Controller('v2/users')
export class UsersV2Controller {}
// Opcion 2: En Header
@Controller('users')
@ApiHeader({ name: 'Api-Version', enum: ['1', '2'] })
export class UsersController {
@Get()
findAll(@Headers('Api-Version') version: string) {
if (version === '2') {
return this.findAllV2();
}
return this.findAllV1();
}
}
```
---
## 8. CHECKLIST POR TIPO DE CAMBIO
### Agregar Endpoint Nuevo
```
[ ] 1. Crear metodo en Controller
[ ] 2. Agregar decoradores Swagger (@ApiOperation, @ApiResponse)
[ ] 3. Crear/actualizar DTOs si necesario
[ ] 4. Implementar en Service
[ ] 5. Agregar tests
[ ] 6. Actualizar Frontend service
[ ] 7. Crear Frontend hook si necesario
[ ] 8. Verificar Swagger
```
### Modificar Endpoint Existente (NON-BREAKING)
```
[ ] 1. Verificar que cambio es non-breaking
[ ] 2. Actualizar Controller
[ ] 3. Actualizar DTOs si necesario
[ ] 4. Actualizar Swagger decoradores
[ ] 5. Actualizar tests
[ ] 6. Actualizar Frontend si aprovecha nuevas features
```
### Modificar Endpoint Existente (BREAKING)
```
[ ] 1. Evaluar: ¿se puede hacer non-breaking?
[ ] 2. Buscar todos los consumidores: grep -rn "endpoint" apps/
[ ] 3. Planear estrategia de migracion
[ ] 4. Si API publica: crear version nueva, deprecar vieja
[ ] 5. Si API interna: coordinar con frontend
[ ] 6. Actualizar Frontend PRIMERO (apuntar a nuevo)
[ ] 7. Actualizar Backend
[ ] 8. Actualizar tests
[ ] 9. Deploy coordinado
[ ] 10. Eliminar codigo deprecated despues de periodo
```
### Eliminar Endpoint
```
[ ] 1. Buscar todos los usos: grep -rn "endpoint" apps/ tests/
[ ] 2. Eliminar uso en Frontend
[ ] 3. Eliminar tests del endpoint
[ ] 4. Marcar como @Deprecated (si API publica)
[ ] 5. Periodo de gracia
[ ] 6. Eliminar de Controller
[ ] 7. Limpiar Service si metodos quedan sin usar
```
---
## 9. FRONTEND: ADAPTARSE A CAMBIOS API
### Service Layer Pattern
```typescript
// Encapsular llamadas API en service
// Facilita cambiar cuando API cambia
// user.service.ts
export const userService = {
// Si cambia la ruta, solo cambiar aqui
getAll: () => api.get<User[]>('/users'),
// Si cambia estructura response
getAllPaginated: async (page: number) => {
const response = await api.get<PaginatedResponse<User>>('/users', { params: { page } });
return response.data; // Extraer data aqui
},
// Si cambia metodo HTTP
update: (id: string, data: UpdateUserDto) =>
api.put<User>(`/users/${id}`, data), // Cambiar post→put aqui
};
```
### Type Guards para Cambios de Estructura
```typescript
// Manejar diferentes versiones de response
interface UserV1 {
name: string;
}
interface UserV2 {
firstName: string;
lastName: string;
}
function isUserV2(user: UserV1 | UserV2): user is UserV2 {
return 'firstName' in user;
}
function getDisplayName(user: UserV1 | UserV2): string {
if (isUserV2(user)) {
return `${user.firstName} ${user.lastName}`;
}
return user.name;
}
```
---
## 10. MATRIZ DE DECISION
```
¿Es BREAKING CHANGE?
┌────────────┐
│ ¿Cambio de │───NO───▶ Implementar directamente
│ ruta? │ Actualizar Swagger
└────────────┘ Actualizar Frontend (opcional)
YES
┌────────────┐
│ ¿API │───NO───▶ Actualizar Frontend PRIMERO
│ publica? │ Luego actualizar Backend
└────────────┘ Deploy coordinado
YES
┌────────────────────────────────────────┐
│ 1. Crear endpoint nuevo │
│ 2. Deprecar endpoint viejo │
│ 3. Notificar clientes │
│ 4. Periodo de gracia (30-90 dias) │
│ 5. Eliminar endpoint viejo │
└────────────────────────────────────────┘
```
---
## 11. COMANDOS UTILES
```bash
# Ver todos los endpoints actuales
curl http://localhost:3000/api-json | jq '.paths | keys'
# Ver detalle de un endpoint
curl http://localhost:3000/api-json | jq '.paths["/users/{id}"]'
# Buscar uso de endpoint en frontend
grep -rn "users" apps/*/services/
grep -rn "/api/users" apps/
# Comparar Swagger entre versiones
diff <(curl -s http://localhost:3000/api-json | jq -S) \
<(curl -s http://production/api-json | jq -S)
```
---
**Version:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Guia de Impacto

View File

@ -0,0 +1,498 @@
# IMPACTO DE CAMBIOS EN BACKEND
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** OBLIGATORIA - Consultar antes de modificar Backend
**Sistema:** SIMCO + CAPVED
---
## PROPOSITO
Documentar la cascada de cambios que ocurre cuando se modifica cualquier componente del Backend (Entity, DTO, Service, Controller) y su impacto en Frontend.
---
## 1. MATRIZ DE IMPACTO POR COMPONENTE
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ CAMBIO EN BACKEND → IMPACTO EN CAPAS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ENTITY DTO SERVICE CONTROLLER FRONTEND │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ │──────▶│ │───────▶│ │───────▶│ │───────▶│ │ │
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
│ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ │
│ Refleja Valida Logica Endpoints Types │
│ DDL Input Negocio REST Zod │
│ Hooks │
│ Components │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## 2. CAMBIOS EN ENTITY
### 2.1 Agregar Campo a Entity
```typescript
// CAMBIO: Agregar campo 'phone' a UserEntity
@Column({ type: 'varchar', length: 20, nullable: true })
phone: string | null;
```
**Cascada de Cambios:**
| Capa | Archivo | Accion | Obligatorio |
|------|---------|--------|-------------|
| DDL | `XX-users.sql` | Agregar columna (DDL-First) | SI - PRIMERO |
| Entity | `user.entity.ts` | Ya agregado | SI |
| CreateDto | `create-user.dto.ts` | Agregar campo + validacion | SI |
| UpdateDto | `update-user.dto.ts` | Hereda de CreateDto | AUTO |
| ResponseDto | `user-response.dto.ts` | Agregar campo | SI |
| Service | `user.service.ts` | Sin cambios (TypeORM maneja) | NO |
| Controller | `user.controller.ts` | Sin cambios | NO |
| **Frontend** | `user.types.ts` | Agregar campo a interface | SI |
| **Frontend** | `user.schema.ts` | Agregar a Zod schema | SI |
| **Frontend** | `UserForm.tsx` | Agregar input si editable | CONDICIONAL |
**Checklist:**
```
[ ] 1. DDL tiene la columna (DDL-First)
[ ] 2. Entity refleja DDL exactamente
[ ] 3. CreateDto tiene validacion apropiada
[ ] 4. ResponseDto incluye el campo
[ ] 5. Swagger muestra campo correctamente
[ ] 6. Frontend types actualizados
[ ] 7. Frontend Zod schema actualizado
[ ] 8. Componentes de formulario actualizados (si aplica)
[ ] 9. npm run build (backend)
[ ] 10. npm run build (frontend)
[ ] 11. npm run typecheck (frontend)
```
### 2.2 Eliminar Campo de Entity
```typescript
// CAMBIO: Eliminar campo 'legacyCode' de ProductEntity
// @Column() legacyCode: string; // ELIMINADO
```
**Cascada de Cambios:**
| Capa | Archivo | Accion | Obligatorio |
|------|---------|--------|-------------|
| DDL | `XX-products.sql` | DROP COLUMN (con migracion) | SI - PRIMERO |
| Entity | `product.entity.ts` | Eliminar @Column | SI |
| CreateDto | `create-product.dto.ts` | Eliminar campo | SI |
| UpdateDto | `update-product.dto.ts` | Hereda cambio | AUTO |
| ResponseDto | `product-response.dto.ts` | Eliminar campo | SI |
| Service | `product.service.ts` | Eliminar referencias | VERIFICAR |
| Controller | `product.controller.ts` | Eliminar de queries | VERIFICAR |
| **Frontend** | `product.types.ts` | Eliminar de interface | SI |
| **Frontend** | `product.schema.ts` | Eliminar de Zod | SI |
| **Frontend** | Componentes | Eliminar referencias | SI |
**ADVERTENCIA:**
```
⚠️ ANTES DE ELIMINAR UN CAMPO:
1. Buscar TODAS las referencias en backend: grep -r "legacyCode" src/
2. Buscar TODAS las referencias en frontend: grep -r "legacyCode" apps/
3. Verificar que no hay datos importantes en la columna
4. Considerar soft-delete o campo deprecated primero
```
### 2.3 Cambiar Tipo de Campo
```typescript
// CAMBIO: price de number a Decimal
// ANTES
@Column({ type: 'integer' })
price: number;
// DESPUES
@Column({ type: 'decimal', precision: 10, scale: 2 })
price: string; // String para precision decimal
```
**Cascada de Cambios:**
| Capa | Archivo | Accion | Obligatorio |
|------|---------|--------|-------------|
| DDL | `XX-products.sql` | ALTER COLUMN type | SI - PRIMERO |
| Entity | `product.entity.ts` | Cambiar tipo TS | SI |
| CreateDto | `create-product.dto.ts` | Cambiar validador | SI |
| ResponseDto | `product-response.dto.ts` | Cambiar tipo | SI |
| Service | `product.service.ts` | Ajustar transformaciones | VERIFICAR |
| **Frontend** | `product.types.ts` | Cambiar tipo | SI |
| **Frontend** | `product.schema.ts` | Cambiar validador Zod | SI |
| **Frontend** | Componentes | Ajustar formateo/parsing | SI |
---
## 3. CAMBIOS EN DTO
### 3.1 Agregar Validacion a DTO
```typescript
// CAMBIO: Agregar validacion de formato a email
@IsEmail({}, { message: 'Email invalido' })
@MaxLength(255)
@Transform(({ value }) => value?.toLowerCase().trim())
email: string;
```
**Impacto:**
| Capa | Impacto | Accion Requerida |
|------|---------|------------------|
| Entity | Ninguno | - |
| Service | Ninguno | - |
| Controller | Ninguno (validacion automatica) | - |
| Swagger | Actualiza automaticamente | Verificar |
| **Frontend** | Debe sincronizar validacion | Actualizar Zod |
**Frontend debe reflejar:**
```typescript
// Zod schema debe coincidir con backend
const userSchema = z.object({
email: z.string()
.email('Email invalido')
.max(255)
.transform(v => v.toLowerCase().trim()),
});
```
### 3.2 Agregar Campo a ResponseDto
```typescript
// CAMBIO: Agregar campo calculado 'fullName'
@ApiProperty({ example: 'Juan Perez' })
fullName: string;
```
**Impacto:**
| Capa | Impacto | Accion Requerida |
|------|---------|------------------|
| Entity | Ninguno (campo calculado) | - |
| Service | Debe calcular el campo | Agregar logica |
| Controller | Ninguno | - |
| **Frontend** | Debe tipar el campo | Actualizar interface |
**Service debe mapear:**
```typescript
// En service
toResponseDto(entity: UserEntity): UserResponseDto {
return {
...entity,
fullName: `${entity.firstName} ${entity.lastName}`,
};
}
```
---
## 4. CAMBIOS EN SERVICE
### 4.1 Agregar Metodo al Service
```typescript
// CAMBIO: Agregar metodo de busqueda avanzada
async searchByFilters(filters: SearchFiltersDto): Promise<UserEntity[]> {
// ...
}
```
**Impacto:**
| Capa | Impacto | Accion Requerida |
|------|---------|------------------|
| Entity | Ninguno | - |
| DTO | Crear SearchFiltersDto | SI |
| Controller | Exponer endpoint | SI |
| **Frontend** | Crear servicio + hook | SI |
### 4.2 Modificar Logica de Negocio
```typescript
// CAMBIO: Agregar validacion de negocio en create
async create(dto: CreateOrderDto): Promise<OrderEntity> {
// NUEVA: Validar stock antes de crear
await this.validateStock(dto.items);
// ... resto
}
```
**Impacto:**
| Capa | Impacto | Accion Requerida |
|------|---------|------------------|
| Entity | Ninguno | - |
| DTO | Posible nuevo error response | Documentar |
| Controller | Ninguno | - |
| Swagger | Documentar nuevo error | SI |
| **Frontend** | Manejar nuevo error | SI |
**Frontend debe manejar:**
```typescript
// Hook debe manejar error especifico
const { mutate } = useCreateOrder({
onError: (error) => {
if (error.message.includes('stock')) {
toast.error('Stock insuficiente');
}
}
});
```
---
## 5. CAMBIOS EN CONTROLLER
### 5.1 Agregar Endpoint
```typescript
// CAMBIO: Agregar endpoint de exportacion
@Get('export')
@ApiOperation({ summary: 'Exportar usuarios a CSV' })
async exportToCsv(): Promise<StreamableFile> {
// ...
}
```
**Impacto Frontend:**
| Componente | Accion Requerida |
|------------|------------------|
| `user.service.ts` | Agregar metodo `exportToCsv()` |
| `useExportUsers.ts` | Crear hook (si necesario) |
| Componente UI | Agregar boton de exportar |
### 5.2 Modificar Ruta de Endpoint
```typescript
// CAMBIO: Renombrar endpoint
// ANTES: @Get('by-email/:email')
// DESPUES: @Get('search/email/:email')
```
**ADVERTENCIA - BREAKING CHANGE:**
```
⚠️ CAMBIO DE RUTA ES BREAKING CHANGE
PROCESO OBLIGATORIO:
1. Verificar que frontend no usa la ruta antigua
2. Si usa: Actualizar frontend PRIMERO
3. Considerar: Mantener ruta antigua con @Deprecated
4. Documentar en CHANGELOG
BUSCAR EN FRONTEND:
grep -r "by-email" apps/
grep -r "users/by-email" apps/
```
### 5.3 Cambiar Metodo HTTP
```typescript
// CAMBIO: De POST a PUT para update
// ANTES: @Post(':id/update')
// DESPUES: @Put(':id')
```
**Impacto Frontend:**
```typescript
// ANTES
await api.post(`/users/${id}/update`, data);
// DESPUES
await api.put(`/users/${id}`, data);
```
---
## 6. SINCRONIZACION BACKEND-FRONTEND
### 6.1 Mapeo de Tipos
| Backend (NestJS) | Frontend (TypeScript) | Notas |
|------------------|----------------------|-------|
| `string` | `string` | Directo |
| `number` | `number` | Directo |
| `boolean` | `boolean` | Directo |
| `Date` | `string` | ISO string en JSON |
| `Decimal` (string) | `string` o `number` | Parsear si necesario |
| `UUID` | `string` | Directo |
| `enum Status` | `type Status = ...` | Replicar valores |
| `T[]` | `T[]` | Directo |
| `T \| null` | `T \| null` | Directo |
### 6.2 Patron de Sincronizacion
```typescript
// BACKEND: ResponseDto define contrato
export class UserResponseDto {
@ApiProperty()
id: string;
@ApiProperty()
email: string;
@ApiProperty({ enum: UserStatus })
status: UserStatus;
@ApiProperty()
createdAt: Date; // Se serializa como ISO string
}
// FRONTEND: Interface refleja ResponseDto
export interface User {
id: string;
email: string;
status: UserStatus;
createdAt: string; // String porque viene de JSON
}
// FRONTEND: Enum replicado
export enum UserStatus {
ACTIVE = 'active',
INACTIVE = 'inactive',
}
// FRONTEND: Zod refleja CreateDto
export const createUserSchema = z.object({
email: z.string().email(),
// ... campos de CreateUserDto
});
```
### 6.3 Checklist de Sincronizacion
```
Cuando cambias Backend, verificar en Frontend:
[ ] 1. Interfaces actualizadas (types/*.ts)
[ ] 2. Enums sincronizados
[ ] 3. Zod schemas actualizados
[ ] 4. Services API actualizados
[ ] 5. Hooks actualizados (si cambia endpoint)
[ ] 6. Componentes manejan nuevos campos
[ ] 7. Componentes manejan campos eliminados
[ ] 8. Manejo de errores actualizado
[ ] 9. npm run typecheck pasa
[ ] 10. npm run build pasa
```
---
## 7. COMANDOS DE VERIFICACION
```bash
# Buscar referencias a campo en backend
grep -rn "fieldName" src/
# Buscar referencias a campo en frontend
grep -rn "fieldName" apps/ shared/
# Verificar tipos TypeScript
npm run typecheck
# Verificar que Swagger refleja cambios
curl http://localhost:3000/api-json | jq '.paths["/users"]'
# Comparar DTO con Interface
diff <(grep -A 50 "class UserResponseDto" src/modules/user/dto/user-response.dto.ts) \
<(grep -A 50 "interface User" apps/web/types/user.types.ts)
```
---
## 8. ANTI-PATRONES
### Anti-Patron 1: Cambiar Backend sin Frontend
```
❌ INCORRECTO:
1. Backend-Agent agrega campo a ResponseDto
2. Se hace deploy
3. Frontend falla porque no espera el campo (usualmente OK)
4. O peor: Frontend espera campo que ya no existe (ROMPE)
✅ CORRECTO:
1. Backend-Agent agrega campo
2. Frontend-Agent actualiza types ANTES de deploy
3. Deploy coordinado
```
### Anti-Patron 2: Tipos Diferentes
```
❌ INCORRECTO:
// Backend
@Column({ type: 'decimal' })
price: string; // String para precision
// Frontend
interface Product {
price: number; // ← INCORRECTO: tipo diferente
}
✅ CORRECTO:
// Frontend
interface Product {
price: string; // String como backend
}
// O usar transformacion explicita
const displayPrice = parseFloat(product.price).toFixed(2);
```
### Anti-Patron 3: Validaciones Desincronizadas
```
❌ INCORRECTO:
// Backend: max 100 caracteres
@MaxLength(100)
name: string;
// Frontend: max 50 caracteres
const schema = z.object({
name: z.string().max(50) // ← INCORRECTO: limite diferente
});
✅ CORRECTO:
// Mismos limites en ambos lados
// Backend: @MaxLength(100)
// Frontend: z.string().max(100)
```
---
## 9. MATRIZ RESUMEN
| Si Cambias... | Actualiza en Backend | Actualiza en Frontend |
|---------------|---------------------|----------------------|
| Entity campo | DTO, posible Service | Types, Schema, Components |
| Entity relacion | DTO, Service | Types, Hooks |
| CreateDto campo | - | Schema (Zod) |
| CreateDto validacion | - | Schema (Zod) |
| ResponseDto campo | Service (si calculado) | Types, Components |
| Service metodo | Controller (si expuesto) | Service, Hook |
| Service logica | - | Manejo errores |
| Controller endpoint | Swagger | Service, Hook |
| Controller ruta | Swagger | Service (URL) |
---
**Version:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Guia de Impacto

View File

@ -0,0 +1,428 @@
# IMPACTO: CAMBIOS EN DDL (Base de Datos)
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** CRÍTICA - Leer antes de modificar DDL
**Sistema:** SIMCO + CAPVED
---
## PROPÓSITO
Documentar el impacto cascada de cambios en DDL y las acciones requeridas en cada capa.
---
## PRINCIPIO FUNDAMENTAL
```
╔══════════════════════════════════════════════════════════════════════╗
║ CAMBIO EN DDL = CAMBIO EN CASCADA ║
║ ║
║ DDL → Entity → DTO → Service → Controller → Frontend ║
║ ║
║ ⚠️ NUNCA cambiar DDL sin actualizar las capas dependientes ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## 1. AGREGAR COLUMNA
### Impacto por Capa
| Capa | Archivo(s) Afectado(s) | Acción Requerida |
|------|------------------------|------------------|
| **DDL** | `schemas/{schema}/tables/{tabla}.sql` | Agregar columna |
| **Entity** | `{nombre}.entity.ts` | Agregar @Column |
| **DTO Create** | `create-{nombre}.dto.ts` | Agregar campo (si input) |
| **DTO Update** | `update-{nombre}.dto.ts` | Heredado de Create |
| **DTO Response** | `{nombre}-response.dto.ts` | Agregar campo (si output) |
| **Service** | `{nombre}.service.ts` | Ajustar lógica si necesario |
| **Controller** | `{nombre}.controller.ts` | Swagger actualizado automático |
| **Frontend Types** | `{nombre}.types.ts` | Agregar campo |
| **Frontend Forms** | `{Nombre}Form.tsx` | Agregar input (si editable) |
| **Frontend Display** | `{Nombre}Card.tsx`, etc. | Mostrar campo (si visible) |
| **Tests** | `*.spec.ts`, `*.test.tsx` | Actualizar fixtures |
### Checklist: Agregar Columna
```
DDL (Database-Agent):
[ ] ALTER TABLE o modificar CREATE TABLE
[ ] Definir tipo correcto (ver MAPEO-TIPOS.md)
[ ] Definir NULL/NOT NULL
[ ] Definir DEFAULT si aplica
[ ] Crear índice si es buscable
[ ] Ejecutar carga limpia exitosa
[ ] Actualizar DATABASE_INVENTORY.yml
Backend (Backend-Agent):
[ ] Agregar @Column en Entity
- type: coincide con DDL
- nullable: coincide con DDL
- default: coincide con DDL
[ ] Agregar campo en CreateDto (si es input)
- Validaciones apropiadas
- @ApiProperty con ejemplo
[ ] Agregar campo en ResponseDto (si es output)
[ ] Ajustar Service si hay lógica nueva
[ ] npm run build → pasa
[ ] npm run lint → pasa
[ ] Actualizar BACKEND_INVENTORY.yml
Frontend (Frontend-Agent):
[ ] Agregar campo en interface/type
[ ] Actualizar Zod schema si hay form
[ ] Agregar input en formulario (si editable)
[ ] Mostrar en UI (si visible)
[ ] npm run build → pasa
[ ] npm run lint → pasa
[ ] Actualizar FRONTEND_INVENTORY.yml
Integración:
[ ] Test e2e funciona
[ ] Swagger muestra campo nuevo
[ ] Frontend consume correctamente
```
### Ejemplo Completo: Agregar Campo `phone`
**1. DDL:**
```sql
-- schemas/auth/tables/01-users.sql
ALTER TABLE auth.users
ADD COLUMN phone VARCHAR(20);
```
**2. Entity:**
```typescript
// entities/user.entity.ts
@Column({ type: 'varchar', length: 20, nullable: true })
phone: string;
```
**3. DTO Create:**
```typescript
// dto/create-user.dto.ts
@ApiPropertyOptional({ description: 'Teléfono', example: '+521234567890' })
@IsOptional()
@IsPhoneNumber('MX')
phone?: string;
```
**4. DTO Response:**
```typescript
// dto/user-response.dto.ts
@ApiPropertyOptional()
phone?: string;
```
**5. Frontend Type:**
```typescript
// types/user.types.ts
interface User {
id: string;
email: string;
name: string;
phone?: string; // ← NUEVO
}
```
**6. Frontend Form:**
```typescript
// components/UserForm.tsx
<FormField
name="phone"
label="Teléfono"
placeholder="+52 123 456 7890"
/>
```
---
## 2. ELIMINAR COLUMNA
### Impacto por Capa
| Capa | Acción | Riesgo |
|------|--------|--------|
| **DDL** | `ALTER TABLE DROP COLUMN` | ALTO - Datos perdidos |
| **Entity** | Eliminar @Column | MEDIO |
| **DTO** | Eliminar campo | MEDIO |
| **Service** | Eliminar referencias | ALTO - Lógica rota |
| **Frontend** | Eliminar campo y usos | ALTO - UI rota |
| **Tests** | Actualizar fixtures | MEDIO |
### Checklist: Eliminar Columna
```
⚠️ ANTES DE ELIMINAR:
[ ] Confirmar que datos no son necesarios
[ ] Verificar que no hay dependencias en código
[ ] Grep en todo el proyecto: grep -rn "{columna}" apps/
[ ] Planificar migración de datos si necesario
DDL:
[ ] ALTER TABLE DROP COLUMN
[ ] Verificar que no rompe constraints/FK
[ ] Carga limpia exitosa
Backend:
[ ] Eliminar @Column de Entity
[ ] Eliminar de DTOs
[ ] Buscar y eliminar referencias en Services
[ ] npm run build → sin errores de tipo
[ ] npm run test → pasa
Frontend:
[ ] Eliminar de types/interfaces
[ ] Eliminar de formularios
[ ] Eliminar de displays
[ ] npm run build → sin errores de tipo
Post-eliminación:
[ ] Verificar que aplicación funciona
[ ] Verificar que no hay errores en consola
[ ] Actualizar documentación
```
---
## 3. MODIFICAR TIPO DE COLUMNA
### Impacto por Capa
| Cambio | Backend | Frontend | Riesgo |
|--------|---------|----------|--------|
| `VARCHAR(50)``VARCHAR(100)` | Cambiar length | - | BAJO |
| `VARCHAR``TEXT` | Cambiar type | - | BAJO |
| `INTEGER``BIGINT` | `number``string` | Cambiar tipo | MEDIO |
| `VARCHAR``ENUM` | Agregar enum | Agregar enum | MEDIO |
| `TEXT``JSON` | Cambiar tipo, parsear | Cambiar tipo | ALTO |
### Checklist: Modificar Tipo
```
DDL:
[ ] ALTER TABLE ALTER COLUMN TYPE
[ ] Verificar que datos existentes son compatibles
[ ] Carga limpia exitosa
Backend:
[ ] Actualizar @Column type
[ ] Actualizar tipo TypeScript si cambió
[ ] Actualizar validaciones en DTO
[ ] npm run build → pasa
Frontend:
[ ] Actualizar tipo en interface
[ ] Actualizar validación Zod
[ ] Ajustar componentes si tipo cambió
[ ] npm run build → pasa
Datos:
[ ] Migrar datos existentes si necesario
[ ] Validar integridad post-migración
```
---
## 4. AGREGAR/MODIFICAR FOREIGN KEY
### Impacto por Capa
| Capa | Acción |
|------|--------|
| **DDL** | Agregar FK + índice |
| **Entity** | Agregar @ManyToOne + @JoinColumn |
| **Service** | Validar existencia antes de insertar |
| **DTO** | Agregar campo ID de relación |
| **Frontend** | Agregar selector/dropdown |
### Checklist: Agregar FK
```
DDL:
[ ] Agregar columna FK (UUID)
[ ] Agregar FOREIGN KEY constraint
[ ] Definir ON DELETE (CASCADE/SET NULL/RESTRICT)
[ ] Crear índice en FK
[ ] Verificar que tabla referenciada existe
Backend:
[ ] Agregar columna en Entity: {relation}Id: string
[ ] Agregar relación: @ManyToOne(() => RelatedEntity)
[ ] Agregar @JoinColumn({ name: '{relation}_id' })
[ ] Validar existencia en Service antes de crear
[ ] Agregar en DTO con @IsUUID
Frontend:
[ ] Agregar campo en type
[ ] Crear selector si es editable
[ ] Mostrar relación en UI
```
---
## 5. AGREGAR/MODIFICAR ÍNDICE
### Impacto
- **DDL**: Agregar `CREATE INDEX`
- **Otras capas**: Sin impacto directo
- **Performance**: Mejora en queries
### Checklist: Agregar Índice
```
[ ] Identificar columnas frecuentemente buscadas
[ ] Agregar CREATE INDEX en DDL
[ ] Para búsquedas compuestas: índice compuesto
[ ] Ejecutar carga limpia
[ ] Verificar plan de query (EXPLAIN)
```
---
## 6. AGREGAR TABLA NUEVA
### Impacto Completo
```
NUEVA TABLA = Feature completa
DDL:
├── CREATE TABLE con todas las columnas
├── PRIMARY KEY
├── FOREIGN KEYS
├── INDEXES
├── COMMENTS
└── Actualizar DATABASE_INVENTORY.yml
Backend:
├── {nombre}.entity.ts
├── create-{nombre}.dto.ts
├── update-{nombre}.dto.ts
├── {nombre}-response.dto.ts
├── {nombre}.service.ts
├── {nombre}.controller.ts
├── {nombre}.module.ts
├── Tests unitarios
└── Actualizar BACKEND_INVENTORY.yml
Frontend:
├── {nombre}.types.ts
├── {nombre}.service.ts (API calls)
├── use{Nombre}.ts (hook)
├── {Nombre}Form.tsx
├── {Nombre}List.tsx
├── {Nombre}Page.tsx
├── Tests de componentes
└── Actualizar FRONTEND_INVENTORY.yml
```
---
## 7. ELIMINAR TABLA
### Impacto CRÍTICO
```
⚠️ ELIMINAR TABLA = DESTRUIR FEATURE
ANTES de eliminar:
[ ] Confirmar que feature ya no se usa
[ ] Backup de datos si necesario
[ ] Verificar que no hay FK apuntando a esta tabla
[ ] Grep completo: grep -rn "{tabla}" apps/
Orden de eliminación (inverso a creación):
1. Frontend: Eliminar páginas, componentes, types
2. Backend: Eliminar controller, service, entity, DTOs, module
3. DDL: DROP TABLE
4. Actualizar todos los inventarios
Post-eliminación:
[ ] Build pasa en todas las capas
[ ] Aplicación funciona sin errores
[ ] Documentación actualizada
```
---
## 8. MATRIZ DE PROPAGACIÓN RÁPIDA
```
┌─────────────────┬─────────┬────────┬─────────┬──────────┬──────────┐
│ Cambio DDL │ Entity │ DTO │ Service │ Frontend │ Tests │
├─────────────────┼─────────┼────────┼─────────┼──────────┼──────────┤
│ + Columna │ +Column │ +Field │ ?Lógica │ +Type │ +Fixture │
│ - Columna │ -Column │ -Field │ -Refs │ -Type │ -Fixture │
│ ~ Tipo │ ~Type │ ~Valid │ ?Parse │ ~Type │ ~Fixture │
│ + FK │ +Rel │ +UUID │ +Valid │ +Select │ +Test │
│ + Índice │ - │ - │ - │ - │ - │
│ + Tabla │ +Todo │ +Todo │ +Todo │ +Todo │ +Todo │
│ - Tabla │ -Todo │ -Todo │ -Todo │ -Todo │ -Todo │
└─────────────────┴─────────┴────────┴─────────┴──────────┴──────────┘
Leyenda:
+ = Agregar
- = Eliminar
~ = Modificar
? = Posiblemente
```
---
## 9. COMANDOS DE VERIFICACIÓN
```bash
# Verificar que cambio DDL no rompe
cd {DB_SCRIPTS_PATH}
./recreate-database.sh # Carga limpia completa
# Verificar build backend
cd {BACKEND_ROOT}
npm run build && npm run lint
# Verificar build frontend
cd {FRONTEND_ROOT}
npm run build && npm run lint && npm run typecheck
# Buscar referencias a columna/tabla
grep -rn "{nombre}" apps/
# Verificar alineación Entity-DDL
# Comparar @Column en Entity vs columnas en DDL
```
---
## 10. ANTI-PATRONES
```
❌ NUNCA: Cambiar DDL sin actualizar Entity
→ Resultado: Runtime error al acceder columna
❌ NUNCA: Agregar columna NOT NULL sin DEFAULT
→ Resultado: INSERT falla en registros existentes
❌ NUNCA: Eliminar columna sin verificar uso
→ Resultado: Código roto en múltiples lugares
❌ NUNCA: Cambiar tipo sin migrar datos
→ Resultado: Datos corruptos o perdidos
❌ NUNCA: Modificar FK sin ajustar relaciones en Entity
→ Resultado: ORM genera queries incorrectas
✅ SIEMPRE: DDL primero → Entity segundo → DTO tercero → Frontend cuarto
✅ SIEMPRE: Verificar build en TODAS las capas después de cambio
✅ SIEMPRE: Actualizar inventarios y trazas
✅ SIEMPRE: Grep antes de eliminar para encontrar usos
```
---
**Versión:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Guía de Impacto

View File

@ -0,0 +1,439 @@
# IMPACTO DE CAMBIOS EN ENTITY
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** OBLIGATORIA - Consultar antes de modificar Entities
**Sistema:** SIMCO + CAPVED
---
## PROPOSITO
Documentar especificamente los impactos de modificar Entities de TypeORM, que son el puente entre la base de datos y la logica de negocio.
---
## 1. PRINCIPIO FUNDAMENTAL
```
╔══════════════════════════════════════════════════════════════════════╗
║ ENTITY ES REFLEJO DE DDL, NO AL REVES ║
║ ║
║ DDL (fuente) ──────▶ Entity (reflejo) ──────▶ DTO (contrato API) ║
║ ║
║ ⚠️ NUNCA crear Entity sin DDL existente ║
║ ⚠️ NUNCA modificar Entity sin verificar DDL primero ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## 2. TIPOS DE CAMBIOS EN ENTITY
### 2.1 Cambios de Columna
| Tipo de Cambio | Impacto DDL | Impacto DTO | Impacto Service | Impacto Frontend |
|----------------|-------------|-------------|-----------------|------------------|
| Agregar campo | SI | SI | Posible | SI |
| Eliminar campo | SI | SI | Verificar | SI |
| Renombrar campo | SI | SI | SI | SI |
| Cambiar tipo | SI | SI | Posible | SI |
| Cambiar nullable | SI | SI | Posible | SI |
| Agregar default | SI | Posible | NO | NO |
### 2.2 Cambios de Relacion
| Tipo de Cambio | Impacto DDL | Impacto DTO | Impacto Service | Impacto Frontend |
|----------------|-------------|-------------|-----------------|------------------|
| Agregar ManyToOne | SI (FK) | SI | SI | SI |
| Agregar OneToMany | NO | SI | SI | SI |
| Agregar ManyToMany | SI (junction) | SI | SI | SI |
| Eliminar relacion | SI | SI | SI | SI |
| Cambiar cascade | NO | NO | Verificar | NO |
### 2.3 Cambios de Indice/Constraint
| Tipo de Cambio | Impacto DDL | Impacto DTO | Impacto Service | Impacto Frontend |
|----------------|-------------|-------------|-----------------|------------------|
| Agregar indice | SI | NO | NO | NO |
| Agregar unique | SI | NO | Manejar error | Manejar error |
| Agregar check | SI | NO | Manejar error | Manejar error |
---
## 3. AGREGAR CAMPO A ENTITY
### Flujo Completo
```
┌─────────────────────────────────────────────────────────────────────┐
│ PASO 1: DDL (Database-Agent) │
├─────────────────────────────────────────────────────────────────────┤
│ ALTER TABLE auth.users ADD COLUMN phone VARCHAR(20); │
│ │
│ Validar: ./recreate-database.sh && psql -c "\d auth.users" │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ PASO 2: Entity (Backend-Agent) │
├─────────────────────────────────────────────────────────────────────┤
@Column({ type: 'varchar', length: 20, nullable: true }) │
│ phone: string | null; │
│ │
│ Validar: npm run build │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ PASO 3: DTOs (Backend-Agent) │
├─────────────────────────────────────────────────────────────────────┤
│ CreateUserDto: │
@IsOptional() │
@IsString() │
@MaxLength(20) │
│ phone?: string; │
│ │
│ UserResponseDto: │
@ApiProperty({ required: false }) │
│ phone: string | null; │
│ │
│ Validar: npm run build && npm run lint │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ PASO 4: Frontend (Frontend-Agent) │
├─────────────────────────────────────────────────────────────────────┤
│ user.types.ts: │
│ interface User { │
│ phone: string | null; │
│ } │
│ │
│ user.schema.ts: │
│ phone: z.string().max(20).optional() │
│ │
│ Validar: npm run build && npm run typecheck │
└─────────────────────────────────────────────────────────────────────┘
```
### Checklist Agregar Campo
```
[ ] 1. DDL tiene la columna con tipo correcto
[ ] 2. Entity @Column refleja DDL (tipo, length, nullable)
[ ] 3. CreateDto tiene validaciones apropiadas
[ ] 4. UpdateDto hereda de CreateDto (automatico con PartialType)
[ ] 5. ResponseDto tiene el campo con ApiProperty
[ ] 6. Frontend interface tiene el campo
[ ] 7. Frontend Zod schema tiene validacion equivalente
[ ] 8. Swagger documenta correctamente
[ ] 9. Build pasa en backend
[ ] 10. Build y typecheck pasan en frontend
```
---
## 4. ELIMINAR CAMPO DE ENTITY
### ADVERTENCIA
```
⚠️ ELIMINAR CAMPO ES OPERACION DESTRUCTIVA
ANTES DE ELIMINAR:
1. Verificar que no hay datos importantes
2. Buscar TODAS las referencias en codigo
3. Considerar deprecar primero (nullable + ignorar)
4. Planear migracion de datos si necesario
```
### Flujo de Eliminacion
```bash
# PASO 0: Buscar todas las referencias
grep -rn "fieldName" src/
grep -rn "fieldName" apps/
grep -rn "field_name" db/ # snake_case en DDL
# PASO 1: Eliminar uso en Frontend primero (evita errores de tipo)
# PASO 2: Eliminar uso en Service/Controller
# PASO 3: Eliminar de DTOs
# PASO 4: Eliminar de Entity
# PASO 5: Eliminar de DDL (ultimo porque es irreversible)
```
### Checklist Eliminar Campo
```
[ ] 1. Buscar referencias en backend: grep -rn "field" src/
[ ] 2. Buscar referencias en frontend: grep -rn "field" apps/
[ ] 3. Eliminar de componentes Frontend
[ ] 4. Eliminar de types/interfaces Frontend
[ ] 5. Eliminar de Zod schemas Frontend
[ ] 6. Eliminar de Service (si hay logica)
[ ] 7. Eliminar de ResponseDto
[ ] 8. Eliminar de CreateDto
[ ] 9. Eliminar de Entity
[ ] 10. Eliminar de DDL (con migracion)
[ ] 11. Build pasa en todos los proyectos
```
---
## 5. AGREGAR RELACION A ENTITY
### 5.1 ManyToOne (FK hacia otra tabla)
```typescript
// Entity: Order tiene un User
@ManyToOne(() => UserEntity, { nullable: false })
@JoinColumn({ name: 'user_id' })
user: UserEntity;
// Columna FK explicita (opcional pero recomendado)
@Column({ type: 'uuid' })
userId: string;
```
**Impacto:**
| Capa | Archivo | Cambio Requerido |
|------|---------|------------------|
| DDL | `XX-orders.sql` | `user_id UUID REFERENCES auth.users(id)` |
| Entity | `order.entity.ts` | `@ManyToOne` + `@JoinColumn` |
| CreateDto | `create-order.dto.ts` | `@IsUUID() userId: string;` |
| ResponseDto | `order-response.dto.ts` | `userId: string;` o `user: UserResponseDto;` |
| Service | `order.service.ts` | Decidir: cargar relacion o no |
| Frontend | `order.types.ts` | `userId: string;` o `user: User;` |
### 5.2 OneToMany (Coleccion inversa)
```typescript
// Entity: User tiene muchos Orders
@OneToMany(() => OrderEntity, order => order.user)
orders: OrderEntity[];
```
**Impacto:**
| Capa | Archivo | Cambio Requerido |
|------|---------|------------------|
| DDL | N/A | Sin cambio (FK esta en orders) |
| Entity | `user.entity.ts` | `@OneToMany` |
| ResponseDto | `user-response.dto.ts` | Decidir: incluir o no |
| Service | `user.service.ts` | Decidir: cargar relacion o no |
| Frontend | `user.types.ts` | `orders?: Order[];` |
### 5.3 ManyToMany (Tabla junction)
```typescript
// Entity: User tiene muchos Roles
@ManyToMany(() => RoleEntity)
@JoinTable({
name: 'user_roles',
joinColumn: { name: 'user_id' },
inverseJoinColumn: { name: 'role_id' },
})
roles: RoleEntity[];
```
**Impacto:**
| Capa | Archivo | Cambio Requerido |
|------|---------|------------------|
| DDL | `XX-user-roles.sql` | Crear tabla junction |
| Entity | `user.entity.ts` | `@ManyToMany` + `@JoinTable` |
| ResponseDto | `user-response.dto.ts` | `roles: RoleResponseDto[];` |
| Service | `user.service.ts` | Logica para asignar/remover roles |
| Controller | `user.controller.ts` | Endpoints para manejar roles |
| Frontend | `user.types.ts` | `roles: Role[];` |
| Frontend | `useUserRoles.ts` | Hook para manejar roles |
### Checklist Agregar Relacion
```
[ ] 1. DDL tiene FK o tabla junction
[ ] 2. Entity tiene decorador de relacion correcto
[ ] 3. Entity relacionada tiene relacion inversa (si necesario)
[ ] 4. CreateDto acepta ID de relacion
[ ] 5. ResponseDto decide: incluir objeto o solo ID
[ ] 6. Service decide: eager loading o lazy
[ ] 7. Frontend types reflejan estructura
[ ] 8. Frontend tiene logica para manejar relacion
```
---
## 6. CAMBIAR TIPO DE CAMPO
### Mapeo de Cambios Comunes
| Cambio | DDL | Entity | DTO | Frontend |
|--------|-----|--------|-----|----------|
| `INT``BIGINT` | ALTER TYPE | `number` (sin cambio) | Sin cambio | Sin cambio |
| `VARCHAR``TEXT` | ALTER TYPE | Sin cambio | Ajustar MaxLength | Ajustar max |
| `INTEGER``DECIMAL` | ALTER TYPE | `number``string` | Ajustar validador | Ajustar tipo |
| `BOOLEAN``ENUM` | ALTER TYPE | `boolean``enum` | Cambiar validador | Cambiar tipo |
| `VARCHAR``UUID` | ALTER TYPE + datos | `string` (sin cambio) | Agregar @IsUUID | Agregar validacion |
### Ejemplo: INTEGER a DECIMAL
```sql
-- DDL
ALTER TABLE products ALTER COLUMN price TYPE DECIMAL(10,2);
```
```typescript
// Entity ANTES
@Column({ type: 'integer' })
price: number;
// Entity DESPUES
@Column({ type: 'decimal', precision: 10, scale: 2 })
price: string; // String para precision
```
```typescript
// CreateDto ANTES
@IsNumber()
price: number;
// CreateDto DESPUES
@IsNumberString()
@Matches(/^\d+(\.\d{1,2})?$/)
price: string;
```
```typescript
// Frontend ANTES
interface Product {
price: number;
}
// Frontend DESPUES
interface Product {
price: string;
}
// Uso en componente
const displayPrice = parseFloat(product.price).toFixed(2);
```
---
## 7. PATRON DE MIGRACION SEGURA
### Para cambios que pueden romper
```
FASE 1: Agregar nuevo (sin eliminar viejo)
═══════════════════════════════════════════════════════════════
1. Agregar nuevo campo/columna junto al existente
2. Actualizar codigo para escribir en ambos
3. Deploy backend
FASE 2: Migrar datos
═══════════════════════════════════════════════════════════════
1. Script para copiar datos de viejo a nuevo
2. Verificar integridad
FASE 3: Cambiar lecturas
═══════════════════════════════════════════════════════════════
1. Actualizar codigo para leer de nuevo
2. Deploy backend + frontend
FASE 4: Eliminar viejo
═══════════════════════════════════════════════════════════════
1. Eliminar campo viejo de codigo
2. Eliminar columna de DDL
3. Deploy final
```
---
## 8. DECORADORES ENTITY Y SU IMPACTO
### @Column Options
| Option | Impacto si cambia | Requiere DDL |
|--------|-------------------|--------------|
| `type` | Tipo TS, validacion DTO | SI |
| `length` | Validacion DTO | SI |
| `nullable` | Tipo TS (null), validacion | SI |
| `default` | Posible logica Service | SI |
| `unique` | Manejo error Service | SI |
| `name` | Ninguno (mapeo interno) | SI |
| `precision/scale` | Tipo TS, formateo FE | SI |
### @Index
| Cambio | Impacto |
|--------|---------|
| Agregar | Solo performance, DDL |
| Eliminar | Solo performance, DDL |
| Cambiar columnas | Solo performance, DDL |
### Relation Options
| Option | Impacto si cambia |
|--------|-------------------|
| `eager: true` | Queries cargan automatico |
| `cascade: true` | Operaciones se propagan |
| `onDelete` | Comportamiento al eliminar padre |
| `nullable` | Validacion, tipo TS |
---
## 9. COMANDOS DE VERIFICACION
```bash
# Ver diferencias entre DDL y Entity
# (manual: comparar columnas)
psql -d mydb -c "\d schema.table"
cat src/modules/x/entities/x.entity.ts
# Verificar que Entity compila
npm run build
# Verificar que no hay referencias rotas
npm run lint
# Verificar sincronizacion con frontend
npm run typecheck --prefix apps/web
# Buscar uso de campo especifico
grep -rn "fieldName" src/ apps/
```
---
## 10. MATRIZ DE DECISION
```
¿Que tipo de cambio en Entity?
┌─────────────────────┐
│ Agregar campo │──▶ DDL primero → Entity → DTO → Frontend
└─────────────────────┘
┌─────────────────────┐
│ Eliminar campo │──▶ Frontend primero → Service → DTO → Entity → DDL
└─────────────────────┘
┌─────────────────────┐
│ Cambiar tipo │──▶ DDL primero → Entity → DTO → Frontend
└─────────────────────┘
┌─────────────────────┐
│ Agregar relacion │──▶ DDL (FK) → Entity → DTO → Service → Frontend
└─────────────────────┘
┌─────────────────────┐
│ Renombrar campo │──▶ Migracion segura (agregar nuevo → migrar → eliminar viejo)
└─────────────────────┘
```
---
**Version:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Guia de Impacto

View File

@ -0,0 +1,445 @@
# MATRIZ DE DEPENDENCIAS ENTRE CAPAS
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** REFERENCIA - Consultar para entender impactos
**Sistema:** SIMCO + CAPVED
---
## PROPOSITO
Proporcionar una matriz completa de dependencias entre todos los componentes del sistema para que los agentes puedan identificar rapidamente que se debe actualizar cuando se modifica algo.
---
## 1. VISION GENERAL DE DEPENDENCIAS
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ FLUJO DE DEPENDENCIAS │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ DDL │────▶│ Entity │────▶│ DTO │────▶│ Service │ │
│ │ (SQL) │ │(TypeORM)│ │ (class) │ │ (logic) │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │ │
│ │ │ │ ▼ │
│ │ │ │ ┌─────────┐ │
│ │ │ │ │Controller│ │
│ │ │ │ │ (REST) │ │
│ │ │ │ └─────────┘ │
│ │ │ │ │ │
│ │ │ │ ▼ │
│ │ │ │ ┌─────────────────┐ │
│ │ │ │ │ SWAGGER │ │
│ │ │ │ │ (API Contract) │ │
│ │ │ │ └─────────────────┘ │
│ │ │ │ │ │
│ │ │ ▼ ▼ │
│ │ │ ┌─────────────────────────┐ │
│ │ │ │ FRONTEND │ │
│ │ │ │ Types│Schema│Hooks│UI │ │
│ │ │ └─────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────┐ │
│ │ INVENTARIOS │ │
│ │ DB│BE│FE│MASTER │ │
│ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## 2. MATRIZ DE DEPENDENCIA: DDL
### Cuando se modifica DDL (tablas, columnas)
| Componente Dependiente | Requiere Actualizacion | Prioridad | Detalle |
|------------------------|------------------------|-----------|---------|
| **Entity** | SI | 1 - Inmediato | Debe reflejar estructura DDL |
| **CreateDto** | SI | 2 | Campos nuevos/eliminados |
| **UpdateDto** | AUTO | 2 | Hereda de CreateDto |
| **ResponseDto** | SI | 2 | Campos en response |
| **Service** | POSIBLE | 3 | Si hay logica de campo |
| **Controller** | NO | - | Usa DTOs |
| **Frontend Types** | SI | 4 | Reflejar ResponseDto |
| **Frontend Schema** | SI | 4 | Reflejar CreateDto |
| **Frontend Components** | POSIBLE | 5 | Si muestran campo |
| **Swagger** | AUTO | - | Generado de DTOs |
| **DATABASE_INVENTORY** | SI | 6 | Registrar cambio |
| **Tests** | POSIBLE | 5 | Si prueban campo |
### Diagrama de Propagacion DDL
```
DDL Change
├──▶ Entity (obligatorio)
│ │
│ ├──▶ CreateDto (obligatorio)
│ │ │
│ │ └──▶ Frontend Schema (obligatorio)
│ │
│ ├──▶ ResponseDto (obligatorio)
│ │ │
│ │ └──▶ Frontend Types (obligatorio)
│ │ │
│ │ └──▶ Frontend Components (si usa campo)
│ │
│ └──▶ Service (si logica de campo)
└──▶ DATABASE_INVENTORY (obligatorio)
```
---
## 3. MATRIZ DE DEPENDENCIA: ENTITY
### Cuando se modifica Entity
| Componente Dependiente | Requiere Actualizacion | Prioridad | Detalle |
|------------------------|------------------------|-----------|---------|
| **DDL** | VERIFICAR | 0 | DDL debe existir primero |
| **CreateDto** | SI | 1 | Reflejar campos |
| **UpdateDto** | AUTO | 1 | Hereda de CreateDto |
| **ResponseDto** | SI | 1 | Reflejar campos |
| **Service** | POSIBLE | 2 | Si usa campo directamente |
| **Controller** | NO | - | Usa DTOs |
| **Frontend Types** | SI | 3 | Reflejar ResponseDto |
| **Frontend Schema** | SI | 3 | Reflejar CreateDto |
| **Frontend Components** | POSIBLE | 4 | Si muestran campo |
| **BACKEND_INVENTORY** | SI | 5 | Registrar cambio |
| **Unit Tests** | SI | 4 | Actualizar mocks |
### Por Tipo de Cambio en Entity
| Cambio | DTOs | Service | Frontend | Tests |
|--------|------|---------|----------|-------|
| Agregar @Column | SI | NO | SI | SI |
| Eliminar @Column | SI | Verificar | SI | SI |
| Cambiar tipo @Column | SI | Posible | SI | SI |
| Agregar @ManyToOne | SI | SI | SI | SI |
| Agregar @OneToMany | SI | Posible | SI | Posible |
| Cambiar @Index | NO | NO | NO | NO |
| Cambiar nullable | SI | NO | SI | Posible |
---
## 4. MATRIZ DE DEPENDENCIA: DTO
### Cuando se modifica CreateDto
| Componente Dependiente | Requiere Actualizacion | Prioridad | Detalle |
|------------------------|------------------------|-----------|---------|
| **UpdateDto** | AUTO | 1 | Si usa PartialType |
| **Service** | NO | - | Recibe DTO |
| **Controller** | NO | - | Usa DTO |
| **Swagger** | AUTO | - | Generado |
| **Frontend Schema (Zod)** | SI | 2 | Mismas validaciones |
| **Frontend Forms** | POSIBLE | 3 | Si campos cambian |
| **Integration Tests** | SI | 3 | Fixtures |
### Cuando se modifica ResponseDto
| Componente Dependiente | Requiere Actualizacion | Prioridad | Detalle |
|------------------------|------------------------|-----------|---------|
| **Service** | SI | 1 | Si mapea a ResponseDto |
| **Controller** | NO | - | Retorna Service result |
| **Swagger** | AUTO | - | Generado |
| **Frontend Types** | SI | 2 | Interface debe coincidir |
| **Frontend Components** | POSIBLE | 3 | Si muestran campo |
| **Frontend Hooks** | NO | - | Usan Types |
---
## 5. MATRIZ DE DEPENDENCIA: SERVICE
### Cuando se modifica Service
| Componente Dependiente | Requiere Actualizacion | Prioridad | Detalle |
|------------------------|------------------------|-----------|---------|
| **Controller** | POSIBLE | 1 | Si cambia firma metodo |
| **Swagger** | POSIBLE | 2 | Si cambia response/errors |
| **Frontend** | POSIBLE | 2 | Si cambia contrato API |
| **Unit Tests** | SI | 1 | Tests del service |
| **Integration Tests** | POSIBLE | 2 | Si cambia comportamiento |
### Por Tipo de Cambio en Service
| Cambio | Controller | Frontend | Tests |
|--------|------------|----------|-------|
| Agregar metodo | SI (nuevo endpoint) | SI | SI |
| Eliminar metodo | SI | SI | SI |
| Cambiar firma | SI | SI | SI |
| Cambiar logica interna | NO | NO | SI |
| Agregar validacion | NO | Manejar error | SI |
| Cambiar error thrown | NO | Manejar error | SI |
---
## 6. MATRIZ DE DEPENDENCIA: CONTROLLER
### Cuando se modifica Controller
| Componente Dependiente | Requiere Actualizacion | Prioridad | Detalle |
|------------------------|------------------------|-----------|---------|
| **Service** | NO | - | Controller consume Service |
| **Swagger** | AUTO | - | Generado de decoradores |
| **Frontend Service** | SI | 1 | Si cambia ruta/metodo |
| **Frontend Hooks** | POSIBLE | 2 | Si cambia endpoint |
| **E2E Tests** | SI | 2 | Tests de endpoint |
### Por Tipo de Cambio en Controller
| Cambio | Frontend Service | Frontend Hooks | Tests |
|--------|------------------|----------------|-------|
| Agregar endpoint | SI (nuevo metodo) | SI (nuevo hook) | SI |
| Eliminar endpoint | SI | SI | SI |
| Cambiar ruta | SI | NO | SI |
| Cambiar metodo HTTP | SI | NO | SI |
| Cambiar decoradores Swagger | NO | NO | NO |
| Agregar guard | NO | Manejar 401/403 | SI |
---
## 7. MATRIZ DE DEPENDENCIA: FRONTEND
### Cuando se modifica Frontend Types
| Componente Dependiente | Requiere Actualizacion | Prioridad | Detalle |
|------------------------|------------------------|-----------|---------|
| **Zod Schema** | VERIFICAR | 1 | Debe ser consistente |
| **Hooks** | NO | - | Usan Types |
| **Components** | VERIFICAR | 2 | Si usan tipo |
| **Services** | NO | - | Usan Types |
### Cuando se modifica Frontend Schema (Zod)
| Componente Dependiente | Requiere Actualizacion | Prioridad | Detalle |
|------------------------|------------------------|-----------|---------|
| **Forms** | VERIFICAR | 1 | Si usan schema |
| **Types** | VERIFICAR | 1 | Deben coincidir |
### Cuando se modifica Frontend Hook
| Componente Dependiente | Requiere Actualizacion | Prioridad | Detalle |
|------------------------|------------------------|-----------|---------|
| **Components** | VERIFICAR | 1 | Si usan hook |
| **Pages** | VERIFICAR | 1 | Si usan hook |
---
## 8. MATRIZ RAPIDA DE REFERENCIA
### "Si cambio X, que debo actualizar?"
```
┌─────────────────┬─────┬────────┬─────────┬─────────┬────────┬─────────────┐
│ SI CAMBIO... │ DDL │ Entity │DTO-Crea │DTO-Resp │Service │ Controller │
├─────────────────┼─────┼────────┼─────────┼─────────┼────────┼─────────────┤
│ DDL columna │ ── │ SI │ SI │ SI │ Posib │ NO │
│ DDL FK │ ── │ SI │ SI │ SI │ SI │ Posible │
│ DDL indice │ ── │ NO │ NO │ NO │ NO │ NO │
├─────────────────┼─────┼────────┼─────────┼─────────┼────────┼─────────────┤
│ Entity columna │ Ver │ ── │ SI │ SI │ Posib │ NO │
│ Entity relacion │ Ver │ ── │ SI │ SI │ SI │ Posible │
├─────────────────┼─────┼────────┼─────────┼─────────┼────────┼─────────────┤
│ CreateDto campo │ NO │ NO │ ── │ NO │ NO │ NO │
│ CreateDto valid │ NO │ NO │ ── │ NO │ NO │ NO │
│ ResponseDto │ NO │ NO │ NO │ ── │ Ver │ NO │
├─────────────────┼─────┼────────┼─────────┼─────────┼────────┼─────────────┤
│ Service metodo │ NO │ NO │ NO │ Posib │ ── │ SI │
│ Service logica │ NO │ NO │ NO │ NO │ ── │ NO │
├─────────────────┼─────┼────────┼─────────┼─────────┼────────┼─────────────┤
│ Controller ruta │ NO │ NO │ NO │ NO │ NO │ ── │
│ Controller meth │ NO │ NO │ NO │ NO │ NO │ ── │
└─────────────────┴─────┴────────┴─────────┴─────────┴────────┴─────────────┘
Continuacion → Frontend
┌─────────────────┬───────┬────────┬───────┬───────────┬───────────┐
│ SI CAMBIO... │ Types │ Schema │ Hooks │Components │ Inventory │
├─────────────────┼───────┼────────┼───────┼───────────┼───────────┤
│ DDL columna │ SI │ SI │ NO │ Posible │ DB+MAST │
│ DDL FK │ SI │ SI │ SI │ Posible │ DB+MAST │
│ DDL indice │ NO │ NO │ NO │ NO │ DB │
├─────────────────┼───────┼────────┼───────┼───────────┼───────────┤
│ Entity columna │ SI │ SI │ NO │ Posible │ BE+MAST │
│ Entity relacion │ SI │ SI │ SI │ Posible │ BE+MAST │
├─────────────────┼───────┼────────┼───────┼───────────┼───────────┤
│ CreateDto campo │ NO │ SI │ NO │ Posible │ BE │
│ CreateDto valid │ NO │ SI │ NO │ NO │ NO │
│ ResponseDto │ SI │ NO │ NO │ Posible │ BE │
├─────────────────┼───────┼────────┼───────┼───────────┼───────────┤
│ Service metodo │ Posib │ Posib │ SI │ Posible │ BE │
│ Service logica │ NO │ NO │ NO │ NO │ NO │
├─────────────────┼───────┼────────┼───────┼───────────┼───────────┤
│ Controller ruta │ NO │ NO │ SI │ NO │ BE │
│ Controller meth │ NO │ NO │ SI │ NO │ NO │
├─────────────────┼───────┼────────┼───────┼───────────┼───────────┤
│ Frontend Types │ ── │ Ver │ NO │ Ver │ FE │
│ Frontend Schema │ Ver │ ── │ NO │ Ver │ FE │
│ Frontend Hook │ NO │ NO │ ── │ Ver │ FE │
│ Frontend Comp │ NO │ NO │ NO │ ── │ FE │
└─────────────────┴───────┴────────┴───────┴───────────┴───────────┘
Leyenda:
SI = Siempre actualizar
NO = No requiere actualizacion
Ver = Verificar DDL primero
Posib = Posiblemente, depende del caso
── = No aplica (mismo componente)
```
---
## 9. DEPENDENCIAS POR MODULO
### Modulo Tipico NestJS
```
modules/user/
├── entities/
│ └── user.entity.ts ← Depende de: DDL
│ Impacta: DTOs, Service
├── dto/
│ ├── create-user.dto.ts ← Depende de: Entity
│ │ Impacta: Frontend Schema
│ ├── update-user.dto.ts ← Depende de: CreateDto
│ │ Impacta: Frontend Schema
│ └── user-response.dto.ts ← Depende de: Entity
│ Impacta: Frontend Types
├── services/
│ └── user.service.ts ← Depende de: Entity, DTOs
│ Impacta: Controller
├── controllers/
│ └── user.controller.ts ← Depende de: Service, DTOs
│ Impacta: Frontend Hooks
└── user.module.ts ← Depende de: Todo lo anterior
```
### Modulo Tipico Frontend
```
shared/
├── types/
│ └── user.types.ts ← Depende de: ResponseDto
│ Impacta: Hooks, Components
├── schemas/
│ └── user.schema.ts ← Depende de: CreateDto
│ Impacta: Forms
└── services/
└── user.service.ts ← Depende de: Controller routes
Impacta: Hooks
apps/web/
├── hooks/
│ └── useUsers.ts ← Depende de: Service, Types
│ Impacta: Pages, Components
├── components/
│ └── UserCard.tsx ← Depende de: Types, Hooks
│ Impacta: Pages
└── pages/
└── UsersPage.tsx ← Depende de: Hooks, Components
```
---
## 10. COMANDOS DE VERIFICACION DE DEPENDENCIAS
```bash
# Ver todas las importaciones de un archivo
grep -n "import" src/modules/user/user.service.ts
# Ver quien importa un archivo
grep -rn "from.*user.entity" src/
# Ver dependencias de modulo
grep -rn "UserModule" src/
# Ver uso de tipo en frontend
grep -rn "User" apps/web/
# Arbol de dependencias (si usa herramientas)
npx madge --image graph.svg src/modules/user/
# TypeScript: ver errores de tipo despues de cambio
npm run typecheck 2>&1 | head -50
```
---
## 11. ESCENARIOS COMUNES
### Escenario 1: Agregar campo a tabla existente
```
1. DDL: ALTER TABLE ADD COLUMN
2. Entity: Agregar @Column
3. CreateDto: Agregar campo + validacion
4. ResponseDto: Agregar campo
5. Service: Solo si hay logica especial
6. Frontend Types: Agregar a interface
7. Frontend Schema: Agregar validacion Zod
8. Frontend Components: Agregar UI si necesario
9. Inventarios: DB, BE, FE, MASTER
```
### Escenario 2: Crear nuevo modulo/feature
```
1. DDL: CREATE TABLE
2. Entity: Crear archivo
3. DTOs: Crear Create, Update, Response
4. Service: Crear con CRUD basico
5. Controller: Crear con endpoints REST
6. Module: Crear y registrar en AppModule
7. Frontend Types: Crear interface
8. Frontend Schema: Crear Zod schema
9. Frontend Service: Crear API service
10. Frontend Hook: Crear useQuery/useMutation
11. Frontend Components: Crear UI
12. Inventarios: TODOS
```
### Escenario 3: Refactorizar nombre de campo
```
1. EVALUAR: ¿Es breaking change?
2. OPCION A (breaking): Cambiar en cadena DDL→Entity→DTO→FE
3. OPCION B (seguro):
a. Agregar nuevo campo
b. Migrar datos
c. Actualizar codigo para usar nuevo
d. Eliminar campo viejo
```
---
## 12. ANTI-PATRON: DEPENDENCIAS CIRCULARES
```
❌ INCORRECTO: Dependencia circular entre Services
UserService ──▶ OrderService
▲ │
└───────────────┘
✅ CORRECTO: Extraer a servicio compartido
UserService ──▶ SharedService ◀── OrderService
```
```
❌ INCORRECTO: Frontend importa de Backend
apps/web/types/user.ts
import { UserEntity } from '@backend/modules/user';
✅ CORRECTO: Frontend tiene sus propios tipos
apps/web/types/user.ts
export interface User { ... } // Definido independiente
```
---
**Version:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Matriz de Referencia

View File

@ -0,0 +1,714 @@
# ANTIPATRONES: Lo que NUNCA Hacer
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** OBLIGATORIA - Consultar antes de implementar
**Sistema:** SIMCO + CAPVED
---
## PROPÓSITO
Documentar antipatrones comunes para que agentes y subagentes los eviten. Cada antipatrón incluye el problema, por qué es malo, y la solución correcta.
---
## 1. ANTIPATRONES DE DATABASE
### DB-001: Crear tabla sin schema
```sql
-- ❌ INCORRECTO
CREATE TABLE users (
id UUID PRIMARY KEY
);
-- ✅ CORRECTO
CREATE TABLE auth.users (
id UUID PRIMARY KEY
);
```
**Por qué es malo:** Sin schema, la tabla va a `public`, mezclando dominios y dificultando permisos.
---
### DB-002: Columna NOT NULL sin DEFAULT en tabla existente
```sql
-- ❌ INCORRECTO (en tabla con datos)
ALTER TABLE users ADD COLUMN status VARCHAR(20) NOT NULL;
-- ✅ CORRECTO
ALTER TABLE users ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT 'active';
```
**Por qué es malo:** Falla en INSERT para registros existentes que no tienen valor.
---
### DB-003: Foreign Key sin ON DELETE
```sql
-- ❌ INCORRECTO
CREATE TABLE orders (
user_id UUID REFERENCES users(id)
);
-- ✅ CORRECTO
CREATE TABLE orders (
user_id UUID REFERENCES users(id) ON DELETE CASCADE
-- o ON DELETE SET NULL, ON DELETE RESTRICT según el caso
);
```
**Por qué es malo:** Al eliminar usuario, los orders quedan huérfanos o el DELETE falla sin explicación clara.
---
### DB-004: Usar TEXT cuando debería ser ENUM
```sql
-- ❌ INCORRECTO
CREATE TABLE users (
status TEXT -- Permite cualquier valor
);
-- ✅ CORRECTO
CREATE TYPE user_status AS ENUM ('active', 'inactive', 'suspended');
CREATE TABLE users (
status user_status DEFAULT 'active'
);
-- O con CHECK constraint
CREATE TABLE users (
status VARCHAR(20) CHECK (status IN ('active', 'inactive', 'suspended'))
);
```
**Por qué es malo:** TEXT permite valores inválidos, causando bugs silenciosos.
---
### DB-005: Índice faltante en columna de búsqueda frecuente
```sql
-- ❌ INCORRECTO
CREATE TABLE products (
sku VARCHAR(50) UNIQUE -- Sin índice adicional para búsquedas
);
-- ✅ CORRECTO
CREATE TABLE products (
sku VARCHAR(50) UNIQUE
);
CREATE INDEX idx_products_sku ON products(sku);
-- UNIQUE ya crea índice, pero para búsquedas parciales:
CREATE INDEX idx_products_sku_pattern ON products(sku varchar_pattern_ops);
```
**Por qué es malo:** Queries lentas en tablas grandes (full table scan).
---
### DB-006: Guardar contraseñas en texto plano
```sql
-- ❌ INCORRECTO
CREATE TABLE users (
password VARCHAR(100) -- Almacena "mipassword123"
);
-- ✅ CORRECTO
CREATE TABLE users (
password_hash VARCHAR(255) -- Almacena hash bcrypt
);
-- Hashing se hace en backend, no en SQL
```
**Por qué es malo:** Vulnerabilidad de seguridad crítica.
---
## 2. ANTIPATRONES DE BACKEND
### BE-001: Lógica de negocio en Controller
```typescript
// ❌ INCORRECTO
@Controller('orders')
export class OrderController {
@Post()
async create(@Body() dto: CreateOrderDto) {
// Lógica de negocio en controller
const total = dto.items.reduce((sum, item) => sum + item.price * item.qty, 0);
const tax = total * 0.16;
const discount = dto.couponCode ? await this.calculateDiscount() : 0;
// ... más lógica
}
}
// ✅ CORRECTO
@Controller('orders')
export class OrderController {
@Post()
async create(@Body() dto: CreateOrderDto) {
return this.orderService.create(dto); // Delega a service
}
}
@Injectable()
export class OrderService {
async create(dto: CreateOrderDto) {
const total = this.calculateTotal(dto.items);
const tax = this.calculateTax(total);
// Lógica de negocio en service
}
}
```
**Por qué es malo:** Controller difícil de testear, lógica no reutilizable, violación de SRP.
---
### BE-002: Query directo en Service sin Repository
```typescript
// ❌ INCORRECTO
@Injectable()
export class UserService {
async findActive() {
// Query directo con QueryBuilder
return this.connection.query(`
SELECT * FROM users WHERE status = 'active'
`);
}
}
// ✅ CORRECTO
@Injectable()
export class UserService {
constructor(
@InjectRepository(UserEntity)
private readonly repository: Repository<UserEntity>,
) {}
async findActive() {
return this.repository.find({
where: { status: UserStatus.ACTIVE },
});
}
}
```
**Por qué es malo:** No tipado, vulnerable a SQL injection, difícil de mantener.
---
### BE-003: Validación en Service en lugar de DTO
```typescript
// ❌ INCORRECTO
@Injectable()
export class UserService {
async create(dto: any) {
if (!dto.email) throw new BadRequestException('Email required');
if (!dto.email.includes('@')) throw new BadRequestException('Invalid email');
if (dto.name.length < 2) throw new BadRequestException('Name too short');
// ... más validaciones manuales
}
}
// ✅ CORRECTO
export class CreateUserDto {
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@MinLength(2)
name: string;
}
// Service recibe DTO ya validado
```
**Por qué es malo:** Validación duplicada, inconsistente, no documentada en Swagger.
---
### BE-004: Catch vacío o silencioso
```typescript
// ❌ INCORRECTO
async processPayment() {
try {
await this.paymentGateway.charge();
} catch (e) {
// Silencioso - error perdido
}
}
// ❌ TAMBIÉN INCORRECTO
async processPayment() {
try {
await this.paymentGateway.charge();
} catch (e) {
console.log(e); // Solo log, sin manejar
}
}
// ✅ CORRECTO
async processPayment() {
try {
await this.paymentGateway.charge();
} catch (error) {
this.logger.error('Payment failed', { error, context: 'payment' });
throw new InternalServerErrorException('Error procesando pago');
}
}
```
**Por qué es malo:** Errores silenciosos causan bugs imposibles de debuggear.
---
### BE-005: Entity no alineada con DDL
```typescript
// DDL tiene:
// status VARCHAR(20) CHECK (status IN ('active', 'inactive'))
// ❌ INCORRECTO
@Column()
status: string; // No valida valores
// ✅ CORRECTO
enum UserStatus {
ACTIVE = 'active',
INACTIVE = 'inactive',
}
@Column({ type: 'varchar', length: 20 })
status: UserStatus;
```
**Por qué es malo:** Entity permite valores que BD rechaza, errores en runtime.
---
### BE-006: Hardcodear configuración
```typescript
// ❌ INCORRECTO
const API_URL = 'https://api.stripe.com/v1';
const DB_HOST = '192.168.1.100';
// ✅ CORRECTO
const API_URL = this.configService.get('STRIPE_API_URL');
const DB_HOST = this.configService.get('DB_HOST');
// O usar decorador
@Injectable()
export class PaymentService {
constructor(
@Inject('STRIPE_CONFIG')
private readonly config: StripeConfig,
) {}
}
```
**Por qué es malo:** Difícil cambiar entre ambientes, secretos expuestos en código.
---
### BE-007: N+1 Query Problem
```typescript
// ❌ INCORRECTO
async getOrdersWithItems() {
const orders = await this.orderRepository.find();
for (const order of orders) {
order.items = await this.itemRepository.find({
where: { orderId: order.id }
});
}
return orders;
}
// Resultado: 1 query para orders + N queries para items
// ✅ CORRECTO
async getOrdersWithItems() {
return this.orderRepository.find({
relations: ['items'], // JOIN en una query
});
}
// Resultado: 1 query con JOIN
```
**Por qué es malo:** Performance terrible en listas grandes (100 orders = 101 queries).
---
## 3. ANTIPATRONES DE FRONTEND
### FE-001: Fetch directo en componente
```typescript
// ❌ INCORRECTO
const UserProfile = () => {
const [user, setUser] = useState(null);
useEffect(() => {
fetch('/api/users/me')
.then(res => res.json())
.then(setUser);
}, []);
return <div>{user?.name}</div>;
};
// ✅ CORRECTO
// hooks/useUser.ts
const useUser = () => {
return useQuery({
queryKey: ['user', 'me'],
queryFn: () => userService.getMe(),
});
};
// components/UserProfile.tsx
const UserProfile = () => {
const { data: user, isLoading, error } = useUser();
if (isLoading) return <Loading />;
if (error) return <Error error={error} />;
return <div>{user.name}</div>;
};
```
**Por qué es malo:** No cachea, no maneja loading/error, lógica no reutilizable.
---
### FE-002: Hardcodear URLs de API
```typescript
// ❌ INCORRECTO
const response = await fetch('http://localhost:3000/api/users');
// ✅ CORRECTO
// config.ts
export const API_URL = import.meta.env.VITE_API_URL;
// services/api.ts
const api = axios.create({
baseURL: API_URL,
});
```
**Por qué es malo:** Rompe en producción, difícil cambiar backend.
---
### FE-003: Estado global para todo
```typescript
// ❌ INCORRECTO - Redux/Zustand para estado de formulario
const formStore = create((set) => ({
name: '',
email: '',
setName: (name) => set({ name }),
setEmail: (email) => set({ email }),
}));
// ✅ CORRECTO - Estado local para formularios
const UserForm = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
// O usar react-hook-form
};
// Store global SOLO para:
// - Auth state (usuario logueado)
// - UI state (tema, sidebar abierto)
// - Cache de server state (mejor usar React Query)
```
**Por qué es malo:** Complejidad innecesaria, renders innecesarios, difícil de seguir.
---
### FE-004: Props drilling excesivo
```typescript
// ❌ INCORRECTO
<App user={user} theme={theme}>
<Layout user={user} theme={theme}>
<Sidebar user={user} theme={theme}>
<UserMenu user={user} theme={theme}>
<Avatar user={user} /> // Finalmente se usa
</UserMenu>
</Sidebar>
</Layout>
</App>
// ✅ CORRECTO - Context para datos compartidos
const UserContext = createContext<User | null>(null);
const useUser = () => useContext(UserContext);
<UserProvider value={user}>
<App>
<Layout>
<Sidebar>
<UserMenu>
<Avatar /> // Usa useUser() internamente
</UserMenu>
</Sidebar>
</Layout>
</App>
</UserProvider>
```
**Por qué es malo:** Componentes intermedios reciben props que no usan, refactoring doloroso.
---
### FE-005: useEffect para todo
```typescript
// ❌ INCORRECTO
const ProductList = ({ categoryId }) => {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(`/api/products?category=${categoryId}`)
.then(res => res.json())
.then(data => {
setProducts(data);
setLoading(false);
});
}, [categoryId]);
// Race conditions, no cleanup, no error handling
};
// ✅ CORRECTO - React Query
const ProductList = ({ categoryId }) => {
const { data: products, isLoading } = useQuery({
queryKey: ['products', categoryId],
queryFn: () => productService.getByCategory(categoryId),
});
// Maneja cache, loading, error, race conditions automáticamente
};
```
**Por qué es malo:** Race conditions, memory leaks, re-inventar la rueda.
---
### FE-006: Tipos no sincronizados con backend
```typescript
// Backend DTO (actualizado)
class UserDto {
id: string;
email: string;
name: string;
phone: string; // ← NUEVO CAMPO
}
// ❌ INCORRECTO - Frontend desactualizado
interface User {
id: string;
email: string;
name: string;
// Falta phone → undefined en runtime
}
// ✅ CORRECTO - Mantener sincronizado
interface User {
id: string;
email: string;
name: string;
phone?: string; // Sincronizado con backend
}
```
**Por qué es malo:** Bugs silenciosos en runtime cuando backend cambia.
---
## 4. ANTIPATRONES DE ARQUITECTURA
### ARCH-001: Importar de capa incorrecta
```typescript
// ❌ INCORRECTO - Controller importa de otro módulo directamente
import { ProductEntity } from '../products/entities/product.entity';
// ✅ CORRECTO - Usar exports del módulo
import { ProductService } from '../products/product.module';
// O mejor: dependency injection
@Module({
imports: [ProductModule],
})
export class OrderModule {}
```
**Por qué es malo:** Acopla módulos, rompe encapsulamiento, dependencias circulares.
---
### ARCH-002: Duplicar código entre módulos
```typescript
// ❌ INCORRECTO - Misma función en dos módulos
// users/utils.ts
function formatDate(date: Date) { ... }
// orders/utils.ts
function formatDate(date: Date) { ... } // Duplicado
// ✅ CORRECTO - Shared utils
// shared/utils/date.utils.ts
export function formatDate(date: Date) { ... }
```
**Por qué es malo:** Cambios deben hacerse en múltiples lugares, inconsistencias.
---
### ARCH-003: Circular dependencies
```typescript
// ❌ INCORRECTO
// user.service.ts
import { OrderService } from '../orders/order.service';
// order.service.ts
import { UserService } from '../users/user.service';
// ✅ CORRECTO - Usar forwardRef o reestructurar
@Injectable()
export class UserService {
constructor(
@Inject(forwardRef(() => OrderService))
private orderService: OrderService,
) {}
}
// O mejor: Extraer lógica compartida a un tercer servicio
```
**Por qué es malo:** Errores en runtime, código difícil de entender.
---
## 5. ANTIPATRONES DE TESTING
### TEST-001: Tests que dependen de orden
```typescript
// ❌ INCORRECTO
describe('UserService', () => {
it('should create user', () => {
const user = service.create({ email: 'test@test.com' });
// Asume que este test corre primero
});
it('should find user', () => {
const user = service.findByEmail('test@test.com');
// Depende del test anterior
});
});
// ✅ CORRECTO - Tests independientes
describe('UserService', () => {
beforeEach(async () => {
// Setup limpio para cada test
await repository.clear();
});
it('should create user', () => { ... });
it('should find user', async () => {
// Crear datos necesarios en el test
await repository.save({ email: 'test@test.com' });
const user = await service.findByEmail('test@test.com');
});
});
```
---
### TEST-002: Mock incorrecto
```typescript
// ❌ INCORRECTO - Mock parcial/inconsistente
jest.mock('./user.service', () => ({
findOne: jest.fn().mockResolvedValue({ id: '1' }),
// Otros métodos no mockeados → undefined
}));
// ✅ CORRECTO - Mock completo
const mockUserService = {
findOne: jest.fn(),
findAll: jest.fn(),
create: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
};
beforeEach(() => {
jest.clearAllMocks();
});
```
---
## 6. CHECKLIST DE REVISIÓN
Antes de hacer commit, verificar que NO estás haciendo:
```
Database:
[ ] Tabla sin schema
[ ] NOT NULL sin DEFAULT en tabla existente
[ ] FK sin ON DELETE
[ ] TEXT donde debería ser ENUM
[ ] Índices faltantes
Backend:
[ ] Lógica en Controller
[ ] Queries directas sin Repository
[ ] Validación en Service
[ ] Catch vacío
[ ] Entity desalineada con DDL
[ ] Config hardcodeada
[ ] N+1 queries
Frontend:
[ ] Fetch en componente
[ ] URLs hardcodeadas
[ ] Store global para estado local
[ ] Props drilling excesivo
[ ] useEffect innecesario
[ ] Tipos desactualizados
Arquitectura:
[ ] Import de capa incorrecta
[ ] Código duplicado
[ ] Dependencias circulares
Testing:
[ ] Tests dependientes
[ ] Mocks incompletos
```
---
**Versión:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Guía de Antipatrones

View File

@ -0,0 +1,499 @@
# MAPEO DE TIPOS: PostgreSQL DDL → TypeScript/TypeORM
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** OBLIGATORIA - Consultar antes de crear Entity
**Sistema:** SIMCO + CAPVED
---
## PROPÓSITO
Este documento define el mapeo EXACTO entre tipos de PostgreSQL y TypeScript/TypeORM para garantizar alineación 100% entre DDL y Entities.
---
## REGLA FUNDAMENTAL
```
╔══════════════════════════════════════════════════════════════════════╗
║ DDL ES LA FUENTE DE VERDAD (DDL-First) ║
║ ║
║ 1. DDL define el tipo → Entity lo REFLEJA ║
║ 2. NUNCA crear Entity sin DDL existente ║
║ 3. Si DDL cambia → Entity DEBE cambiar ║
║ 4. Si Entity no coincide → ERROR (corregir Entity, no DDL) ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## TABLA DE MAPEO COMPLETA
### Tipos Numéricos
| PostgreSQL | TypeScript | TypeORM Column | Notas |
|------------|------------|----------------|-------|
| `SMALLINT` | `number` | `@Column({ type: 'smallint' })` | -32,768 a 32,767 |
| `INTEGER` | `number` | `@Column({ type: 'integer' })` | -2B a 2B |
| `BIGINT` | `string` | `@Column({ type: 'bigint' })` | Usar string para precisión |
| `DECIMAL(p,s)` | `string` | `@Column({ type: 'decimal', precision: p, scale: s })` | Usar string para dinero |
| `NUMERIC(p,s)` | `string` | `@Column({ type: 'numeric', precision: p, scale: s })` | Igual que DECIMAL |
| `REAL` | `number` | `@Column({ type: 'real' })` | Punto flotante 4 bytes |
| `DOUBLE PRECISION` | `number` | `@Column({ type: 'double precision' })` | Punto flotante 8 bytes |
| `SERIAL` | `number` | `@PrimaryGeneratedColumn()` | Auto-increment |
| `BIGSERIAL` | `string` | `@PrimaryGeneratedColumn('increment')` | Auto-increment grande |
**Ejemplo Numéricos:**
```sql
-- DDL
CREATE TABLE products (
id SERIAL PRIMARY KEY,
price DECIMAL(10,2) NOT NULL,
quantity INTEGER DEFAULT 0,
rating REAL
);
```
```typescript
// Entity
@Entity({ schema: 'inventory', name: 'products' })
export class ProductEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'decimal', precision: 10, scale: 2 })
price: string; // String para precisión decimal
@Column({ type: 'integer', default: 0 })
quantity: number;
@Column({ type: 'real', nullable: true })
rating: number;
}
```
---
### Tipos de Texto
| PostgreSQL | TypeScript | TypeORM Column | Notas |
|------------|------------|----------------|-------|
| `CHAR(n)` | `string` | `@Column({ type: 'char', length: n })` | Longitud fija |
| `VARCHAR(n)` | `string` | `@Column({ type: 'varchar', length: n })` | Longitud variable |
| `TEXT` | `string` | `@Column({ type: 'text' })` | Sin límite |
**Ejemplo Texto:**
```sql
-- DDL
CREATE TABLE users (
code CHAR(10) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
bio TEXT
);
```
```typescript
// Entity
@Column({ type: 'char', length: 10 })
code: string;
@Column({ type: 'varchar', length: 255, unique: true })
email: string;
@Column({ type: 'text', nullable: true })
bio: string;
```
---
### Tipos de Fecha y Hora
| PostgreSQL | TypeScript | TypeORM Column | Notas |
|------------|------------|----------------|-------|
| `DATE` | `Date` | `@Column({ type: 'date' })` | Solo fecha |
| `TIME` | `string` | `@Column({ type: 'time' })` | Solo hora |
| `TIMESTAMP` | `Date` | `@Column({ type: 'timestamp' })` | Fecha + hora sin TZ |
| `TIMESTAMPTZ` | `Date` | `@Column({ type: 'timestamptz' })` | Fecha + hora con TZ |
| `INTERVAL` | `string` | `@Column({ type: 'interval' })` | Intervalo de tiempo |
**Ejemplo Fechas:**
```sql
-- DDL
CREATE TABLE events (
event_date DATE NOT NULL,
start_time TIME,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
duration INTERVAL
);
```
```typescript
// Entity
@Column({ type: 'date' })
eventDate: Date;
@Column({ type: 'time', nullable: true })
startTime: string;
@CreateDateColumn({ type: 'timestamp' })
createdAt: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt: Date;
@Column({ type: 'interval', nullable: true })
duration: string;
```
---
### Tipos Booleanos
| PostgreSQL | TypeScript | TypeORM Column | Notas |
|------------|------------|----------------|-------|
| `BOOLEAN` | `boolean` | `@Column({ type: 'boolean' })` | true/false |
**Ejemplo Boolean:**
```sql
-- DDL
CREATE TABLE users (
is_active BOOLEAN DEFAULT TRUE,
is_verified BOOLEAN DEFAULT FALSE
);
```
```typescript
// Entity
@Column({ type: 'boolean', default: true })
isActive: boolean;
@Column({ type: 'boolean', default: false })
isVerified: boolean;
```
---
### Tipos UUID
| PostgreSQL | TypeScript | TypeORM Column | Notas |
|------------|------------|----------------|-------|
| `UUID` | `string` | `@Column({ type: 'uuid' })` | Usar con gen_random_uuid() |
**Ejemplo UUID:**
```sql
-- DDL
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id)
);
```
```typescript
// Entity
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ type: 'uuid' })
tenantId: string;
@ManyToOne(() => TenantEntity)
@JoinColumn({ name: 'tenant_id' })
tenant: TenantEntity;
```
---
### Tipos JSON
| PostgreSQL | TypeScript | TypeORM Column | Notas |
|------------|------------|----------------|-------|
| `JSON` | `object \| any` | `@Column({ type: 'json' })` | Sin indexación |
| `JSONB` | `object \| any` | `@Column({ type: 'jsonb' })` | Con indexación |
**Ejemplo JSON:**
```sql
-- DDL
CREATE TABLE users (
preferences JSONB DEFAULT '{}',
metadata JSON
);
```
```typescript
// Entity - Opción 1: any
@Column({ type: 'jsonb', default: {} })
preferences: any;
// Entity - Opción 2: Interface tipada (RECOMENDADO)
interface UserPreferences {
theme: 'light' | 'dark';
notifications: boolean;
language: string;
}
@Column({ type: 'jsonb', default: {} })
preferences: UserPreferences;
@Column({ type: 'json', nullable: true })
metadata: Record<string, any>;
```
---
### Tipos ENUM
| PostgreSQL | TypeScript | TypeORM Column | Notas |
|------------|------------|----------------|-------|
| `CREATE TYPE ... AS ENUM` | `enum` | `@Column({ type: 'enum', enum: X })` | Definir enum TS |
| `VARCHAR CHECK (IN ...)` | `enum \| string` | `@Column()` + validator | Alternativa sin tipo |
**Ejemplo ENUM (Recomendado):**
```sql
-- DDL
CREATE TYPE user_status AS ENUM ('active', 'inactive', 'suspended', 'deleted');
CREATE TABLE users (
status user_status DEFAULT 'active'
);
```
```typescript
// Entity
export enum UserStatus {
ACTIVE = 'active',
INACTIVE = 'inactive',
SUSPENDED = 'suspended',
DELETED = 'deleted',
}
@Column({
type: 'enum',
enum: UserStatus,
default: UserStatus.ACTIVE,
})
status: UserStatus;
```
**Ejemplo VARCHAR con CHECK (Alternativa):**
```sql
-- DDL
CREATE TABLE orders (
status VARCHAR(20) CHECK (status IN ('pending', 'processing', 'completed', 'cancelled'))
);
```
```typescript
// Entity - Usar tipo union + validación en DTO
type OrderStatus = 'pending' | 'processing' | 'completed' | 'cancelled';
@Column({ type: 'varchar', length: 20 })
status: OrderStatus;
// DTO - Validación obligatoria
@IsIn(['pending', 'processing', 'completed', 'cancelled'])
status: string;
```
---
### Tipos Array
| PostgreSQL | TypeScript | TypeORM Column | Notas |
|------------|------------|----------------|-------|
| `INTEGER[]` | `number[]` | `@Column({ type: 'integer', array: true })` | Array de enteros |
| `VARCHAR[]` | `string[]` | `@Column({ type: 'varchar', array: true })` | Array de strings |
| `UUID[]` | `string[]` | `@Column({ type: 'uuid', array: true })` | Array de UUIDs |
**Ejemplo Arrays:**
```sql
-- DDL
CREATE TABLE posts (
tags VARCHAR(50)[],
category_ids UUID[]
);
```
```typescript
// Entity
@Column({ type: 'varchar', array: true, nullable: true })
tags: string[];
@Column({ type: 'uuid', array: true, nullable: true })
categoryIds: string[];
```
---
### Tipos Especiales
| PostgreSQL | TypeScript | TypeORM Column | Notas |
|------------|------------|----------------|-------|
| `BYTEA` | `Buffer` | `@Column({ type: 'bytea' })` | Datos binarios |
| `INET` | `string` | `@Column({ type: 'inet' })` | Dirección IP |
| `CIDR` | `string` | `@Column({ type: 'cidr' })` | Red IP |
| `MACADDR` | `string` | `@Column({ type: 'macaddr' })` | Dirección MAC |
---
## MAPEO DE CONSTRAINTS
### NOT NULL
```sql
-- DDL
email VARCHAR(255) NOT NULL
```
```typescript
// Entity
@Column({ type: 'varchar', length: 255, nullable: false })
email: string; // Sin ? = no nullable
```
### DEFAULT
```sql
-- DDL
created_at TIMESTAMP DEFAULT NOW()
status VARCHAR(20) DEFAULT 'active'
```
```typescript
// Entity
@CreateDateColumn({ type: 'timestamp' })
createdAt: Date;
@Column({ type: 'varchar', length: 20, default: 'active' })
status: string;
```
### UNIQUE
```sql
-- DDL
email VARCHAR(255) UNIQUE
```
```typescript
// Entity
@Column({ type: 'varchar', length: 255, unique: true })
email: string;
```
### CHECK (No soportado directamente en TypeORM)
```sql
-- DDL
age INTEGER CHECK (age >= 0 AND age <= 150)
```
```typescript
// Entity - Sin validación en Entity
@Column({ type: 'integer' })
age: number;
// DTO - OBLIGATORIO validar aquí
@IsInt()
@Min(0)
@Max(150)
age: number;
```
---
## MAPEO DE RELACIONES
### ONE-TO-MANY / MANY-TO-ONE
```sql
-- DDL
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE
);
```
```typescript
// Entity Order
@ManyToOne(() => UserEntity, user => user.orders, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'user_id' })
user: UserEntity;
@Column({ type: 'uuid' })
userId: string;
// Entity User
@OneToMany(() => OrderEntity, order => order.user)
orders: OrderEntity[];
```
### MANY-TO-MANY
```sql
-- DDL
CREATE TABLE user_roles (
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
PRIMARY KEY (user_id, role_id)
);
```
```typescript
// Entity User
@ManyToMany(() => RoleEntity, role => role.users)
@JoinTable({
name: 'user_roles',
joinColumn: { name: 'user_id', referencedColumnName: 'id' },
inverseJoinColumn: { name: 'role_id', referencedColumnName: 'id' },
})
roles: RoleEntity[];
// Entity Role
@ManyToMany(() => UserEntity, user => user.roles)
users: UserEntity[];
```
---
## CHECKLIST DE ALINEACIÓN
Antes de completar Entity, verificar:
```
[ ] Cada columna DDL tiene @Column en Entity
[ ] Tipos PostgreSQL mapeados correctamente (ver tabla)
[ ] nullable: false para NOT NULL
[ ] default: X para DEFAULT X
[ ] unique: true para UNIQUE
[ ] Relaciones (@ManyToOne, etc.) coinciden con FOREIGN KEY
[ ] onDelete coincide con ON DELETE
[ ] Nombre de tabla correcto en @Entity({ name: 'x' })
[ ] Schema correcto en @Entity({ schema: 'x' })
[ ] PK coincide (@PrimaryGeneratedColumn vs @PrimaryColumn)
```
---
## ERRORES COMUNES
| Error | Causa | Solución |
|-------|-------|----------|
| `bigint` retorna string inesperado | BIGINT se mapea a string | Usar `string` en TypeScript |
| Decimales pierden precisión | DECIMAL mapeado a number | Usar `string` para dinero |
| Enum no reconocido | Tipo no creado en BD | Crear TYPE primero en DDL |
| Array vacío falla | Array sin default | Agregar `default: []` |
| Fecha inválida | TIMESTAMP vs TIMESTAMPTZ | Usar TIMESTAMPTZ para TZ |
---
## REFERENCIAS
- SIMCO-DDL.md - Crear tablas
- SIMCO-BACKEND.md - Crear entities
- PRINCIPIO-VALIDACION-OBLIGATORIA.md - Validar alineación
---
**Versión:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Patrón de Mapeo

View File

@ -0,0 +1,539 @@
# NOMENCLATURA UNIFICADA
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** OBLIGATORIA - Seguir en todo el código
**Sistema:** SIMCO + CAPVED
---
## PROPÓSITO
Definir convenciones de nomenclatura consistentes para todas las capas del sistema.
---
## 1. DIRECTORIOS
### Regla General
```
lowercase-kebab-case
```
### Ejemplos
```
✅ CORRECTO ❌ INCORRECTO
───────────────────────────────────────
user-management/ UserManagement/
auth-module/ AuthModule/
shared-utils/ sharedUtils/
api-v1/ API_V1/
```
### Estructura por Capa
```
DATABASE:
db/
├── schemas/
│ ├── auth/
│ │ └── tables/
│ ├── core/
│ └── {domain}/
├── seeds/
│ ├── dev/
│ └── prod/
└── scripts/
BACKEND (NestJS):
src/
├── modules/
│ └── {module-name}/
│ ├── entities/
│ ├── dto/
│ ├── services/
│ ├── controllers/
│ └── tests/
├── shared/
│ ├── config/
│ ├── guards/
│ ├── decorators/
│ ├── filters/
│ ├── interceptors/
│ └── utils/
└── main.ts
FRONTEND (React):
src/
├── apps/
│ └── {app-name}/
│ ├── components/
│ ├── pages/
│ └── hooks/
├── shared/
│ ├── components/
│ │ ├── ui/ # Componentes base
│ │ └── common/ # Componentes compartidos
│ ├── hooks/
│ ├── stores/
│ ├── services/
│ ├── types/
│ └── utils/
└── main.tsx
```
---
## 2. ARCHIVOS
### Por Capa
| Capa | Patrón | Ejemplo |
|------|--------|---------|
| **DDL** | `{NN}-{nombre}.sql` | `01-users.sql`, `02-products.sql` |
| **Seed** | `{NN}-{nombre}.sql` | `01-admin-user.sql` |
| **Entity** | `{nombre}.entity.ts` | `user.entity.ts` |
| **DTO Create** | `create-{nombre}.dto.ts` | `create-user.dto.ts` |
| **DTO Update** | `update-{nombre}.dto.ts` | `update-user.dto.ts` |
| **DTO Response** | `{nombre}-response.dto.ts` | `user-response.dto.ts` |
| **DTO Query** | `{nombre}-query.dto.ts` | `user-query.dto.ts` |
| **Service** | `{nombre}.service.ts` | `user.service.ts` |
| **Controller** | `{nombre}.controller.ts` | `user.controller.ts` |
| **Module** | `{nombre}.module.ts` | `user.module.ts` |
| **Guard** | `{nombre}.guard.ts` | `jwt-auth.guard.ts` |
| **Strategy** | `{nombre}.strategy.ts` | `jwt.strategy.ts` |
| **Filter** | `{nombre}.filter.ts` | `http-exception.filter.ts` |
| **Interceptor** | `{nombre}.interceptor.ts` | `logging.interceptor.ts` |
| **Decorator** | `{nombre}.decorator.ts` | `current-user.decorator.ts` |
| **Component** | `{Nombre}.tsx` | `UserCard.tsx` |
| **Page** | `{Nombre}Page.tsx` | `UserProfilePage.tsx` |
| **Hook** | `use{Nombre}.ts` | `useUser.ts`, `useAuth.ts` |
| **Store** | `{nombre}.store.ts` | `auth.store.ts` |
| **Service (FE)** | `{nombre}.service.ts` | `user.service.ts` |
| **Types** | `{nombre}.types.ts` | `user.types.ts` |
| **Test Unit** | `{nombre}.spec.ts` | `user.service.spec.ts` |
| **Test E2E** | `{nombre}.e2e-spec.ts` | `user.e2e-spec.ts` |
| **Test Component** | `{Nombre}.test.tsx` | `UserCard.test.tsx` |
### DDL - Orden de Archivos
```sql
-- Usar prefijo numérico para orden de ejecución
01-users.sql -- Tablas base primero
02-roles.sql -- Tablas relacionadas
03-user-roles.sql -- Junction tables después
04-permissions.sql
10-products.sql -- Otro dominio, nuevo rango
11-categories.sql
12-product-categories.sql
```
---
## 3. CLASES Y TIPOS
### TypeScript/NestJS
| Tipo | Patrón | Ejemplo |
|------|--------|---------|
| **Entity** | `{Nombre}Entity` | `UserEntity` |
| **DTO** | `{Accion}{Nombre}Dto` | `CreateUserDto` |
| **Service** | `{Nombre}Service` | `UserService` |
| **Controller** | `{Nombre}Controller` | `UserController` |
| **Module** | `{Nombre}Module` | `UserModule` |
| **Guard** | `{Nombre}Guard` | `JwtAuthGuard` |
| **Strategy** | `{Nombre}Strategy` | `JwtStrategy` |
| **Filter** | `{Nombre}Filter` | `HttpExceptionFilter` |
| **Interceptor** | `{Nombre}Interceptor` | `LoggingInterceptor` |
| **Enum** | `{Nombre}` | `UserStatus`, `OrderState` |
| **Interface** | `{Nombre}` o `I{Nombre}` | `User`, `IUserService` |
| **Type** | `{Nombre}` | `UserWithRoles`, `OrderSummary` |
### Ejemplos Completos
```typescript
// Entity
@Entity({ schema: 'auth', name: 'users' })
export class UserEntity { ... }
// DTOs
export class CreateUserDto { ... }
export class UpdateUserDto extends PartialType(CreateUserDto) { ... }
export class UserResponseDto { ... }
export class UserQueryDto { ... }
// Service
@Injectable()
export class UserService { ... }
// Controller
@Controller('users')
export class UserController { ... }
// Module
@Module({})
export class UserModule { ... }
// Enum
export enum UserStatus {
ACTIVE = 'active',
INACTIVE = 'inactive',
}
// Interface
export interface User {
id: string;
email: string;
}
// Type
export type UserWithRoles = User & { roles: Role[] };
```
---
## 4. FUNCIONES Y MÉTODOS
### Verbos Estándar
| Operación | Verbo | Ejemplo |
|-----------|-------|---------|
| Obtener uno | `find`, `get` | `findById()`, `getUser()` |
| Obtener muchos | `findAll`, `list` | `findAll()`, `listUsers()` |
| Crear | `create` | `create()`, `createUser()` |
| Actualizar | `update` | `update()`, `updateUser()` |
| Eliminar | `remove`, `delete` | `remove()`, `deleteUser()` |
| Buscar | `search` | `search()`, `searchUsers()` |
| Verificar | `is`, `has`, `can` | `isActive()`, `hasPermission()` |
| Contar | `count` | `count()`, `countActive()` |
| Validar | `validate` | `validateEmail()` |
| Formatear | `format` | `formatDate()`, `formatPrice()` |
| Parsear | `parse` | `parseJson()`, `parseDate()` |
| Convertir | `to`, `from` | `toDto()`, `fromEntity()` |
### Ejemplos por Capa
```typescript
// Service - CRUD estándar
class UserService {
async create(dto: CreateUserDto): Promise<UserEntity> { ... }
async findAll(query: UserQueryDto): Promise<UserEntity[]> { ... }
async findById(id: string): Promise<UserEntity> { ... }
async findByEmail(email: string): Promise<UserEntity | null> { ... }
async update(id: string, dto: UpdateUserDto): Promise<UserEntity> { ... }
async remove(id: string): Promise<void> { ... }
// Métodos adicionales
async activate(id: string): Promise<void> { ... }
async deactivate(id: string): Promise<void> { ... }
async assignRole(userId: string, roleId: string): Promise<void> { ... }
async hasPermission(userId: string, permission: string): Promise<boolean> { ... }
}
// Controller - Endpoints REST
class UserController {
@Get()
async findAll(@Query() query: UserQueryDto) { ... }
@Get(':id')
async findOne(@Param('id') id: string) { ... }
@Post()
async create(@Body() dto: CreateUserDto) { ... }
@Put(':id')
async update(@Param('id') id: string, @Body() dto: UpdateUserDto) { ... }
@Delete(':id')
async remove(@Param('id') id: string) { ... }
}
// Hook (Frontend)
function useUsers() {
return useQuery({ ... });
}
function useUser(id: string) {
return useQuery({ ... });
}
function useCreateUser() {
return useMutation({ ... });
}
function useUpdateUser() {
return useMutation({ ... });
}
function useDeleteUser() {
return useMutation({ ... });
}
```
---
## 5. VARIABLES Y CONSTANTES
### Variables
```typescript
// camelCase
const userId = '123';
const isActive = true;
const userCount = 10;
const createdAt = new Date();
// Arrays en plural
const users = [];
const activeUsers = [];
const userIds = ['1', '2', '3'];
// Maps/Records con sufijo
const userMap = new Map<string, User>();
const roleById = {}; // Record<string, Role>
```
### Constantes
```typescript
// UPPER_SNAKE_CASE para constantes
const MAX_LOGIN_ATTEMPTS = 5;
const DEFAULT_PAGE_SIZE = 20;
const API_VERSION = 'v1';
const JWT_EXPIRATION = '1d';
// Constantes de configuración
const CONFIG = {
MAX_FILE_SIZE: 10 * 1024 * 1024, // 10MB
ALLOWED_EXTENSIONS: ['.jpg', '.png', '.pdf'],
RATE_LIMIT: 100,
};
// Rutas/Paths
const ROUTES = {
HOME: '/',
LOGIN: '/auth/login',
DASHBOARD: '/dashboard',
USER_PROFILE: '/users/:id',
};
```
---
## 6. ENUMS
### Definición
```typescript
// PascalCase para nombre
// Valores en minúsculas (para BD) o UPPER_CASE (para constantes)
// Opción 1: Valores string (recomendado para BD)
export enum UserStatus {
ACTIVE = 'active',
INACTIVE = 'inactive',
SUSPENDED = 'suspended',
DELETED = 'deleted',
}
// Opción 2: Valores UPPER_CASE (para constantes internas)
export enum HttpMethod {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
DELETE = 'DELETE',
}
// Opción 3: Numéricos (solo si necesario)
export enum Priority {
LOW = 1,
MEDIUM = 2,
HIGH = 3,
CRITICAL = 4,
}
```
### Uso
```typescript
// Entity
@Column({ type: 'enum', enum: UserStatus, default: UserStatus.ACTIVE })
status: UserStatus;
// Validación DTO
@IsEnum(UserStatus)
status: UserStatus;
// Frontend
const statusLabel = {
[UserStatus.ACTIVE]: 'Activo',
[UserStatus.INACTIVE]: 'Inactivo',
[UserStatus.SUSPENDED]: 'Suspendido',
};
```
---
## 7. DATABASE (SQL)
### Tablas y Columnas
```sql
-- Tablas: snake_case, plural
CREATE TABLE users ( ... );
CREATE TABLE product_categories ( ... );
CREATE TABLE user_login_attempts ( ... );
-- Columnas: snake_case
CREATE TABLE users (
id UUID PRIMARY KEY,
first_name VARCHAR(100),
last_name VARCHAR(100),
email VARCHAR(255),
is_active BOOLEAN,
created_at TIMESTAMP,
updated_at TIMESTAMP,
deleted_at TIMESTAMP
);
-- Foreign Keys: {tabla_singular}_id
CREATE TABLE orders (
user_id UUID REFERENCES users(id),
product_id UUID REFERENCES products(id)
);
-- Junction Tables: {tabla1}_{tabla2} (orden alfabético)
CREATE TABLE role_users ( ... ); -- ❌
CREATE TABLE user_roles ( ... ); -- ✅ (u antes de r)
```
### Índices y Constraints
```sql
-- Índices: idx_{tabla}_{columnas}
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_orders_user_created ON orders(user_id, created_at);
-- Unique: uq_{tabla}_{columnas}
ALTER TABLE users ADD CONSTRAINT uq_users_email UNIQUE (email);
-- Foreign Key: fk_{tabla}_{referencia}
ALTER TABLE orders ADD CONSTRAINT fk_orders_user
FOREIGN KEY (user_id) REFERENCES users(id);
-- Check: chk_{tabla}_{descripcion}
ALTER TABLE users ADD CONSTRAINT chk_users_status
CHECK (status IN ('active', 'inactive'));
```
---
## 8. API ENDPOINTS
### Convenciones REST
```
GET /api/v1/users # Listar
GET /api/v1/users/:id # Obtener uno
POST /api/v1/users # Crear
PUT /api/v1/users/:id # Actualizar completo
PATCH /api/v1/users/:id # Actualizar parcial
DELETE /api/v1/users/:id # Eliminar
# Recursos anidados
GET /api/v1/users/:id/orders # Órdenes del usuario
POST /api/v1/users/:id/orders # Crear orden para usuario
# Acciones especiales
POST /api/v1/users/:id/activate # Acción
POST /api/v1/users/:id/deactivate # Acción
POST /api/v1/auth/login # Auth
POST /api/v1/auth/logout # Auth
POST /api/v1/auth/refresh # Auth
```
### URL Patterns
```
✅ CORRECTO ❌ INCORRECTO
──────────────────────────────────────────
/users /user # Plural
/users/:id /users/get/:id # Verbo innecesario
/users/:id/orders /getUserOrders # Snake case, verbo
/auth/login /doLogin # Verbo innecesario
```
---
## 9. COMPONENTES REACT
### Nombres
```typescript
// PascalCase para componentes
const UserCard = () => { ... };
const ProductList = () => { ... };
const LoginForm = () => { ... };
// Sufijos descriptivos
const UserProfilePage = () => { ... }; // Página completa
const UserEditModal = () => { ... }; // Modal
const UserDeleteDialog = () => { ... }; // Dialog de confirmación
const UserAvatarSkeleton = () => { ... }; // Loading skeleton
```
### Props
```typescript
// Interface con Props suffix
interface UserCardProps {
user: User;
onEdit?: (user: User) => void;
onDelete?: (id: string) => void;
isLoading?: boolean;
}
const UserCard: React.FC<UserCardProps> = ({
user,
onEdit,
onDelete,
isLoading = false,
}) => { ... };
```
---
## 10. CHECKLIST DE NOMENCLATURA
```
Antes de crear archivo:
[ ] Nombre en formato correcto para su tipo
[ ] Directorio correcto según capa
[ ] Sin duplicados en nombre
Antes de crear clase/tipo:
[ ] PascalCase
[ ] Sufijo correcto (Entity, Dto, Service, etc.)
[ ] Nombre descriptivo
Antes de crear función:
[ ] camelCase
[ ] Verbo al inicio
[ ] Nombre describe qué hace
Antes de crear variable:
[ ] camelCase
[ ] Nombre descriptivo
[ ] Plural para arrays
Antes de crear endpoint:
[ ] Recurso en plural
[ ] Sin verbos en URL
[ ] Estructura RESTful
```
---
**Versión:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Guía de Nomenclatura

View File

@ -0,0 +1,745 @@
# PATRON DE CONFIGURACION
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** RECOMENDADA - Seguir para consistencia
**Sistema:** SIMCO + CAPVED
---
## PROPOSITO
Definir patrones estandarizados para manejo de configuracion en todas las capas, incluyendo variables de entorno, archivos de configuracion, y validacion de settings.
---
## 1. PRINCIPIOS FUNDAMENTALES
```
╔══════════════════════════════════════════════════════════════════════╗
║ PRINCIPIOS DE CONFIGURACION ║
╠══════════════════════════════════════════════════════════════════════╣
║ ║
║ 1. NUNCA hardcodear valores sensibles ║
║ 2. SIEMPRE validar configuracion al iniciar ║
║ 3. FALLAR RAPIDO si configuracion es invalida ║
║ 4. SEPARAR configuracion por ambiente ║
║ 5. CENTRALIZAR acceso a configuracion ║
║ 6. DOCUMENTAR cada variable requerida ║
║ ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## 2. ESTRUCTURA DE ARCHIVOS
### Estructura Recomendada
```
project/
├── .env.example # Template con TODAS las variables (sin valores reales)
├── .env # Valores locales (NUNCA en git)
├── .env.development # Override para desarrollo
├── .env.staging # Override para staging
├── .env.production # Override para produccion (solo en servidor)
├── apps/backend/
│ └── src/
│ └── shared/
│ └── config/
│ ├── configuration.ts # Configuracion principal
│ ├── config.validation.ts # Schema de validacion
│ ├── database.config.ts # Config de BD
│ ├── jwt.config.ts # Config de JWT
│ └── index.ts # Export centralizado
└── apps/frontend/
└── src/
└── shared/
└── config/
├── env.ts # Variables de entorno
└── constants.ts # Constantes de app
```
### .env.example (Template)
```bash
# ═══════════════════════════════════════════════════════════════════
# CONFIGURACION DE BASE DE DATOS
# ═══════════════════════════════════════════════════════════════════
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp_development
DB_USER=postgres
DB_PASSWORD= # REQUERIDO: Password de BD
DB_SSL=false
# ═══════════════════════════════════════════════════════════════════
# CONFIGURACION DE JWT/AUTH
# ═══════════════════════════════════════════════════════════════════
JWT_SECRET= # REQUERIDO: Minimo 32 caracteres
JWT_EXPIRATION=1d
JWT_REFRESH_EXPIRATION=7d
# ═══════════════════════════════════════════════════════════════════
# CONFIGURACION DE SERVIDOR
# ═══════════════════════════════════════════════════════════════════
NODE_ENV=development
PORT=3000
API_PREFIX=api
CORS_ORIGINS=http://localhost:5173
# ═══════════════════════════════════════════════════════════════════
# CONFIGURACION DE SERVICIOS EXTERNOS
# ═══════════════════════════════════════════════════════════════════
REDIS_URL=redis://localhost:6379
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=
# ═══════════════════════════════════════════════════════════════════
# CONFIGURACION DE LOGGING
# ═══════════════════════════════════════════════════════════════════
LOG_LEVEL=debug # trace|debug|info|warn|error
LOG_FORMAT=pretty # pretty|json
```
---
## 3. BACKEND (NestJS)
### Schema de Validacion con Joi
```typescript
// src/shared/config/config.validation.ts
import * as Joi from 'joi';
export const configValidationSchema = Joi.object({
// Database
DB_HOST: Joi.string().required(),
DB_PORT: Joi.number().default(5432),
DB_NAME: Joi.string().required(),
DB_USER: Joi.string().required(),
DB_PASSWORD: Joi.string().required(),
DB_SSL: Joi.boolean().default(false),
// JWT
JWT_SECRET: Joi.string().min(32).required(),
JWT_EXPIRATION: Joi.string().default('1d'),
JWT_REFRESH_EXPIRATION: Joi.string().default('7d'),
// Server
NODE_ENV: Joi.string()
.valid('development', 'staging', 'production', 'test')
.default('development'),
PORT: Joi.number().default(3000),
API_PREFIX: Joi.string().default('api'),
CORS_ORIGINS: Joi.string().default('*'),
// Redis (opcional)
REDIS_URL: Joi.string().uri().optional(),
// SMTP (opcional)
SMTP_HOST: Joi.string().optional(),
SMTP_PORT: Joi.number().optional(),
SMTP_USER: Joi.string().optional(),
SMTP_PASSWORD: Joi.string().optional(),
// Logging
LOG_LEVEL: Joi.string()
.valid('trace', 'debug', 'info', 'warn', 'error')
.default('info'),
LOG_FORMAT: Joi.string().valid('pretty', 'json').default('json'),
});
```
### Configuracion Tipada
```typescript
// src/shared/config/configuration.ts
export interface DatabaseConfig {
host: string;
port: number;
name: string;
user: string;
password: string;
ssl: boolean;
}
export interface JwtConfig {
secret: string;
expiration: string;
refreshExpiration: string;
}
export interface ServerConfig {
nodeEnv: string;
port: number;
apiPrefix: string;
corsOrigins: string[];
}
export interface AppConfig {
database: DatabaseConfig;
jwt: JwtConfig;
server: ServerConfig;
redis?: {
url: string;
};
smtp?: {
host: string;
port: number;
user: string;
password: string;
};
logging: {
level: string;
format: string;
};
}
export default (): AppConfig => ({
database: {
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT, 10),
name: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
ssl: process.env.DB_SSL === 'true',
},
jwt: {
secret: process.env.JWT_SECRET,
expiration: process.env.JWT_EXPIRATION,
refreshExpiration: process.env.JWT_REFRESH_EXPIRATION,
},
server: {
nodeEnv: process.env.NODE_ENV,
port: parseInt(process.env.PORT, 10),
apiPrefix: process.env.API_PREFIX,
corsOrigins: process.env.CORS_ORIGINS?.split(',') || ['*'],
},
redis: process.env.REDIS_URL
? { url: process.env.REDIS_URL }
: undefined,
smtp: process.env.SMTP_HOST
? {
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT, 10),
user: process.env.SMTP_USER,
password: process.env.SMTP_PASSWORD,
}
: undefined,
logging: {
level: process.env.LOG_LEVEL,
format: process.env.LOG_FORMAT,
},
});
```
### Registro en AppModule
```typescript
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import configuration from './shared/config/configuration';
import { configValidationSchema } from './shared/config/config.validation';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [configuration],
validationSchema: configValidationSchema,
validationOptions: {
abortEarly: true, // Fallar en primer error
},
expandVariables: true, // Permitir ${VAR} en valores
}),
// ... otros modulos
],
})
export class AppModule {}
```
### Uso en Services
```typescript
// src/modules/auth/services/auth.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtConfig } from '@/shared/config/configuration';
@Injectable()
export class AuthService {
private readonly jwtConfig: JwtConfig;
constructor(private readonly configService: ConfigService) {
// Acceso tipado a configuracion
this.jwtConfig = this.configService.get<JwtConfig>('jwt');
}
async generateToken(userId: string): Promise<string> {
return this.jwtService.sign(
{ sub: userId },
{
secret: this.jwtConfig.secret,
expiresIn: this.jwtConfig.expiration,
},
);
}
}
```
### Configuraciones Especificas
```typescript
// src/shared/config/database.config.ts
import { registerAs } from '@nestjs/config';
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
export default registerAs('database', (): TypeOrmModuleOptions => ({
type: 'postgres',
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT, 10),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
ssl: process.env.DB_SSL === 'true'
? { rejectUnauthorized: false }
: false,
autoLoadEntities: true,
synchronize: false, // NUNCA true en produccion
logging: process.env.NODE_ENV === 'development',
}));
```
```typescript
// src/shared/config/jwt.config.ts
import { registerAs } from '@nestjs/config';
import { JwtModuleOptions } from '@nestjs/jwt';
export default registerAs('jwt', (): JwtModuleOptions => ({
secret: process.env.JWT_SECRET,
signOptions: {
expiresIn: process.env.JWT_EXPIRATION || '1d',
},
}));
```
---
## 4. FRONTEND (React/Vite)
### Variables de Entorno
```typescript
// src/shared/config/env.ts
// Vite expone variables con prefijo VITE_
interface EnvConfig {
apiUrl: string;
apiTimeout: number;
environment: 'development' | 'staging' | 'production';
enableMockApi: boolean;
sentryDsn?: string;
gaTrackingId?: string;
}
function validateEnv(): EnvConfig {
const apiUrl = import.meta.env.VITE_API_URL;
const environment = import.meta.env.VITE_ENVIRONMENT || 'development';
// Validar variables requeridas
if (!apiUrl) {
throw new Error('VITE_API_URL is required');
}
return {
apiUrl,
apiTimeout: parseInt(import.meta.env.VITE_API_TIMEOUT || '30000', 10),
environment: environment as EnvConfig['environment'],
enableMockApi: import.meta.env.VITE_ENABLE_MOCK_API === 'true',
sentryDsn: import.meta.env.VITE_SENTRY_DSN,
gaTrackingId: import.meta.env.VITE_GA_TRACKING_ID,
};
}
export const env = validateEnv();
// Helpers
export const isDev = env.environment === 'development';
export const isProd = env.environment === 'production';
export const isStaging = env.environment === 'staging';
```
### Constantes de Aplicacion
```typescript
// src/shared/config/constants.ts
import { env } from './env';
export const APP_CONFIG = {
// API
API_URL: env.apiUrl,
API_TIMEOUT: env.apiTimeout,
// Paginacion
DEFAULT_PAGE_SIZE: 20,
MAX_PAGE_SIZE: 100,
// UI
TOAST_DURATION: 5000,
DEBOUNCE_DELAY: 300,
// Storage keys
STORAGE_KEYS: {
AUTH_TOKEN: 'auth_token',
REFRESH_TOKEN: 'refresh_token',
USER_PREFERENCES: 'user_preferences',
THEME: 'theme',
},
// Feature flags (pueden venir de API)
FEATURES: {
DARK_MODE: true,
NOTIFICATIONS: true,
BETA_FEATURES: env.environment !== 'production',
},
} as const;
// Rutas
export const ROUTES = {
HOME: '/',
LOGIN: '/auth/login',
REGISTER: '/auth/register',
DASHBOARD: '/dashboard',
PROFILE: '/profile',
SETTINGS: '/settings',
USERS: '/users',
USER_DETAIL: '/users/:id',
} as const;
// API Endpoints
export const API_ENDPOINTS = {
AUTH: {
LOGIN: '/auth/login',
REGISTER: '/auth/register',
REFRESH: '/auth/refresh',
LOGOUT: '/auth/logout',
},
USERS: {
BASE: '/users',
BY_ID: (id: string) => `/users/${id}`,
ME: '/users/me',
},
// ... otros endpoints
} as const;
```
### Uso en Componentes
```typescript
// src/apps/web/hooks/useAuth.ts
import { APP_CONFIG } from '@/shared/config/constants';
export const useAuth = () => {
const login = async (credentials: LoginCredentials) => {
const response = await api.post(API_ENDPOINTS.AUTH.LOGIN, credentials);
// Guardar token usando key centralizada
localStorage.setItem(
APP_CONFIG.STORAGE_KEYS.AUTH_TOKEN,
response.data.accessToken,
);
};
return { login, /* ... */ };
};
```
---
## 5. DATABASE
### Configuracion de Conexion
```typescript
// src/shared/config/typeorm.config.ts
import { DataSource, DataSourceOptions } from 'typeorm';
import * as dotenv from 'dotenv';
dotenv.config();
export const dataSourceOptions: DataSourceOptions = {
type: 'postgres',
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT, 10),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
ssl: process.env.DB_SSL === 'true'
? { rejectUnauthorized: false }
: false,
// Entities
entities: ['dist/**/*.entity.js'],
// Migrations
migrations: ['dist/migrations/*.js'],
migrationsTableName: 'migrations',
// Logging
logging: process.env.DB_LOGGING === 'true',
maxQueryExecutionTime: 1000, // Log queries > 1s
// Pool
extra: {
max: parseInt(process.env.DB_POOL_SIZE || '10', 10),
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 10000,
},
};
// Para CLI de TypeORM
export default new DataSource(dataSourceOptions);
```
---
## 6. PATRON POR AMBIENTE
### Configuracion Condicional
```typescript
// src/shared/config/by-environment.ts
type Environment = 'development' | 'staging' | 'production' | 'test';
interface EnvironmentConfig {
api: {
rateLimit: number;
timeout: number;
};
cache: {
ttl: number;
enabled: boolean;
};
features: {
debugMode: boolean;
mockExternalApis: boolean;
};
}
const configs: Record<Environment, EnvironmentConfig> = {
development: {
api: {
rateLimit: 1000, // Muy alto para desarrollo
timeout: 60000,
},
cache: {
ttl: 60,
enabled: false, // Deshabilitado para desarrollo
},
features: {
debugMode: true,
mockExternalApis: true,
},
},
staging: {
api: {
rateLimit: 100,
timeout: 30000,
},
cache: {
ttl: 300,
enabled: true,
},
features: {
debugMode: true,
mockExternalApis: false,
},
},
production: {
api: {
rateLimit: 60,
timeout: 15000,
},
cache: {
ttl: 3600,
enabled: true,
},
features: {
debugMode: false,
mockExternalApis: false,
},
},
test: {
api: {
rateLimit: 10000,
timeout: 5000,
},
cache: {
ttl: 0,
enabled: false,
},
features: {
debugMode: true,
mockExternalApis: true,
},
},
};
export function getEnvironmentConfig(): EnvironmentConfig {
const env = (process.env.NODE_ENV || 'development') as Environment;
return configs[env];
}
```
---
## 7. SECRETOS Y SEGURIDAD
### Nunca en Codigo
```typescript
// ❌ INCORRECTO: Secretos hardcodeados
const JWT_SECRET = 'my-super-secret-key-12345';
const DB_PASSWORD = 'password123';
// ✅ CORRECTO: Desde variables de entorno
const JWT_SECRET = process.env.JWT_SECRET;
const DB_PASSWORD = process.env.DB_PASSWORD;
```
### Validar Secretos Requeridos
```typescript
// src/shared/config/secrets.validation.ts
const REQUIRED_SECRETS = [
'JWT_SECRET',
'DB_PASSWORD',
];
const OPTIONAL_SECRETS = [
'SMTP_PASSWORD',
'STRIPE_SECRET_KEY',
];
export function validateSecrets(): void {
const missing: string[] = [];
for (const secret of REQUIRED_SECRETS) {
if (!process.env[secret]) {
missing.push(secret);
}
}
if (missing.length > 0) {
throw new Error(
`Missing required secrets: ${missing.join(', ')}\n` +
'Please check your .env file or environment variables.',
);
}
// Validar formato de secretos
if (process.env.JWT_SECRET && process.env.JWT_SECRET.length < 32) {
throw new Error('JWT_SECRET must be at least 32 characters');
}
}
```
### Rotacion de Secretos
```typescript
// Soportar multiples secretos para rotacion
const JWT_SECRETS = (process.env.JWT_SECRETS || process.env.JWT_SECRET).split(',');
// Verificar token con cualquier secreto valido
async function verifyToken(token: string): Promise<TokenPayload> {
for (const secret of JWT_SECRETS) {
try {
return jwt.verify(token, secret.trim()) as TokenPayload;
} catch {
continue; // Probar siguiente secreto
}
}
throw new UnauthorizedException('Invalid token');
}
// Firmar siempre con el primer secreto (mas reciente)
function signToken(payload: TokenPayload): string {
return jwt.sign(payload, JWT_SECRETS[0].trim());
}
```
---
## 8. CHECKLIST DE CONFIGURACION
```
Setup inicial:
[ ] .env.example creado con TODAS las variables
[ ] .env en .gitignore
[ ] Schema de validacion implementado
[ ] App falla si configuracion invalida
[ ] Tipos definidos para configuracion
Seguridad:
[ ] Ningun secreto en codigo fuente
[ ] Ningun secreto en logs
[ ] Secretos rotables (multiples valores soportados)
[ ] Variables sensibles marcadas en documentacion
Por ambiente:
[ ] Configuracion diferenciada por ambiente
[ ] Defaults seguros para produccion
[ ] Modo debug deshabilitado en produccion
[ ] Rate limiting apropiado por ambiente
Documentacion:
[ ] Cada variable documentada en .env.example
[ ] README explica como configurar
[ ] Variables opcionales vs requeridas claras
```
---
## 9. ANTI-PATRONES
```typescript
// ❌ ANTI-PATRON 1: Configuracion dispersa
// archivo1.ts
const API_URL = 'http://api.com';
// archivo2.ts
const apiUrl = process.env.API_URL || 'http://api.com';
// ✅ CORRECTO: Centralizado
// config/constants.ts
export const API_URL = process.env.API_URL;
```
```typescript
// ❌ ANTI-PATRON 2: No validar
const port = process.env.PORT; // Puede ser undefined o string invalido
server.listen(port); // Error en runtime
// ✅ CORRECTO: Validar y convertir
const port = parseInt(process.env.PORT, 10);
if (isNaN(port)) {
throw new Error('PORT must be a valid number');
}
```
```typescript
// ❌ ANTI-PATRON 3: Defaults inseguros
const DEBUG = process.env.DEBUG || true; // Debug activo por default
// ✅ CORRECTO: Defaults seguros
const DEBUG = process.env.DEBUG === 'true'; // False por default
```
---
**Version:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Patron de Codigo

View File

@ -0,0 +1,534 @@
# PATRÓN: MANEJO DE EXCEPCIONES
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Aplica a:** Backend (NestJS/Express)
**Prioridad:** OBLIGATORIA
---
## PROPÓSITO
Definir patrones estándar de manejo de errores para garantizar respuestas consistentes y debugging efectivo.
---
## PRINCIPIO FUNDAMENTAL
```
╔══════════════════════════════════════════════════════════════════════╗
║ EXCEPCIONES CLARAS Y CONSISTENTES ║
║ ║
║ 1. Usar HttpException estándar de NestJS ║
║ 2. Mensajes claros para el usuario ║
║ 3. Detalles técnicos en logs (no en response) ║
║ 4. Códigos HTTP semánticos ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## 1. EXCEPCIONES HTTP ESTÁNDAR
### Matriz de Decisión
| Situación | Exception | HTTP Code | Cuándo Usar |
|-----------|-----------|-----------|-------------|
| Recurso no existe | `NotFoundException` | 404 | `findOne` retorna null |
| Ya existe (duplicado) | `ConflictException` | 409 | Violación de unique |
| Datos inválidos | `BadRequestException` | 400 | Validación de negocio falla |
| Sin autenticación | `UnauthorizedException` | 401 | Token falta o inválido |
| Sin permiso | `ForbiddenException` | 403 | Autenticado pero sin permiso |
| Método no permitido | `MethodNotAllowedException` | 405 | HTTP method incorrecto |
| Payload muy grande | `PayloadTooLargeException` | 413 | Archivo/body excede límite |
| Rate limit | `TooManyRequestsException` | 429 | Muchas peticiones |
| Error interno | `InternalServerErrorException` | 500 | Error inesperado |
| Servicio no disponible | `ServiceUnavailableException` | 503 | DB/API externa caída |
---
## 2. PATRONES POR CASO
### Not Found (404)
```typescript
// PATRÓN: Recurso no encontrado
async findOne(id: string): Promise<UserEntity> {
const user = await this.repository.findOne({ where: { id } });
if (!user) {
throw new NotFoundException(`Usuario con ID ${id} no encontrado`);
}
return user;
}
// PATRÓN: Recurso relacionado no encontrado
async assignRole(userId: string, roleId: string): Promise<void> {
const user = await this.userRepository.findOne({ where: { id: userId } });
if (!user) {
throw new NotFoundException(`Usuario ${userId} no encontrado`);
}
const role = await this.roleRepository.findOne({ where: { id: roleId } });
if (!role) {
throw new NotFoundException(`Rol ${roleId} no encontrado`);
}
// Proceder...
}
```
### Conflict (409)
```typescript
// PATRÓN: Duplicado por campo único
async create(dto: CreateUserDto): Promise<UserEntity> {
const existing = await this.repository.findOne({
where: { email: dto.email },
});
if (existing) {
throw new ConflictException('El email ya está registrado');
}
return this.repository.save(this.repository.create(dto));
}
// PATRÓN: Duplicado con múltiples campos
async createProduct(dto: CreateProductDto): Promise<ProductEntity> {
const existing = await this.repository.findOne({
where: {
sku: dto.sku,
tenantId: dto.tenantId,
},
});
if (existing) {
throw new ConflictException(
`Ya existe un producto con SKU ${dto.sku} en este tenant`
);
}
return this.repository.save(this.repository.create(dto));
}
```
### Bad Request (400)
```typescript
// PATRÓN: Validación de negocio
async transfer(dto: TransferDto): Promise<void> {
if (dto.fromAccountId === dto.toAccountId) {
throw new BadRequestException(
'La cuenta origen y destino no pueden ser iguales'
);
}
const fromAccount = await this.findAccount(dto.fromAccountId);
if (fromAccount.balance < dto.amount) {
throw new BadRequestException('Saldo insuficiente para la transferencia');
}
// Proceder...
}
// PATRÓN: Estado inválido para operación
async cancelOrder(orderId: string): Promise<void> {
const order = await this.findOne(orderId);
if (order.status === 'delivered') {
throw new BadRequestException(
'No se puede cancelar un pedido ya entregado'
);
}
if (order.status === 'cancelled') {
throw new BadRequestException('El pedido ya está cancelado');
}
// Proceder...
}
```
### Forbidden (403)
```typescript
// PATRÓN: Sin permiso sobre recurso
async update(userId: string, dto: UpdateUserDto, currentUser: User): Promise<UserEntity> {
const user = await this.findOne(userId);
// Solo el usuario mismo o admin puede editar
if (user.id !== currentUser.id && !currentUser.roles.includes('admin')) {
throw new ForbiddenException('No tienes permiso para editar este usuario');
}
return this.repository.save({ ...user, ...dto });
}
// PATRÓN: Límite de plan/tenant
async createProject(dto: CreateProjectDto, tenant: Tenant): Promise<Project> {
const projectCount = await this.repository.count({
where: { tenantId: tenant.id },
});
if (projectCount >= tenant.plan.maxProjects) {
throw new ForbiddenException(
`Tu plan permite máximo ${tenant.plan.maxProjects} proyectos. ` +
'Actualiza tu plan para crear más.'
);
}
// Proceder...
}
```
### Unauthorized (401)
```typescript
// PATRÓN: Token inválido (en Guard)
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
handleRequest(err: any, user: any, info: any) {
if (err || !user) {
if (info?.name === 'TokenExpiredError') {
throw new UnauthorizedException('Tu sesión ha expirado');
}
if (info?.name === 'JsonWebTokenError') {
throw new UnauthorizedException('Token inválido');
}
throw new UnauthorizedException('No autenticado');
}
return user;
}
}
// PATRÓN: Credenciales incorrectas
async login(dto: LoginDto): Promise<TokenResponse> {
const user = await this.userRepository.findOne({
where: { email: dto.email },
});
if (!user || !(await bcrypt.compare(dto.password, user.password))) {
throw new UnauthorizedException('Credenciales incorrectas');
}
// Generar token...
}
```
### Internal Server Error (500)
```typescript
// PATRÓN: Error inesperado con logging
async processPayment(dto: PaymentDto): Promise<PaymentResult> {
try {
const result = await this.paymentGateway.charge(dto);
return result;
} catch (error) {
// Log detallado para debugging
this.logger.error('Error procesando pago', {
dto,
error: error.message,
stack: error.stack,
gatewayResponse: error.response?.data,
});
// Respuesta genérica al usuario
throw new InternalServerErrorException(
'Error procesando el pago. Por favor intenta de nuevo.'
);
}
}
```
---
## 3. ESTRUCTURA DE RESPUESTA DE ERROR
### Formato Estándar
```typescript
// Respuesta de error estándar
interface ErrorResponse {
statusCode: number;
message: string | string[];
error: string;
timestamp: string;
path: string;
}
// Ejemplo de respuesta
{
"statusCode": 404,
"message": "Usuario con ID abc-123 no encontrado",
"error": "Not Found",
"timestamp": "2024-01-15T10:30:00.000Z",
"path": "/api/v1/users/abc-123"
}
```
### Exception Filter Global
```typescript
// filters/http-exception.filter.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
Logger,
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(GlobalExceptionFilter.name);
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
let status = HttpStatus.INTERNAL_SERVER_ERROR;
let message: string | string[] = 'Error interno del servidor';
let error = 'Internal Server Error';
if (exception instanceof HttpException) {
status = exception.getStatus();
const exceptionResponse = exception.getResponse();
if (typeof exceptionResponse === 'object') {
message = (exceptionResponse as any).message || exception.message;
error = (exceptionResponse as any).error || exception.name;
} else {
message = exceptionResponse;
}
} else if (exception instanceof Error) {
// Log error interno completo
this.logger.error('Unhandled exception', {
message: exception.message,
stack: exception.stack,
path: request.url,
method: request.method,
body: request.body,
user: (request as any).user?.id,
});
}
response.status(status).json({
statusCode: status,
message,
error,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
```
---
## 4. EXCEPCIONES PERSONALIZADAS
### Cuándo Crear Excepción Custom
```typescript
// CREAR excepción custom cuando:
// 1. Necesitas información adicional estructurada
// 2. El error es específico del dominio
// 3. Quieres diferenciar en handling
// NO crear custom para errores HTTP estándar
// ❌ class UserNotFoundException extends HttpException {} // Usar NotFoundException
```
### Ejemplo Excepción Custom
```typescript
// exceptions/business.exception.ts
export class InsufficientBalanceException extends BadRequestException {
constructor(
public readonly currentBalance: number,
public readonly requiredAmount: number,
) {
super({
message: `Saldo insuficiente. Tienes $${currentBalance}, necesitas $${requiredAmount}`,
error: 'Insufficient Balance',
currentBalance,
requiredAmount,
deficit: requiredAmount - currentBalance,
});
}
}
// Uso
if (account.balance < amount) {
throw new InsufficientBalanceException(account.balance, amount);
}
```
### Excepción para Errores de Integración
```typescript
// exceptions/integration.exception.ts
export class PaymentGatewayException extends ServiceUnavailableException {
constructor(
public readonly gateway: string,
public readonly originalError: string,
) {
super({
message: 'Error de conexión con el servicio de pagos',
error: 'Payment Gateway Error',
gateway,
});
}
}
// Uso
try {
await stripe.charges.create(params);
} catch (error) {
throw new PaymentGatewayException('Stripe', error.message);
}
```
---
## 5. LOGGING DE ERRORES
### Niveles de Log
```typescript
// logger.service.ts
@Injectable()
export class AppLogger {
private readonly logger = new Logger();
// ERROR: Errores que requieren atención
error(message: string, context: object) {
this.logger.error(message, { ...context, timestamp: new Date() });
}
// WARN: Situaciones anómalas pero manejadas
warn(message: string, context: object) {
this.logger.warn(message, { ...context, timestamp: new Date() });
}
// INFO: Eventos importantes del negocio
info(message: string, context: object) {
this.logger.log(message, { ...context, timestamp: new Date() });
}
// DEBUG: Información para desarrollo
debug(message: string, context: object) {
this.logger.debug(message, { ...context, timestamp: new Date() });
}
}
```
### Qué Loggear
```typescript
// SIEMPRE loggear en ERROR:
{
message: 'Descripción del error',
stack: error.stack, // Stack trace
userId: currentUser?.id, // Quién causó el error
tenantId: currentUser?.tenant, // Contexto de tenant
requestId: request.id, // Para tracing
path: request.url, // Endpoint
method: request.method, // HTTP method
body: sanitize(request.body), // Body (sin passwords)
query: request.query, // Query params
timestamp: new Date(), // Cuándo
}
// NUNCA loggear:
// - Passwords
// - Tokens
// - Tarjetas de crédito
// - Datos sensibles (CURP, RFC, etc.)
```
---
## 6. DOCUMENTACIÓN SWAGGER
```typescript
// Documentar posibles errores en controller
@Post()
@ApiOperation({ summary: 'Crear usuario' })
@ApiResponse({ status: 201, description: 'Usuario creado', type: UserEntity })
@ApiResponse({ status: 400, description: 'Datos inválidos' })
@ApiResponse({ status: 409, description: 'Email ya registrado' })
@ApiResponse({ status: 401, description: 'No autenticado' })
@ApiResponse({ status: 403, description: 'Sin permisos' })
async create(@Body() dto: CreateUserDto): Promise<UserEntity> {
return this.service.create(dto);
}
```
---
## 7. CHECKLIST DE MANEJO DE ERRORES
```
Service:
[ ] Cada método que busca por ID usa NotFoundException si no existe
[ ] Operaciones de creación verifican duplicados → ConflictException
[ ] Validaciones de negocio usan BadRequestException
[ ] Verificaciones de permisos usan ForbiddenException
[ ] Errores de integraciones externas tienen try/catch
[ ] Errores inesperados se loggean con contexto completo
[ ] Mensajes de error son claros para el usuario
Controller:
[ ] Swagger documenta posibles errores
[ ] @ApiResponse para cada código de error posible
Global:
[ ] GlobalExceptionFilter configurado
[ ] Logger configurado para errores
[ ] Errores no exponen detalles técnicos en producción
```
---
## ANTI-PATRONES
```typescript
// ❌ NUNCA: Mensaje genérico sin contexto
throw new BadRequestException('Error');
// ✅ SIEMPRE: Mensaje descriptivo
throw new BadRequestException('El email ya está registrado');
// ❌ NUNCA: Exponer stack trace en response
throw new Error(error.stack);
// ✅ SIEMPRE: Log interno, mensaje limpio al usuario
this.logger.error('Error detallado', { stack: error.stack });
throw new InternalServerErrorException('Error procesando solicitud');
// ❌ NUNCA: Catch vacío
try { ... } catch (e) { }
// ✅ SIEMPRE: Manejar o re-lanzar
try { ... } catch (e) {
this.logger.error('Context', { error: e });
throw new InternalServerErrorException('Mensaje usuario');
}
// ❌ NUNCA: 500 para errores de validación
throw new InternalServerErrorException('Email inválido');
// ✅ SIEMPRE: Código HTTP semántico
throw new BadRequestException('Email inválido');
```
---
**Versión:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Patrón de Excepciones

View File

@ -0,0 +1,663 @@
# PATRON DE LOGGING
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** RECOMENDADA - Seguir para consistencia
**Sistema:** SIMCO + CAPVED
---
## PROPOSITO
Definir patrones estandarizados de logging para todas las capas del sistema, asegurando trazabilidad, debugging efectivo y monitoreo en produccion.
---
## 1. NIVELES DE LOG
```
╔══════════════════════════════════════════════════════════════════════╗
║ NIVELES DE LOG (de menor a mayor severidad) ║
╠══════════════════════════════════════════════════════════════════════╣
║ ║
║ TRACE → Detalle extremo (solo desarrollo) ║
║ DEBUG → Informacion de debugging ║
║ INFO → Eventos normales del sistema ║
║ WARN → Situaciones anormales pero manejables ║
║ ERROR → Errores que afectan funcionalidad ║
║ FATAL → Errores criticos que detienen el sistema ║
║ ║
╚══════════════════════════════════════════════════════════════════════╝
```
### Cuando Usar Cada Nivel
| Nivel | Uso | Ejemplo |
|-------|-----|---------|
| **TRACE** | Flujo detallado de ejecucion | `Entering method findById with id=123` |
| **DEBUG** | Variables, estados internos | `User cache hit: userId=123` |
| **INFO** | Eventos de negocio normales | `Order created: orderId=456` |
| **WARN** | Situaciones inesperadas no criticas | `Retry attempt 2/3 for external API` |
| **ERROR** | Errores que necesitan atencion | `Failed to process payment: timeout` |
| **FATAL** | Sistema no puede continuar | `Database connection lost` |
---
## 2. ESTRUCTURA DE LOG
### Formato Estandar
```typescript
{
timestamp: "2025-12-08T10:30:45.123Z", // ISO 8601
level: "INFO", // Nivel
context: "UserService", // Clase/modulo origen
message: "User created successfully", // Mensaje descriptivo
correlationId: "req-abc123", // ID de request/transaccion
userId: "user-456", // Usuario (si aplica)
data: { // Datos adicionales
email: "user@example.com",
action: "create"
},
duration: 45 // Duracion en ms (si aplica)
}
```
### Campos Obligatorios
| Campo | Descripcion | Siempre |
|-------|-------------|---------|
| `timestamp` | Fecha/hora ISO 8601 | SI |
| `level` | Nivel del log | SI |
| `context` | Origen del log | SI |
| `message` | Descripcion del evento | SI |
| `correlationId` | ID para trazar request | EN PRODUCCION |
### Campos Opcionales Recomendados
| Campo | Cuando Usar |
|-------|-------------|
| `userId` | Cuando hay usuario autenticado |
| `data` | Datos relevantes al evento |
| `duration` | Para operaciones medibles |
| `error` | Cuando es log de error |
| `stack` | Stack trace en errores |
---
## 3. BACKEND (NestJS)
### Configuracion del Logger
```typescript
// src/shared/logger/logger.service.ts
import { Injectable, LoggerService, Scope } from '@nestjs/common';
import { Logger } from 'winston';
@Injectable({ scope: Scope.TRANSIENT })
export class AppLogger implements LoggerService {
private context: string;
private correlationId: string;
constructor(private readonly logger: Logger) {}
setContext(context: string) {
this.context = context;
}
setCorrelationId(correlationId: string) {
this.correlationId = correlationId;
}
log(message: string, data?: Record<string, any>) {
this.logger.info(message, {
context: this.context,
correlationId: this.correlationId,
...data,
});
}
error(message: string, trace?: string, data?: Record<string, any>) {
this.logger.error(message, {
context: this.context,
correlationId: this.correlationId,
stack: trace,
...data,
});
}
warn(message: string, data?: Record<string, any>) {
this.logger.warn(message, {
context: this.context,
correlationId: this.correlationId,
...data,
});
}
debug(message: string, data?: Record<string, any>) {
this.logger.debug(message, {
context: this.context,
correlationId: this.correlationId,
...data,
});
}
}
```
### Configuracion Winston
```typescript
// src/shared/logger/winston.config.ts
import * as winston from 'winston';
const { combine, timestamp, json, printf, colorize } = winston.format;
// Formato para desarrollo
const devFormat = combine(
colorize(),
timestamp(),
printf(({ timestamp, level, message, context, ...meta }) => {
return `${timestamp} [${context}] ${level}: ${message} ${
Object.keys(meta).length ? JSON.stringify(meta) : ''
}`;
}),
);
// Formato para produccion (JSON estructurado)
const prodFormat = combine(
timestamp(),
json(),
);
export const winstonConfig: winston.LoggerOptions = {
level: process.env.LOG_LEVEL || 'info',
format: process.env.NODE_ENV === 'production' ? prodFormat : devFormat,
transports: [
new winston.transports.Console(),
// En produccion: agregar transports adicionales
// new winston.transports.File({ filename: 'error.log', level: 'error' }),
],
};
```
### Uso en Service
```typescript
// src/modules/user/services/user.service.ts
@Injectable()
export class UserService {
constructor(
private readonly logger: AppLogger,
private readonly repository: Repository<UserEntity>,
) {
this.logger.setContext(UserService.name);
}
async create(dto: CreateUserDto): Promise<UserEntity> {
this.logger.log('Creating new user', { email: dto.email });
try {
const user = await this.repository.save(dto);
this.logger.log('User created successfully', {
userId: user.id,
email: user.email,
});
return user;
} catch (error) {
this.logger.error('Failed to create user', error.stack, {
email: dto.email,
errorCode: error.code,
});
throw error;
}
}
async findById(id: string): Promise<UserEntity> {
this.logger.debug('Finding user by ID', { userId: id });
const startTime = Date.now();
const user = await this.repository.findOne({ where: { id } });
const duration = Date.now() - startTime;
if (!user) {
this.logger.warn('User not found', { userId: id, duration });
throw new NotFoundException(`User ${id} not found`);
}
this.logger.debug('User found', { userId: id, duration });
return user;
}
}
```
### Interceptor para Correlation ID
```typescript
// src/shared/interceptors/correlation.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { v4 as uuidv4 } from 'uuid';
@Injectable()
export class CorrelationInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
// Usar header existente o generar nuevo
const correlationId = request.headers['x-correlation-id'] || uuidv4();
// Guardar en request para uso posterior
request.correlationId = correlationId;
// Agregar a response headers
const response = context.switchToHttp().getResponse();
response.setHeader('x-correlation-id', correlationId);
return next.handle();
}
}
```
### Interceptor de Logging de Requests
```typescript
// src/shared/interceptors/logging.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
constructor(private readonly logger: AppLogger) {
this.logger.setContext('HTTP');
}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const { method, url, correlationId, user } = request;
const startTime = Date.now();
this.logger.log('Incoming request', {
method,
url,
correlationId,
userId: user?.id,
});
return next.handle().pipe(
tap({
next: () => {
const duration = Date.now() - startTime;
this.logger.log('Request completed', {
method,
url,
correlationId,
duration,
statusCode: context.switchToHttp().getResponse().statusCode,
});
},
error: (error) => {
const duration = Date.now() - startTime;
this.logger.error('Request failed', error.stack, {
method,
url,
correlationId,
duration,
errorMessage: error.message,
});
},
}),
);
}
}
```
---
## 4. FRONTEND (React)
### Logger Service
```typescript
// src/shared/services/logger.service.ts
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
interface LogEntry {
timestamp: string;
level: LogLevel;
message: string;
context?: string;
data?: Record<string, any>;
userId?: string;
}
class LoggerService {
private isDev = process.env.NODE_ENV === 'development';
private userId: string | null = null;
setUserId(userId: string | null) {
this.userId = userId;
}
private log(level: LogLevel, message: string, context?: string, data?: Record<string, any>) {
const entry: LogEntry = {
timestamp: new Date().toISOString(),
level,
message,
context,
data,
userId: this.userId || undefined,
};
// En desarrollo: console
if (this.isDev) {
const consoleMethod = level === 'error' ? console.error :
level === 'warn' ? console.warn :
level === 'debug' ? console.debug : console.log;
consoleMethod(`[${entry.context}] ${message}`, data || '');
}
// En produccion: enviar a servicio de logging
if (!this.isDev && (level === 'error' || level === 'warn')) {
this.sendToServer(entry);
}
}
private async sendToServer(entry: LogEntry) {
try {
await fetch('/api/logs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(entry),
});
} catch {
// Silently fail - don't cause more errors
}
}
debug(message: string, context?: string, data?: Record<string, any>) {
this.log('debug', message, context, data);
}
info(message: string, context?: string, data?: Record<string, any>) {
this.log('info', message, context, data);
}
warn(message: string, context?: string, data?: Record<string, any>) {
this.log('warn', message, context, data);
}
error(message: string, context?: string, data?: Record<string, any>) {
this.log('error', message, context, data);
}
}
export const logger = new LoggerService();
```
### Uso en Componentes
```typescript
// src/apps/web/pages/UsersPage.tsx
import { logger } from '@/shared/services/logger.service';
export const UsersPage = () => {
const { data, error, isLoading } = useUsers();
useEffect(() => {
logger.info('Users page mounted', 'UsersPage');
}, []);
useEffect(() => {
if (error) {
logger.error('Failed to load users', 'UsersPage', {
errorMessage: error.message,
});
}
}, [error]);
const handleDelete = async (userId: string) => {
logger.info('Deleting user', 'UsersPage', { userId });
try {
await deleteUser(userId);
logger.info('User deleted successfully', 'UsersPage', { userId });
} catch (err) {
logger.error('Failed to delete user', 'UsersPage', {
userId,
error: err.message,
});
}
};
return (/* ... */);
};
```
### Error Boundary con Logging
```typescript
// src/shared/components/ErrorBoundary.tsx
import { Component, ErrorInfo, ReactNode } from 'react';
import { logger } from '@/shared/services/logger.service';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
}
export class ErrorBoundary extends Component<Props, State> {
state = { hasError: false };
static getDerivedStateFromError(): State {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
logger.error('React error boundary caught error', 'ErrorBoundary', {
error: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
});
}
render() {
if (this.state.hasError) {
return this.props.fallback || <div>Something went wrong</div>;
}
return this.props.children;
}
}
```
---
## 5. DATABASE
### Logging de Queries (TypeORM)
```typescript
// src/shared/config/typeorm.config.ts
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
export const typeOrmConfig: TypeOrmModuleOptions = {
// ... otras opciones
logging: process.env.NODE_ENV === 'development'
? ['query', 'error', 'warn']
: ['error'],
logger: 'advanced-console', // o custom logger
maxQueryExecutionTime: 1000, // Log queries > 1s
};
```
### Custom Query Logger
```typescript
// src/shared/logger/typeorm.logger.ts
import { Logger as TypeOrmLogger } from 'typeorm';
import { AppLogger } from './logger.service';
export class CustomTypeOrmLogger implements TypeOrmLogger {
constructor(private readonly logger: AppLogger) {
this.logger.setContext('TypeORM');
}
logQuery(query: string, parameters?: any[]) {
this.logger.debug('Query executed', {
query: query.substring(0, 500), // Truncar queries largas
parameters,
});
}
logQueryError(error: string, query: string, parameters?: any[]) {
this.logger.error('Query failed', undefined, {
error,
query: query.substring(0, 500),
parameters,
});
}
logQuerySlow(time: number, query: string, parameters?: any[]) {
this.logger.warn('Slow query detected', {
duration: time,
query: query.substring(0, 500),
parameters,
});
}
logSchemaBuild(message: string) {
this.logger.info(message);
}
logMigration(message: string) {
this.logger.info(message);
}
log(level: 'log' | 'info' | 'warn', message: any) {
if (level === 'warn') {
this.logger.warn(message);
} else {
this.logger.log(message);
}
}
}
```
---
## 6. QUE LOGUEAR Y QUE NO
### SI Loguear
```
✅ Inicio/fin de operaciones importantes
✅ Errores y excepciones (con contexto)
✅ Eventos de autenticacion (login, logout, failed attempts)
✅ Operaciones de negocio criticas (pagos, cambios de estado)
✅ Llamadas a APIs externas (request/response resumido)
✅ Queries lentas (>1s)
✅ Warnings de recursos (memoria, conexiones)
✅ Cambios de configuracion en runtime
```
### NO Loguear
```
❌ Datos sensibles (passwords, tokens, tarjetas)
❌ PII sin necesidad (emails completos, nombres)
❌ Cada iteracion de loops
❌ Contenido completo de requests/responses grandes
❌ Logs de debug en produccion
❌ Informacion redundante
❌ Stack traces en logs INFO/DEBUG
```
### Sanitizacion de Datos Sensibles
```typescript
// src/shared/utils/log-sanitizer.ts
const SENSITIVE_FIELDS = ['password', 'token', 'secret', 'authorization', 'credit_card'];
export function sanitizeForLogging(data: Record<string, any>): Record<string, any> {
const sanitized = { ...data };
for (const key of Object.keys(sanitized)) {
if (SENSITIVE_FIELDS.some(field => key.toLowerCase().includes(field))) {
sanitized[key] = '[REDACTED]';
} else if (typeof sanitized[key] === 'object' && sanitized[key] !== null) {
sanitized[key] = sanitizeForLogging(sanitized[key]);
}
}
return sanitized;
}
// Uso
this.logger.log('User login attempt', sanitizeForLogging({
email: dto.email,
password: dto.password, // Se convierte en [REDACTED]
}));
```
---
## 7. CONFIGURACION POR AMBIENTE
```typescript
// src/shared/config/logger.config.ts
export const loggerConfig = {
development: {
level: 'debug',
format: 'pretty',
includeTimestamp: true,
colorize: true,
},
staging: {
level: 'info',
format: 'json',
includeTimestamp: true,
colorize: false,
},
production: {
level: 'warn',
format: 'json',
includeTimestamp: true,
colorize: false,
// Enviar a servicio externo
externalService: {
enabled: true,
endpoint: process.env.LOG_ENDPOINT,
},
},
};
```
---
## 8. CHECKLIST DE LOGGING
```
Antes de hacer deploy:
[ ] Logs no contienen datos sensibles
[ ] Nivel de log apropiado para ambiente
[ ] Errores tienen contexto suficiente para debug
[ ] Correlation ID implementado
[ ] Queries lentas se detectan
[ ] Error boundary implementado en frontend
En cada Service nuevo:
[ ] Logger inyectado y contexto configurado
[ ] Operaciones principales logueadas
[ ] Errores logueados con stack trace
[ ] Tiempos de operaciones criticas medidos
```
---
**Version:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Patron de Codigo

View File

@ -0,0 +1,657 @@
# PATRON DE PERFORMANCE
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** RECOMENDADA - Seguir para optimizacion
**Sistema:** SIMCO + CAPVED
---
## PROPOSITO
Definir patrones de optimizacion de rendimiento para todas las capas del sistema, asegurando tiempos de respuesta aceptables y uso eficiente de recursos.
---
## 1. METRICAS OBJETIVO
```
╔══════════════════════════════════════════════════════════════════════╗
║ OBJETIVOS DE PERFORMANCE ║
╠══════════════════════════════════════════════════════════════════════╣
║ ║
║ API Response Time: ║
║ • P50: < 100ms
║ • P95: < 500ms
║ • P99: < 1000ms
║ ║
║ Database Queries: ║
║ • Simple query: < 10ms
║ • Complex query: < 100ms
║ • Report query: < 1000ms
║ ║
║ Frontend: ║
║ • First Contentful Paint: < 1.5s
║ • Time to Interactive: < 3s
║ • Largest Contentful Paint: < 2.5s
║ ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## 2. DATABASE PERFORMANCE
### 2.1 Indices Efectivos
```sql
-- Indices para columnas frecuentemente filtradas
CREATE INDEX idx_users_email ON auth.users(email);
CREATE INDEX idx_users_status ON auth.users(status);
-- Indice compuesto para queries frecuentes
CREATE INDEX idx_orders_user_created
ON core.orders(user_id, created_at DESC);
-- Indice parcial para datos activos
CREATE INDEX idx_users_active
ON auth.users(email)
WHERE status = 'active';
-- Indice para busqueda de texto
CREATE INDEX idx_products_name_gin
ON core.products USING gin(to_tsvector('spanish', name));
```
### 2.2 Analisis de Queries
```sql
-- Ver plan de ejecucion
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE user_id = 'uuid-123'
AND created_at > NOW() - INTERVAL '30 days';
-- Identificar queries lentas
SELECT query, calls, mean_time, total_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
```
### 2.3 Evitar N+1 Queries
```typescript
// ❌ INCORRECTO: N+1 queries
async findAllWithOrders(): Promise<User[]> {
const users = await this.userRepository.find();
// N queries adicionales para cargar orders
for (const user of users) {
user.orders = await this.orderRepository.find({
where: { userId: user.id }
});
}
return users;
}
// ✅ CORRECTO: Join en una query
async findAllWithOrders(): Promise<User[]> {
return this.userRepository.find({
relations: ['orders'], // TypeORM hace JOIN
});
}
// ✅ CORRECTO: QueryBuilder con control
async findAllWithOrders(): Promise<User[]> {
return this.userRepository
.createQueryBuilder('user')
.leftJoinAndSelect('user.orders', 'order')
.where('user.status = :status', { status: 'active' })
.orderBy('user.createdAt', 'DESC')
.getMany();
}
```
### 2.4 Paginacion Eficiente
```typescript
// ❌ INCORRECTO: OFFSET para paginas grandes
async findPaginated(page: number, limit: number) {
return this.repository.find({
skip: (page - 1) * limit, // Lento en paginas grandes
take: limit,
});
}
// ✅ CORRECTO: Cursor-based pagination
async findPaginatedByCursor(cursor?: string, limit: number = 20) {
const qb = this.repository
.createQueryBuilder('item')
.orderBy('item.createdAt', 'DESC')
.take(limit + 1); // +1 para saber si hay mas
if (cursor) {
const decodedCursor = this.decodeCursor(cursor);
qb.where('item.createdAt < :cursor', { cursor: decodedCursor });
}
const items = await qb.getMany();
const hasMore = items.length > limit;
if (hasMore) {
items.pop(); // Remover el extra
}
return {
data: items,
nextCursor: hasMore ? this.encodeCursor(items[items.length - 1]) : null,
hasMore,
};
}
```
### 2.5 Select Solo Campos Necesarios
```typescript
// ❌ INCORRECTO: Traer toda la entidad
const users = await this.userRepository.find();
// ✅ CORRECTO: Solo campos necesarios
const users = await this.userRepository.find({
select: ['id', 'email', 'firstName'],
});
// ✅ CORRECTO: Con QueryBuilder
const users = await this.userRepository
.createQueryBuilder('user')
.select(['user.id', 'user.email', 'user.firstName'])
.getMany();
```
---
## 3. BACKEND PERFORMANCE
### 3.1 Caching con Redis
```typescript
// src/shared/cache/cache.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
@Injectable()
export class CacheService {
constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}
async get<T>(key: string): Promise<T | null> {
return this.cacheManager.get<T>(key);
}
async set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {
await this.cacheManager.set(key, value, ttlSeconds * 1000);
}
async del(key: string): Promise<void> {
await this.cacheManager.del(key);
}
async delByPattern(pattern: string): Promise<void> {
const keys = await this.cacheManager.store.keys(pattern);
await Promise.all(keys.map(key => this.cacheManager.del(key)));
}
}
```
### 3.2 Cache Decorator
```typescript
// src/shared/decorators/cached.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const CACHE_KEY = 'cache_key';
export const CACHE_TTL = 'cache_ttl';
export const Cached = (key: string, ttlSeconds: number = 300) => {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
SetMetadata(CACHE_KEY, key)(target, propertyKey, descriptor);
SetMetadata(CACHE_TTL, ttlSeconds)(target, propertyKey, descriptor);
};
};
// Interceptor que implementa el caching
@Injectable()
export class CacheInterceptor implements NestInterceptor {
constructor(
private readonly cacheService: CacheService,
private readonly reflector: Reflector,
) {}
async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
const cacheKey = this.reflector.get<string>(CACHE_KEY, context.getHandler());
if (!cacheKey) {
return next.handle();
}
const cacheTtl = this.reflector.get<number>(CACHE_TTL, context.getHandler()) || 300;
const request = context.switchToHttp().getRequest();
const fullKey = `${cacheKey}:${JSON.stringify(request.query)}`;
const cached = await this.cacheService.get(fullKey);
if (cached) {
return of(cached);
}
return next.handle().pipe(
tap(async (data) => {
await this.cacheService.set(fullKey, data, cacheTtl);
}),
);
}
}
```
### 3.3 Uso de Cache en Service
```typescript
// src/modules/product/services/product.service.ts
@Injectable()
export class ProductService {
constructor(
private readonly repository: Repository<ProductEntity>,
private readonly cacheService: CacheService,
) {}
async findAll(query: ProductQueryDto): Promise<Product[]> {
const cacheKey = `products:list:${JSON.stringify(query)}`;
// Intentar obtener de cache
const cached = await this.cacheService.get<Product[]>(cacheKey);
if (cached) {
return cached;
}
// Query a BD
const products = await this.repository.find({
where: this.buildWhereClause(query),
take: query.limit,
});
// Guardar en cache (5 minutos)
await this.cacheService.set(cacheKey, products, 300);
return products;
}
async update(id: string, dto: UpdateProductDto): Promise<Product> {
const product = await this.repository.save({ id, ...dto });
// Invalidar cache relacionado
await this.cacheService.delByPattern('products:*');
return product;
}
}
```
### 3.4 Compresion de Responses
```typescript
// src/main.ts
import * as compression from 'compression';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Comprimir responses > 1kb
app.use(compression({
threshold: 1024,
level: 6, // Balance entre compresion y CPU
}));
await app.listen(3000);
}
```
### 3.5 Lazy Loading de Modulos
```typescript
// Cargar modulo pesado solo cuando se necesita
@Module({
imports: [
// Modulo de reportes cargado lazy
RouterModule.register([
{
path: 'reports',
module: ReportsModule,
},
]),
],
})
export class AppModule {}
```
---
## 4. FRONTEND PERFORMANCE
### 4.1 Code Splitting
```typescript
// ❌ INCORRECTO: Importar todo
import { HeavyComponent } from './HeavyComponent';
// ✅ CORRECTO: Lazy loading
const HeavyComponent = lazy(() => import('./HeavyComponent'));
// Uso con Suspense
<Suspense fallback={<LoadingSpinner />}>
<HeavyComponent />
</Suspense>
```
### 4.2 Memoizacion
```typescript
// React.memo para componentes puros
const UserCard = memo(({ user }: { user: User }) => {
return (
<div>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
});
// useMemo para calculos costosos
const ExpensiveList = ({ items, filter }: Props) => {
const filteredItems = useMemo(
() => items.filter(item => complexFilter(item, filter)),
[items, filter], // Solo recalcular si cambian
);
return <ul>{filteredItems.map(/* ... */)}</ul>;
};
// useCallback para funciones estables
const ParentComponent = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('clicked');
}, []); // Funcion estable
return <ChildComponent onClick={handleClick} />;
};
```
### 4.3 Virtualizacion de Listas
```typescript
// Para listas largas, usar virtualizacion
import { useVirtualizer } from '@tanstack/react-virtual';
const VirtualList = ({ items }: { items: Item[] }) => {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50, // Altura estimada de cada item
});
return (
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize() }}>
{virtualizer.getVirtualItems().map(virtualItem => (
<div
key={virtualItem.key}
style={{
position: 'absolute',
top: virtualItem.start,
height: virtualItem.size,
}}
>
<ItemComponent item={items[virtualItem.index]} />
</div>
))}
</div>
</div>
);
};
```
### 4.4 Optimizacion de Imagenes
```typescript
// Componente de imagen optimizada
const OptimizedImage = ({
src,
alt,
width,
height,
}: ImageProps) => {
return (
<img
src={src}
alt={alt}
width={width}
height={height}
loading="lazy" // Lazy loading nativo
decoding="async" // Decodificacion asincrona
style={{ aspectRatio: `${width}/${height}` }} // Prevenir layout shift
/>
);
};
// Con srcset para responsive
const ResponsiveImage = ({ src, alt }: Props) => {
return (
<img
src={src}
alt={alt}
srcSet={`
${src}?w=400 400w,
${src}?w=800 800w,
${src}?w=1200 1200w
`}
sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
loading="lazy"
/>
);
};
```
### 4.5 Debounce y Throttle
```typescript
// src/shared/hooks/useDebounce.ts
import { useState, useEffect } from 'react';
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Uso en busqueda
const SearchInput = () => {
const [search, setSearch] = useState('');
const debouncedSearch = useDebounce(search, 300);
// Query solo se ejecuta cuando debouncedSearch cambia
const { data } = useQuery({
queryKey: ['search', debouncedSearch],
queryFn: () => api.search(debouncedSearch),
enabled: debouncedSearch.length > 2,
});
return <input value={search} onChange={e => setSearch(e.target.value)} />;
};
```
### 4.6 React Query - Cache y Stale Time
```typescript
// src/shared/hooks/useProducts.ts
export const useProducts = (filters: ProductFilters) => {
return useQuery({
queryKey: ['products', filters],
queryFn: () => productService.getAll(filters),
staleTime: 5 * 60 * 1000, // 5 minutos antes de refetch
gcTime: 30 * 60 * 1000, // 30 minutos en cache
placeholderData: keepPreviousData, // Mostrar datos anteriores mientras carga
});
};
// Prefetch para navegacion anticipada
const ProductList = () => {
const queryClient = useQueryClient();
const handleMouseEnter = (productId: string) => {
// Prefetch detalle del producto
queryClient.prefetchQuery({
queryKey: ['product', productId],
queryFn: () => productService.getById(productId),
});
};
return (/* ... */);
};
```
---
## 5. API DESIGN PARA PERFORMANCE
### 5.1 Campos Seleccionables
```typescript
// Permitir al cliente elegir campos
@Get()
async findAll(
@Query('fields') fields?: string, // ?fields=id,name,price
): Promise<Partial<Product>[]> {
const select = fields?.split(',') || undefined;
return this.productService.findAll({ select });
}
```
### 5.2 Expansion de Relaciones
```typescript
// Permitir expansion opcional de relaciones
@Get(':id')
async findOne(
@Param('id') id: string,
@Query('expand') expand?: string, // ?expand=category,reviews
): Promise<Product> {
const relations = expand?.split(',') || [];
return this.productService.findOne(id, { relations });
}
```
### 5.3 Batch Endpoints
```typescript
// Endpoint para multiples operaciones
@Post('batch')
async batchCreate(@Body() dtos: CreateProductDto[]): Promise<Product[]> {
// Una transaccion en lugar de N requests
return this.productService.createMany(dtos);
}
// Endpoint para multiples IDs
@Get('batch')
async batchGet(@Query('ids') ids: string): Promise<Product[]> {
const idArray = ids.split(',');
return this.productService.findByIds(idArray);
}
```
---
## 6. MONITORING Y PROFILING
### 6.1 Metricas de API
```typescript
// src/shared/interceptors/metrics.interceptor.ts
@Injectable()
export class MetricsInterceptor implements NestInterceptor {
constructor(private readonly metricsService: MetricsService) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const { method, url } = request;
const startTime = Date.now();
return next.handle().pipe(
tap({
next: () => {
const duration = Date.now() - startTime;
this.metricsService.recordRequest(method, url, 200, duration);
},
error: (error) => {
const duration = Date.now() - startTime;
this.metricsService.recordRequest(method, url, error.status || 500, duration);
},
}),
);
}
}
```
### 6.2 Query Logging Condicional
```typescript
// Solo loguear queries lentas en produccion
const typeOrmConfig: TypeOrmModuleOptions = {
logging: process.env.NODE_ENV === 'production' ? ['error', 'warn'] : true,
maxQueryExecutionTime: 1000, // Loguear queries > 1s
};
```
---
## 7. CHECKLIST DE PERFORMANCE
```
Database:
[ ] Indices en columnas de WHERE frecuentes
[ ] Indices compuestos para queries comunes
[ ] No N+1 queries (usar JOIN/relations)
[ ] Paginacion cursor-based para datasets grandes
[ ] SELECT solo campos necesarios
[ ] EXPLAIN ANALYZE en queries criticas
Backend:
[ ] Cache implementado para datos frecuentes
[ ] Invalidacion de cache correcta
[ ] Compresion habilitada
[ ] Connection pooling configurado
[ ] Timeouts apropiados
Frontend:
[ ] Code splitting / lazy loading
[ ] Memoizacion donde corresponde
[ ] Virtualizacion para listas largas
[ ] Imagenes optimizadas y lazy loaded
[ ] Debounce en inputs de busqueda
[ ] React Query con staleTime apropiado
API:
[ ] Paginacion en endpoints de listas
[ ] Campos seleccionables (opcional)
[ ] Batch endpoints para operaciones multiples
[ ] Rate limiting para proteger recursos
```
---
**Version:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Patron de Performance

View File

@ -0,0 +1,778 @@
# PATRON DE SEGURIDAD
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** OBLIGATORIA - Seguir en todo el codigo
**Sistema:** SIMCO + CAPVED
---
## PROPOSITO
Definir patrones de seguridad obligatorios para prevenir vulnerabilidades comunes (OWASP Top 10) y proteger datos sensibles.
---
## 1. OWASP TOP 10 - RESUMEN
```
╔══════════════════════════════════════════════════════════════════════╗
║ OWASP TOP 10 - 2021 ║
╠══════════════════════════════════════════════════════════════════════╣
║ ║
║ A01 - Broken Access Control ║
║ A02 - Cryptographic Failures ║
║ A03 - Injection ║
║ A04 - Insecure Design ║
║ A05 - Security Misconfiguration ║
║ A06 - Vulnerable Components ║
║ A07 - Authentication Failures ║
║ A08 - Software Integrity Failures ║
║ A09 - Logging & Monitoring Failures ║
║ A10 - Server-Side Request Forgery (SSRF) ║
║ ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## 2. SANITIZACION DE INPUT
### Backend - Validacion con class-validator
```typescript
// src/modules/user/dto/create-user.dto.ts
import {
IsEmail,
IsString,
MinLength,
MaxLength,
Matches,
IsNotEmpty,
} from 'class-validator';
import { Transform } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';
export class CreateUserDto {
@ApiProperty({ example: 'user@example.com' })
@IsEmail({}, { message: 'Email invalido' })
@MaxLength(255)
@Transform(({ value }) => value?.toLowerCase().trim()) // Sanitizar
email: string;
@ApiProperty({ example: 'John' })
@IsString()
@IsNotEmpty()
@MinLength(2)
@MaxLength(100)
@Matches(/^[a-zA-ZÀ-ÿ\s'-]+$/, {
message: 'Nombre solo puede contener letras',
})
@Transform(({ value }) => value?.trim()) // Sanitizar espacios
firstName: string;
@ApiProperty()
@IsString()
@MinLength(8)
@MaxLength(128)
@Matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/,
{ message: 'Password debe tener mayuscula, minuscula, numero y simbolo' },
)
password: string;
}
```
### Sanitizacion de HTML (Prevenir XSS)
```typescript
// src/shared/utils/sanitizer.ts
import DOMPurify from 'isomorphic-dompurify';
export function sanitizeHtml(dirty: string): string {
return DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br'],
ALLOWED_ATTR: [],
});
}
export function stripHtml(dirty: string): string {
return DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: [],
ALLOWED_ATTR: [],
});
}
// Uso en DTO
@Transform(({ value }) => stripHtml(value))
@IsString()
comment: string;
```
### Prevenir SQL Injection
```typescript
// ❌ INCORRECTO: SQL Injection vulnerable
async findByName(name: string) {
return this.repository.query(
`SELECT * FROM users WHERE name = '${name}'` // VULNERABLE
);
}
// ✅ CORRECTO: Usar parametros
async findByName(name: string) {
return this.repository.query(
'SELECT * FROM users WHERE name = $1',
[name], // Parametrizado
);
}
// ✅ MEJOR: Usar QueryBuilder de TypeORM
async findByName(name: string) {
return this.repository
.createQueryBuilder('user')
.where('user.name = :name', { name }) // Automaticamente seguro
.getMany();
}
```
---
## 3. AUTENTICACION
### Password Hashing
```typescript
// src/shared/utils/password.util.ts
import * as bcrypt from 'bcrypt';
const SALT_ROUNDS = 12; // Minimo 10 para produccion
export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, SALT_ROUNDS);
}
export async function verifyPassword(
password: string,
hash: string,
): Promise<boolean> {
return bcrypt.compare(password, hash);
}
```
### JWT con Refresh Tokens
```typescript
// src/modules/auth/services/auth.service.ts
@Injectable()
export class AuthService {
constructor(
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
private readonly userService: UserService,
private readonly tokenService: RefreshTokenService,
) {}
async login(dto: LoginDto): Promise<AuthTokens> {
const user = await this.validateUser(dto.email, dto.password);
if (!user) {
// Mensaje generico para no revelar si email existe
throw new UnauthorizedException('Credenciales invalidas');
}
const tokens = await this.generateTokens(user);
// Guardar refresh token hasheado en BD
await this.tokenService.saveRefreshToken(
user.id,
await hashPassword(tokens.refreshToken),
);
return tokens;
}
private async generateTokens(user: UserEntity): Promise<AuthTokens> {
const payload: JwtPayload = {
sub: user.id,
email: user.email,
roles: user.roles.map(r => r.name),
};
const [accessToken, refreshToken] = await Promise.all([
this.jwtService.signAsync(payload, {
secret: this.configService.get('JWT_SECRET'),
expiresIn: '15m', // Corta duracion
}),
this.jwtService.signAsync(
{ sub: user.id, type: 'refresh' },
{
secret: this.configService.get('JWT_REFRESH_SECRET'),
expiresIn: '7d',
},
),
]);
return { accessToken, refreshToken };
}
async refresh(refreshToken: string): Promise<AuthTokens> {
try {
const payload = await this.jwtService.verifyAsync(refreshToken, {
secret: this.configService.get('JWT_REFRESH_SECRET'),
});
// Verificar que token existe en BD y no fue revocado
const storedToken = await this.tokenService.findByUserId(payload.sub);
if (!storedToken || !await verifyPassword(refreshToken, storedToken.hash)) {
throw new UnauthorizedException('Token invalido');
}
const user = await this.userService.findById(payload.sub);
return this.generateTokens(user);
} catch {
throw new UnauthorizedException('Token invalido o expirado');
}
}
async logout(userId: string): Promise<void> {
// Revocar todos los refresh tokens del usuario
await this.tokenService.revokeAllUserTokens(userId);
}
}
```
### Guard de Autenticacion
```typescript
// src/shared/guards/jwt-auth.guard.ts
import { Injectable, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
// Verificar si es ruta publica
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
handleRequest(err: any, user: any, info: any) {
if (err || !user) {
throw err || new UnauthorizedException('No autorizado');
}
return user;
}
}
```
---
## 4. AUTORIZACION (RBAC)
### Roles y Permisos
```typescript
// src/shared/enums/roles.enum.ts
export enum Role {
SUPER_ADMIN = 'super_admin',
ADMIN = 'admin',
MANAGER = 'manager',
USER = 'user',
GUEST = 'guest',
}
export enum Permission {
// Users
USER_CREATE = 'user:create',
USER_READ = 'user:read',
USER_UPDATE = 'user:update',
USER_DELETE = 'user:delete',
// Products
PRODUCT_CREATE = 'product:create',
PRODUCT_READ = 'product:read',
PRODUCT_UPDATE = 'product:update',
PRODUCT_DELETE = 'product:delete',
}
// Mapeo de roles a permisos
export const ROLE_PERMISSIONS: Record<Role, Permission[]> = {
[Role.SUPER_ADMIN]: Object.values(Permission),
[Role.ADMIN]: [
Permission.USER_CREATE,
Permission.USER_READ,
Permission.USER_UPDATE,
Permission.PRODUCT_CREATE,
Permission.PRODUCT_READ,
Permission.PRODUCT_UPDATE,
Permission.PRODUCT_DELETE,
],
[Role.MANAGER]: [
Permission.USER_READ,
Permission.PRODUCT_CREATE,
Permission.PRODUCT_READ,
Permission.PRODUCT_UPDATE,
],
[Role.USER]: [
Permission.PRODUCT_READ,
],
[Role.GUEST]: [],
};
```
### Guard de Roles
```typescript
// src/shared/guards/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Role, Permission, ROLE_PERMISSIONS } from '../enums/roles.enum';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<Role[]>('roles', [
context.getHandler(),
context.getClass(),
]);
const requiredPermissions = this.reflector.getAllAndOverride<Permission[]>(
'permissions',
[context.getHandler(), context.getClass()],
);
if (!requiredRoles && !requiredPermissions) {
return true; // Sin restricciones
}
const { user } = context.switchToHttp().getRequest();
if (!user) {
throw new ForbiddenException('Usuario no autenticado');
}
// Verificar roles
if (requiredRoles?.length > 0) {
const hasRole = requiredRoles.some(role => user.roles?.includes(role));
if (!hasRole) {
throw new ForbiddenException('Rol insuficiente');
}
}
// Verificar permisos
if (requiredPermissions?.length > 0) {
const userPermissions = this.getUserPermissions(user.roles);
const hasPermission = requiredPermissions.every(
permission => userPermissions.includes(permission),
);
if (!hasPermission) {
throw new ForbiddenException('Permiso insuficiente');
}
}
return true;
}
private getUserPermissions(roles: Role[]): Permission[] {
const permissions = new Set<Permission>();
for (const role of roles) {
for (const permission of ROLE_PERMISSIONS[role] || []) {
permissions.add(permission);
}
}
return Array.from(permissions);
}
}
```
### Decoradores
```typescript
// src/shared/decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
import { Role, Permission } from '../enums/roles.enum';
export const Roles = (...roles: Role[]) => SetMetadata('roles', roles);
export const Permissions = (...permissions: Permission[]) =>
SetMetadata('permissions', permissions);
```
### Uso en Controller
```typescript
// src/modules/user/controllers/user.controller.ts
@Controller('users')
@UseGuards(JwtAuthGuard, RolesGuard)
export class UserController {
@Get()
@Roles(Role.ADMIN, Role.MANAGER)
findAll() {
return this.userService.findAll();
}
@Post()
@Permissions(Permission.USER_CREATE)
create(@Body() dto: CreateUserDto) {
return this.userService.create(dto);
}
@Delete(':id')
@Roles(Role.SUPER_ADMIN) // Solo super admin puede eliminar
remove(@Param('id') id: string) {
return this.userService.remove(id);
}
}
```
---
## 5. PROTECCION DE DATOS
### Encriptacion de Datos Sensibles
```typescript
// src/shared/utils/encryption.util.ts
import * as crypto from 'crypto';
const ALGORITHM = 'aes-256-gcm';
const IV_LENGTH = 16;
const AUTH_TAG_LENGTH = 16;
export function encrypt(text: string, key: string): string {
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(
ALGORITHM,
Buffer.from(key, 'hex'),
iv,
);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
// IV + AuthTag + Encrypted
return iv.toString('hex') + authTag.toString('hex') + encrypted;
}
export function decrypt(encryptedText: string, key: string): string {
const iv = Buffer.from(encryptedText.slice(0, IV_LENGTH * 2), 'hex');
const authTag = Buffer.from(
encryptedText.slice(IV_LENGTH * 2, (IV_LENGTH + AUTH_TAG_LENGTH) * 2),
'hex',
);
const encrypted = encryptedText.slice((IV_LENGTH + AUTH_TAG_LENGTH) * 2);
const decipher = crypto.createDecipheriv(
ALGORITHM,
Buffer.from(key, 'hex'),
iv,
);
decipher.setAuthTag(authTag);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
```
### Columnas Encriptadas en Entity
```typescript
// src/shared/transformers/encrypted.transformer.ts
import { ValueTransformer } from 'typeorm';
import { encrypt, decrypt } from '../utils/encryption.util';
export class EncryptedTransformer implements ValueTransformer {
constructor(private readonly key: string) {}
to(value: string | null): string | null {
if (!value) return null;
return encrypt(value, this.key);
}
from(value: string | null): string | null {
if (!value) return null;
return decrypt(value, this.key);
}
}
// Uso en Entity
@Column({
type: 'text',
transformer: new EncryptedTransformer(process.env.ENCRYPTION_KEY),
})
ssn: string; // Se guarda encriptado en BD
```
### Nunca Exponer Datos Sensibles
```typescript
// ❌ INCORRECTO: Exponer password en response
@Get(':id')
async findOne(@Param('id') id: string) {
return this.userRepository.findOne({ where: { id } });
// Retorna { id, email, password, ... } - PASSWORD EXPUESTO!
}
// ✅ CORRECTO: Usar ResponseDto que excluye campos sensibles
@Get(':id')
async findOne(@Param('id') id: string): Promise<UserResponseDto> {
const user = await this.userService.findOne(id);
return plainToClass(UserResponseDto, user, {
excludeExtraneousValues: true,
});
}
// ResponseDto solo expone campos seguros
export class UserResponseDto {
@Expose() id: string;
@Expose() email: string;
@Expose() firstName: string;
// password NO esta expuesto
}
```
---
## 6. RATE LIMITING
### Implementacion con Throttler
```typescript
// src/app.module.ts
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
@Module({
imports: [
ThrottlerModule.forRoot([
{
name: 'short',
ttl: 1000, // 1 segundo
limit: 3, // 3 requests por segundo
},
{
name: 'medium',
ttl: 10000, // 10 segundos
limit: 20, // 20 requests por 10 segundos
},
{
name: 'long',
ttl: 60000, // 1 minuto
limit: 100, // 100 requests por minuto
},
]),
],
providers: [
{
provide: APP_GUARD,
useClass: ThrottlerGuard,
},
],
})
export class AppModule {}
```
### Rate Limiting por Endpoint
```typescript
// Rate limit especifico para login (prevenir brute force)
@Post('login')
@Throttle({ default: { limit: 5, ttl: 60000 } }) // 5 intentos por minuto
async login(@Body() dto: LoginDto) {
return this.authService.login(dto);
}
// Endpoint sin rate limit
@Get('health')
@SkipThrottle()
healthCheck() {
return { status: 'ok' };
}
```
---
## 7. HEADERS DE SEGURIDAD
### Helmet Middleware
```typescript
// src/main.ts
import helmet from 'helmet';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Headers de seguridad
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", 'data:', 'https:'],
},
},
hsts: {
maxAge: 31536000, // 1 año
includeSubDomains: true,
},
}));
// CORS configurado
app.enableCors({
origin: process.env.CORS_ORIGINS?.split(',') || false,
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
credentials: true,
});
await app.listen(3000);
}
```
---
## 8. FRONTEND - SEGURIDAD
### Almacenamiento de Tokens
```typescript
// ❌ INCORRECTO: Token en localStorage (vulnerable a XSS)
localStorage.setItem('token', accessToken);
// ✅ MEJOR: HttpOnly cookies (configurado desde backend)
// El token se maneja automaticamente por el navegador
// ✅ ALTERNATIVA: Si debe estar en JS, usar memoria
class TokenStore {
private accessToken: string | null = null;
setToken(token: string) {
this.accessToken = token;
}
getToken(): string | null {
return this.accessToken;
}
clearToken() {
this.accessToken = null;
}
}
export const tokenStore = new TokenStore();
```
### Prevenir XSS en React
```typescript
// ❌ INCORRECTO: dangerouslySetInnerHTML sin sanitizar
<div dangerouslySetInnerHTML={{ __html: userInput }} />
// ✅ CORRECTO: Sanitizar primero
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(userInput)
}} />
// ✅ MEJOR: Evitar dangerouslySetInnerHTML cuando sea posible
<div>{userInput}</div> // React escapa automaticamente
```
### Validacion en Frontend (Defense in Depth)
```typescript
// src/shared/schemas/user.schema.ts
import { z } from 'zod';
export const createUserSchema = z.object({
email: z.string()
.email('Email invalido')
.max(255)
.transform(v => v.toLowerCase().trim()),
firstName: z.string()
.min(2, 'Minimo 2 caracteres')
.max(100)
.regex(/^[a-zA-ZÀ-ÿ\s'-]+$/, 'Solo letras permitidas')
.transform(v => v.trim()),
password: z.string()
.min(8, 'Minimo 8 caracteres')
.max(128)
.regex(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/,
'Debe incluir mayuscula, minuscula, numero y simbolo',
),
});
```
---
## 9. CHECKLIST DE SEGURIDAD
```
Input/Output:
[ ] Todos los inputs validados con class-validator
[ ] HTML sanitizado antes de renderizar
[ ] SQL usa queries parametrizadas
[ ] Datos sensibles nunca en logs
[ ] ResponseDto excluye campos sensibles
Autenticacion:
[ ] Passwords hasheados con bcrypt (rounds >= 10)
[ ] JWT con expiracion corta (< 15min)
[ ] Refresh tokens almacenados hasheados
[ ] Logout revoca tokens
[ ] Mensajes de error genericos (no revelar info)
Autorizacion:
[ ] Guards en todos los endpoints protegidos
[ ] Verificacion de ownership en recursos
[ ] Roles y permisos implementados
[ ] Principio de minimo privilegio
Infraestructura:
[ ] HTTPS obligatorio
[ ] Headers de seguridad (Helmet)
[ ] CORS configurado correctamente
[ ] Rate limiting implementado
[ ] Secrets en variables de entorno
Frontend:
[ ] No localStorage para tokens sensibles
[ ] CSP configurado
[ ] Validacion client-side (defense in depth)
[ ] No exponer errores detallados a usuarios
```
---
## 10. RECURSOS ADICIONALES
- OWASP Cheat Sheets: https://cheatsheetseries.owasp.org/
- NestJS Security: https://docs.nestjs.com/security/helmet
- React Security: https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
---
**Version:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Patron de Seguridad

View File

@ -0,0 +1,727 @@
# PATRÓN: TESTING
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Aplica a:** Backend (NestJS), Frontend (React)
**Prioridad:** RECOMENDADA
---
## PROPÓSITO
Definir patrones estándar de testing para garantizar código de calidad.
---
## 1. TIPOS DE TESTS
| Tipo | Qué Testea | Herramienta | Cobertura Objetivo |
|------|------------|-------------|-------------------|
| **Unit** | Funciones/Clases aisladas | Jest | 70%+ |
| **Integration** | Módulos integrados | Jest + Supertest | 50%+ |
| **E2E** | Flujos completos | Jest + Supertest | Críticos |
| **Component** | Componentes React | React Testing Library | 60%+ |
---
## 2. BACKEND: TEST DE SERVICE
### Template
```typescript
// user.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserService } from './user.service';
import { UserEntity } from '../entities/user.entity';
import { CreateUserDto } from '../dto/create-user.dto';
import { NotFoundException, ConflictException } from '@nestjs/common';
describe('UserService', () => {
let service: UserService;
let repository: jest.Mocked<Repository<UserEntity>>;
// Mock del repositorio
const mockRepository = {
find: jest.fn(),
findOne: jest.fn(),
create: jest.fn(),
save: jest.fn(),
remove: jest.fn(),
count: jest.fn(),
};
// Fixtures
const mockUser: UserEntity = {
id: '550e8400-e29b-41d4-a716-446655440000',
email: 'test@example.com',
name: 'Test User',
status: 'active',
createdAt: new Date(),
updatedAt: new Date(),
};
const createUserDto: CreateUserDto = {
email: 'new@example.com',
name: 'New User',
password: 'SecurePass123!',
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(UserEntity),
useValue: mockRepository,
},
],
}).compile();
service = module.get<UserService>(UserService);
repository = module.get(getRepositoryToken(UserEntity));
// Reset mocks
jest.clearAllMocks();
});
describe('findOne', () => {
it('should return user when found', async () => {
// Arrange
mockRepository.findOne.mockResolvedValue(mockUser);
// Act
const result = await service.findOne(mockUser.id);
// Assert
expect(result).toEqual(mockUser);
expect(mockRepository.findOne).toHaveBeenCalledWith({
where: { id: mockUser.id },
});
});
it('should throw NotFoundException when user not found', async () => {
// Arrange
mockRepository.findOne.mockResolvedValue(null);
// Act & Assert
await expect(service.findOne('non-existent-id'))
.rejects
.toThrow(NotFoundException);
});
});
describe('create', () => {
it('should create user successfully', async () => {
// Arrange
mockRepository.findOne.mockResolvedValue(null); // No existe
mockRepository.create.mockReturnValue(mockUser);
mockRepository.save.mockResolvedValue(mockUser);
// Act
const result = await service.create(createUserDto);
// Assert
expect(result).toEqual(mockUser);
expect(mockRepository.findOne).toHaveBeenCalled();
expect(mockRepository.create).toHaveBeenCalledWith(
expect.objectContaining({
email: createUserDto.email,
name: createUserDto.name,
})
);
expect(mockRepository.save).toHaveBeenCalled();
});
it('should throw ConflictException when email exists', async () => {
// Arrange
mockRepository.findOne.mockResolvedValue(mockUser); // Ya existe
// Act & Assert
await expect(service.create(createUserDto))
.rejects
.toThrow(ConflictException);
expect(mockRepository.save).not.toHaveBeenCalled();
});
});
describe('findAll', () => {
it('should return array of users', async () => {
// Arrange
const users = [mockUser, { ...mockUser, id: '2' }];
mockRepository.find.mockResolvedValue(users);
// Act
const result = await service.findAll();
// Assert
expect(result).toHaveLength(2);
expect(mockRepository.find).toHaveBeenCalled();
});
it('should return empty array when no users', async () => {
// Arrange
mockRepository.find.mockResolvedValue([]);
// Act
const result = await service.findAll();
// Assert
expect(result).toHaveLength(0);
});
});
describe('update', () => {
it('should update user successfully', async () => {
// Arrange
const updateDto = { name: 'Updated Name' };
const updatedUser = { ...mockUser, ...updateDto };
mockRepository.findOne.mockResolvedValue(mockUser);
mockRepository.save.mockResolvedValue(updatedUser);
// Act
const result = await service.update(mockUser.id, updateDto);
// Assert
expect(result.name).toBe('Updated Name');
expect(mockRepository.save).toHaveBeenCalled();
});
it('should throw NotFoundException when user not found', async () => {
// Arrange
mockRepository.findOne.mockResolvedValue(null);
// Act & Assert
await expect(service.update('non-existent', { name: 'Test' }))
.rejects
.toThrow(NotFoundException);
});
});
describe('remove', () => {
it('should remove user successfully', async () => {
// Arrange
mockRepository.findOne.mockResolvedValue(mockUser);
mockRepository.remove.mockResolvedValue(mockUser);
// Act
await service.remove(mockUser.id);
// Assert
expect(mockRepository.remove).toHaveBeenCalledWith(mockUser);
});
});
});
```
---
## 3. BACKEND: TEST DE CONTROLLER
### Template
```typescript
// user.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { UserController } from './user.controller';
import { UserService } from '../services/user.service';
import { CreateUserDto } from '../dto/create-user.dto';
import { UserEntity } from '../entities/user.entity';
import { NotFoundException } from '@nestjs/common';
describe('UserController', () => {
let controller: UserController;
let service: jest.Mocked<UserService>;
const mockService = {
findAll: jest.fn(),
findOne: jest.fn(),
create: jest.fn(),
update: jest.fn(),
remove: jest.fn(),
};
const mockUser: UserEntity = {
id: '550e8400-e29b-41d4-a716-446655440000',
email: 'test@example.com',
name: 'Test User',
status: 'active',
createdAt: new Date(),
updatedAt: new Date(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UserController],
providers: [
{
provide: UserService,
useValue: mockService,
},
],
}).compile();
controller = module.get<UserController>(UserController);
service = module.get(UserService);
jest.clearAllMocks();
});
describe('findAll', () => {
it('should return array of users', async () => {
// Arrange
mockService.findAll.mockResolvedValue([mockUser]);
// Act
const result = await controller.findAll();
// Assert
expect(result).toHaveLength(1);
expect(service.findAll).toHaveBeenCalled();
});
});
describe('findOne', () => {
it('should return user by id', async () => {
// Arrange
mockService.findOne.mockResolvedValue(mockUser);
// Act
const result = await controller.findOne(mockUser.id);
// Assert
expect(result).toEqual(mockUser);
expect(service.findOne).toHaveBeenCalledWith(mockUser.id);
});
it('should propagate NotFoundException', async () => {
// Arrange
mockService.findOne.mockRejectedValue(new NotFoundException());
// Act & Assert
await expect(controller.findOne('non-existent'))
.rejects
.toThrow(NotFoundException);
});
});
describe('create', () => {
it('should create and return user', async () => {
// Arrange
const createDto: CreateUserDto = {
email: 'new@example.com',
name: 'New User',
password: 'Pass123!',
};
mockService.create.mockResolvedValue(mockUser);
// Act
const result = await controller.create(createDto);
// Assert
expect(result).toEqual(mockUser);
expect(service.create).toHaveBeenCalledWith(createDto);
});
});
describe('update', () => {
it('should update and return user', async () => {
// Arrange
const updateDto = { name: 'Updated' };
const updated = { ...mockUser, ...updateDto };
mockService.update.mockResolvedValue(updated);
// Act
const result = await controller.update(mockUser.id, updateDto);
// Assert
expect(result.name).toBe('Updated');
expect(service.update).toHaveBeenCalledWith(mockUser.id, updateDto);
});
});
describe('remove', () => {
it('should remove user', async () => {
// Arrange
mockService.remove.mockResolvedValue(undefined);
// Act
await controller.remove(mockUser.id);
// Assert
expect(service.remove).toHaveBeenCalledWith(mockUser.id);
});
});
});
```
---
## 4. BACKEND: TEST E2E
### Template
```typescript
// user.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
describe('UserController (e2e)', () => {
let app: INestApplication;
let createdUserId: string;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
app.useGlobalPipes(new ValidationPipe());
await app.init();
});
afterAll(async () => {
await app.close();
});
describe('/users (POST)', () => {
it('should create user', () => {
return request(app.getHttpServer())
.post('/users')
.send({
email: 'e2e@test.com',
name: 'E2E User',
password: 'SecurePass123!',
})
.expect(201)
.expect((res) => {
expect(res.body.id).toBeDefined();
expect(res.body.email).toBe('e2e@test.com');
createdUserId = res.body.id;
});
});
it('should reject invalid email', () => {
return request(app.getHttpServer())
.post('/users')
.send({
email: 'invalid-email',
name: 'Test',
password: 'Pass123!',
})
.expect(400);
});
it('should reject duplicate email', () => {
return request(app.getHttpServer())
.post('/users')
.send({
email: 'e2e@test.com', // Ya existe
name: 'Duplicate',
password: 'Pass123!',
})
.expect(409);
});
});
describe('/users (GET)', () => {
it('should return users list', () => {
return request(app.getHttpServer())
.get('/users')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
});
});
});
describe('/users/:id (GET)', () => {
it('should return user by id', () => {
return request(app.getHttpServer())
.get(`/users/${createdUserId}`)
.expect(200)
.expect((res) => {
expect(res.body.id).toBe(createdUserId);
});
});
it('should return 404 for non-existent user', () => {
return request(app.getHttpServer())
.get('/users/550e8400-e29b-41d4-a716-446655440000')
.expect(404);
});
});
describe('/users/:id (PUT)', () => {
it('should update user', () => {
return request(app.getHttpServer())
.put(`/users/${createdUserId}`)
.send({ name: 'Updated Name' })
.expect(200)
.expect((res) => {
expect(res.body.name).toBe('Updated Name');
});
});
});
describe('/users/:id (DELETE)', () => {
it('should delete user', () => {
return request(app.getHttpServer())
.delete(`/users/${createdUserId}`)
.expect(204);
});
});
});
```
---
## 5. FRONTEND: TEST DE COMPONENTE
### Template con React Testing Library
```typescript
// UserCard.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { UserCard } from './UserCard';
import { User } from '@/types/user.types';
describe('UserCard', () => {
const mockUser: User = {
id: '1',
email: 'test@example.com',
name: 'Test User',
status: 'active',
};
const mockOnEdit = jest.fn();
const mockOnDelete = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});
it('should render user information', () => {
render(<UserCard user={mockUser} />);
expect(screen.getByText('Test User')).toBeInTheDocument();
expect(screen.getByText('test@example.com')).toBeInTheDocument();
});
it('should call onEdit when edit button clicked', () => {
render(
<UserCard
user={mockUser}
onEdit={mockOnEdit}
/>
);
fireEvent.click(screen.getByRole('button', { name: /edit/i }));
expect(mockOnEdit).toHaveBeenCalledWith(mockUser);
});
it('should call onDelete when delete button clicked', () => {
render(
<UserCard
user={mockUser}
onDelete={mockOnDelete}
/>
);
fireEvent.click(screen.getByRole('button', { name: /delete/i }));
expect(mockOnDelete).toHaveBeenCalledWith(mockUser.id);
});
it('should show loading state', () => {
render(<UserCard user={mockUser} isLoading />);
expect(screen.getByTestId('loading-skeleton')).toBeInTheDocument();
});
it('should hide actions when not provided', () => {
render(<UserCard user={mockUser} />);
expect(screen.queryByRole('button', { name: /edit/i })).not.toBeInTheDocument();
});
});
```
---
## 6. FRONTEND: TEST DE HOOK
### Template
```typescript
// useUsers.test.tsx
import { renderHook, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useUsers, useCreateUser } from './useUsers';
import { userService } from '@/services/user.service';
// Mock del servicio
jest.mock('@/services/user.service');
const mockUserService = userService as jest.Mocked<typeof userService>;
describe('useUsers', () => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
const wrapper = ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
beforeEach(() => {
queryClient.clear();
jest.clearAllMocks();
});
describe('useUsers', () => {
it('should fetch users', async () => {
// Arrange
const mockUsers = [
{ id: '1', name: 'User 1', email: 'u1@test.com' },
{ id: '2', name: 'User 2', email: 'u2@test.com' },
];
mockUserService.getAll.mockResolvedValue(mockUsers);
// Act
const { result } = renderHook(() => useUsers(), { wrapper });
// Assert - Initially loading
expect(result.current.isLoading).toBe(true);
// Wait for data
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});
expect(result.current.data).toEqual(mockUsers);
});
it('should handle error', async () => {
// Arrange
mockUserService.getAll.mockRejectedValue(new Error('Network error'));
// Act
const { result } = renderHook(() => useUsers(), { wrapper });
await waitFor(() => {
expect(result.current.isError).toBe(true);
});
expect(result.current.error).toBeDefined();
});
});
describe('useCreateUser', () => {
it('should create user and invalidate cache', async () => {
// Arrange
const newUser = { id: '3', name: 'New', email: 'new@test.com' };
mockUserService.create.mockResolvedValue(newUser);
// Act
const { result } = renderHook(() => useCreateUser(), { wrapper });
await result.current.mutateAsync({
name: 'New',
email: 'new@test.com',
password: 'Pass123!',
});
// Assert
expect(mockUserService.create).toHaveBeenCalled();
});
});
});
```
---
## 7. CONVENCIONES DE NOMBRES
```typescript
// Archivos de test
user.service.spec.ts // Unit test backend
user.controller.spec.ts // Unit test controller
user.e2e-spec.ts // E2E test
UserCard.test.tsx // Component test
useUsers.test.tsx // Hook test
// Describe blocks
describe('UserService', () => { ... });
describe('findOne', () => { ... });
// Test cases
it('should return user when found', () => { ... });
it('should throw NotFoundException when user not found', () => { ... });
```
---
## 8. CHECKLIST DE TESTING
```
Por Service:
[ ] Test de cada método público
[ ] Test de casos de éxito
[ ] Test de casos de error (NotFoundException, etc.)
[ ] Test de validaciones de negocio
Por Controller:
[ ] Test de cada endpoint
[ ] Test de status codes correctos
[ ] Test de propagación de errores
Por Componente:
[ ] Test de render correcto
[ ] Test de eventos (click, change)
[ ] Test de estados (loading, error)
[ ] Test de props opcionales
Por Hook:
[ ] Test de fetch exitoso
[ ] Test de manejo de error
[ ] Test de mutaciones
Cobertura:
[ ] npm run test:cov muestra 70%+ en services
[ ] npm run test:cov muestra 60%+ en components
[ ] Tests críticos e2e pasan
```
---
## 9. COMANDOS
```bash
# Backend
npm run test # Unit tests
npm run test:watch # Watch mode
npm run test:cov # Con cobertura
npm run test:e2e # E2E tests
# Frontend
npm run test # All tests
npm run test -- --watch # Watch mode
npm run test -- --coverage # Con cobertura
npm run test -- UserCard # Test específico
```
---
**Versión:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Patrón de Testing

View File

@ -0,0 +1,678 @@
# PATRON DE TRANSACCIONES
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** OBLIGATORIA - Seguir para integridad de datos
**Sistema:** SIMCO + CAPVED
---
## PROPOSITO
Definir patrones de manejo de transacciones de base de datos para garantizar integridad de datos, consistencia y correcta recuperacion de errores.
---
## 1. PRINCIPIOS ACID
```
╔══════════════════════════════════════════════════════════════════════╗
║ PROPIEDADES ACID ║
╠══════════════════════════════════════════════════════════════════════╣
║ ║
║ A - Atomicity (Atomicidad) ║
║ Todo o nada. Si una parte falla, se revierte todo. ║
║ ║
║ C - Consistency (Consistencia) ║
║ La BD pasa de un estado valido a otro estado valido. ║
║ ║
║ I - Isolation (Aislamiento) ║
║ Transacciones concurrentes no interfieren entre si. ║
║ ║
║ D - Durability (Durabilidad) ║
║ Una vez confirmada, la transaccion persiste. ║
║ ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## 2. CUANDO USAR TRANSACCIONES
### SI Usar Transaccion
```
✅ Multiples operaciones que deben ser atomicas
✅ Operaciones que afectan multiples tablas relacionadas
✅ Operaciones financieras o criticas
✅ Creacion de entidades con relaciones obligatorias
✅ Actualizaciones que requieren consistencia
```
### NO Necesitas Transaccion
```
❌ Una sola query simple (SELECT, INSERT, UPDATE)
❌ Operaciones de solo lectura
❌ Operaciones independientes sin relacion
```
---
## 3. TYPEORM - METODOS DE TRANSACCION
### 3.1 QueryRunner (Recomendado para control total)
```typescript
// src/modules/order/services/order.service.ts
import { Injectable } from '@nestjs/common';
import { DataSource, QueryRunner } from 'typeorm';
@Injectable()
export class OrderService {
constructor(private readonly dataSource: DataSource) {}
async createOrder(dto: CreateOrderDto): Promise<Order> {
// Crear QueryRunner
const queryRunner = this.dataSource.createQueryRunner();
// Conectar y comenzar transaccion
await queryRunner.connect();
await queryRunner.startTransaction();
try {
// 1. Crear la orden
const order = queryRunner.manager.create(OrderEntity, {
userId: dto.userId,
status: OrderStatus.PENDING,
total: 0,
});
await queryRunner.manager.save(order);
// 2. Crear items y calcular total
let total = 0;
for (const item of dto.items) {
// Verificar stock
const product = await queryRunner.manager.findOne(ProductEntity, {
where: { id: item.productId },
lock: { mode: 'pessimistic_write' }, // Lock para evitar race condition
});
if (!product || product.stock < item.quantity) {
throw new BadRequestException(
`Stock insuficiente para producto ${item.productId}`,
);
}
// Crear item
const orderItem = queryRunner.manager.create(OrderItemEntity, {
orderId: order.id,
productId: item.productId,
quantity: item.quantity,
price: product.price,
});
await queryRunner.manager.save(orderItem);
// Actualizar stock
product.stock -= item.quantity;
await queryRunner.manager.save(product);
total += product.price * item.quantity;
}
// 3. Actualizar total de la orden
order.total = total;
await queryRunner.manager.save(order);
// 4. Confirmar transaccion
await queryRunner.commitTransaction();
return order;
} catch (error) {
// Revertir en caso de error
await queryRunner.rollbackTransaction();
throw error;
} finally {
// Liberar QueryRunner
await queryRunner.release();
}
}
}
```
### 3.2 DataSource.transaction() (Mas simple)
```typescript
// Para casos mas simples
async createUserWithProfile(dto: CreateUserWithProfileDto): Promise<User> {
return this.dataSource.transaction(async (manager) => {
// Crear usuario
const user = manager.create(UserEntity, {
email: dto.email,
password: await hashPassword(dto.password),
});
await manager.save(user);
// Crear perfil
const profile = manager.create(ProfileEntity, {
userId: user.id,
firstName: dto.firstName,
lastName: dto.lastName,
});
await manager.save(profile);
return user;
});
}
```
### 3.3 @Transaction Decorator (NestJS)
```typescript
// src/shared/decorators/transactional.decorator.ts
import { DataSource } from 'typeorm';
export function Transactional() {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args: any[]) {
const dataSource: DataSource = this.dataSource;
return dataSource.transaction(async (manager) => {
// Inyectar manager temporal
const originalManager = this.manager;
this.manager = manager;
try {
return await originalMethod.apply(this, args);
} finally {
this.manager = originalManager;
}
});
};
return descriptor;
};
}
// Uso
@Injectable()
export class OrderService {
constructor(
private readonly dataSource: DataSource,
@InjectRepository(OrderEntity)
private readonly orderRepository: Repository<OrderEntity>,
) {}
private manager: EntityManager;
@Transactional()
async createOrder(dto: CreateOrderDto): Promise<Order> {
// this.manager es el manager transaccional
const order = this.manager.create(OrderEntity, dto);
return this.manager.save(order);
}
}
```
---
## 4. NIVELES DE AISLAMIENTO
### Niveles Disponibles
| Nivel | Dirty Reads | Non-Repeatable Reads | Phantom Reads |
|-------|-------------|---------------------|---------------|
| READ UNCOMMITTED | Posible | Posible | Posible |
| READ COMMITTED | No | Posible | Posible |
| REPEATABLE READ | No | No | Posible |
| SERIALIZABLE | No | No | No |
### Configurar Nivel de Aislamiento
```typescript
// Por defecto PostgreSQL usa READ COMMITTED
// Para operaciones criticas, usar SERIALIZABLE
async processPayment(orderId: string): Promise<void> {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
// Configurar nivel de aislamiento
await queryRunner.startTransaction('SERIALIZABLE');
try {
// Operaciones criticas aqui
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
// SERIALIZABLE puede fallar por conflictos
if (error.code === '40001') { // Serialization failure
// Reintentar la transaccion
return this.processPayment(orderId);
}
throw error;
} finally {
await queryRunner.release();
}
}
```
---
## 5. LOCKS (BLOQUEOS)
### 5.1 Pessimistic Locking
```typescript
// Bloquear fila para escritura (otros esperan)
async updateStock(productId: string, quantity: number): Promise<void> {
return this.dataSource.transaction(async (manager) => {
// Obtener producto con lock exclusivo
const product = await manager.findOne(ProductEntity, {
where: { id: productId },
lock: { mode: 'pessimistic_write' },
});
if (product.stock < quantity) {
throw new BadRequestException('Stock insuficiente');
}
product.stock -= quantity;
await manager.save(product);
});
}
```
### 5.2 Optimistic Locking (Version)
```typescript
// Entity con columna de version
@Entity()
export class ProductEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
stock: number;
@VersionColumn() // Auto-incrementa en cada update
version: number;
}
// El update falla si version cambio (alguien mas modifico)
async updateStock(productId: string, quantity: number): Promise<void> {
const product = await this.productRepository.findOne({
where: { id: productId },
});
product.stock -= quantity;
try {
await this.productRepository.save(product);
} catch (error) {
if (error instanceof OptimisticLockVersionMismatchError) {
// Reintentar o notificar conflicto
throw new ConflictException('El producto fue modificado. Reintente.');
}
throw error;
}
}
```
### Cuando Usar Cada Tipo
| Scenario | Lock Recomendado |
|----------|------------------|
| Alta concurrencia, conflictos raros | Optimistic |
| Operaciones financieras criticas | Pessimistic |
| Updates frecuentes al mismo registro | Pessimistic |
| Lecturas frecuentes, updates raros | Optimistic |
---
## 6. PATRONES COMUNES
### 6.1 Unit of Work Pattern
```typescript
// src/shared/database/unit-of-work.ts
@Injectable()
export class UnitOfWork {
private queryRunner: QueryRunner;
constructor(private readonly dataSource: DataSource) {}
async begin(): Promise<void> {
this.queryRunner = this.dataSource.createQueryRunner();
await this.queryRunner.connect();
await this.queryRunner.startTransaction();
}
getRepository<T>(entity: EntityTarget<T>): Repository<T> {
return this.queryRunner.manager.getRepository(entity);
}
async commit(): Promise<void> {
await this.queryRunner.commitTransaction();
await this.queryRunner.release();
}
async rollback(): Promise<void> {
await this.queryRunner.rollbackTransaction();
await this.queryRunner.release();
}
}
// Uso
@Injectable()
export class OrderService {
constructor(private readonly uow: UnitOfWork) {}
async createOrder(dto: CreateOrderDto): Promise<Order> {
await this.uow.begin();
try {
const orderRepo = this.uow.getRepository(OrderEntity);
const productRepo = this.uow.getRepository(ProductEntity);
// Operaciones...
await this.uow.commit();
return order;
} catch (error) {
await this.uow.rollback();
throw error;
}
}
}
```
### 6.2 Saga Pattern (Para transacciones distribuidas)
```typescript
// Cuando no puedes usar transaccion de BD (microservicios)
interface SagaStep {
execute(): Promise<void>;
compensate(): Promise<void>; // Revertir si falla
}
class CreateOrderSaga {
private executedSteps: SagaStep[] = [];
async execute(steps: SagaStep[]): Promise<void> {
try {
for (const step of steps) {
await step.execute();
this.executedSteps.push(step);
}
} catch (error) {
// Compensar en orden inverso
for (const step of this.executedSteps.reverse()) {
await step.compensate();
}
throw error;
}
}
}
// Uso
const saga = new CreateOrderSaga();
await saga.execute([
{
execute: () => this.orderService.create(orderDto),
compensate: () => this.orderService.delete(orderId),
},
{
execute: () => this.inventoryService.reserve(items),
compensate: () => this.inventoryService.release(items),
},
{
execute: () => this.paymentService.charge(payment),
compensate: () => this.paymentService.refund(payment),
},
]);
```
### 6.3 Retry con Backoff
```typescript
// Para manejar deadlocks y conflictos
async withRetry<T>(
operation: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 100,
): Promise<T> {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
// Solo reintentar errores transitorios
const isRetryable =
error.code === '40001' || // Serialization failure
error.code === '40P01' || // Deadlock
error.code === '55P03'; // Lock not available
if (!isRetryable || attempt === maxRetries) {
throw error;
}
// Exponential backoff con jitter
const delay = baseDelay * Math.pow(2, attempt - 1) * (0.5 + Math.random());
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
// Uso
async createOrder(dto: CreateOrderDto): Promise<Order> {
return this.withRetry(() => this.createOrderTransaction(dto));
}
```
---
## 7. ERRORES COMUNES Y SOLUCIONES
### 7.1 Deadlock
```typescript
// ❌ PROBLEMA: Deadlock por orden inconsistente de locks
// Transaction 1: Lock A, then B
// Transaction 2: Lock B, then A
// ✅ SOLUCION: Siempre lockear en el mismo orden
async transferFunds(fromId: string, toId: string, amount: number): Promise<void> {
// Ordenar IDs para lockear siempre en el mismo orden
const [firstId, secondId] = [fromId, toId].sort();
return this.dataSource.transaction(async (manager) => {
const firstAccount = await manager.findOne(AccountEntity, {
where: { id: firstId },
lock: { mode: 'pessimistic_write' },
});
const secondAccount = await manager.findOne(AccountEntity, {
where: { id: secondId },
lock: { mode: 'pessimistic_write' },
});
// Ahora operar
const from = firstId === fromId ? firstAccount : secondAccount;
const to = firstId === toId ? firstAccount : secondAccount;
from.balance -= amount;
to.balance += amount;
await manager.save([from, to]);
});
}
```
### 7.2 Connection Leak
```typescript
// ❌ PROBLEMA: QueryRunner no liberado
async badMethod(): Promise<void> {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
const data = await queryRunner.manager.find(Entity);
// Si hay error aqui, queryRunner nunca se libera!
await queryRunner.commitTransaction();
await queryRunner.release();
}
// ✅ SOLUCION: Siempre usar try/finally
async goodMethod(): Promise<void> {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const data = await queryRunner.manager.find(Entity);
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release(); // SIEMPRE se ejecuta
}
}
```
### 7.3 Long-Running Transactions
```typescript
// ❌ PROBLEMA: Transaccion muy larga
async processAllOrders(): Promise<void> {
return this.dataSource.transaction(async (manager) => {
const orders = await manager.find(OrderEntity); // Puede ser miles
for (const order of orders) {
await this.processOrder(order); // Minutos de bloqueo
}
});
}
// ✅ SOLUCION: Procesar en batches con transacciones cortas
async processAllOrders(): Promise<void> {
const BATCH_SIZE = 100;
let processed = 0;
while (true) {
const orders = await this.orderRepository.find({
where: { status: OrderStatus.PENDING },
take: BATCH_SIZE,
});
if (orders.length === 0) break;
// Transaccion corta por batch
await this.dataSource.transaction(async (manager) => {
for (const order of orders) {
await this.processOrderInTransaction(manager, order);
}
});
processed += orders.length;
}
}
```
---
## 8. TESTING DE TRANSACCIONES
```typescript
// src/modules/order/order.service.spec.ts
describe('OrderService', () => {
let service: OrderService;
let dataSource: DataSource;
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [TypeOrmModule.forRoot(testConfig)],
providers: [OrderService],
}).compile();
service = module.get(OrderService);
dataSource = module.get(DataSource);
});
afterEach(async () => {
// Limpiar despues de cada test
await dataSource.synchronize(true);
});
describe('createOrder', () => {
it('should rollback if stock is insufficient', async () => {
// Arrange
const product = await productRepo.save({
name: 'Test',
stock: 5,
price: 100,
});
// Act & Assert
await expect(
service.createOrder({
items: [{ productId: product.id, quantity: 10 }],
}),
).rejects.toThrow('Stock insuficiente');
// Verificar que stock no cambio (rollback funciono)
const updatedProduct = await productRepo.findOne({
where: { id: product.id },
});
expect(updatedProduct.stock).toBe(5);
});
it('should commit successfully with valid data', async () => {
// ...
});
});
});
```
---
## 9. CHECKLIST DE TRANSACCIONES
```
Antes de implementar:
[ ] ¿Necesito atomicidad? (multiples operaciones)
[ ] ¿Que nivel de aislamiento necesito?
[ ] ¿Necesito locks? ¿Pesimista u optimista?
Durante implementacion:
[ ] QueryRunner siempre liberado (finally)
[ ] Rollback en catch
[ ] Manejo de errores transitorios (retry)
[ ] Transacciones lo mas cortas posible
[ ] Orden consistente de locks (evitar deadlocks)
Testing:
[ ] Test de rollback en error
[ ] Test de commit exitoso
[ ] Test de concurrencia (si aplica)
```
---
**Version:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Patron de Codigo

View File

@ -0,0 +1,619 @@
# PATRÓN: VALIDACIÓN DE DATOS
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Aplica a:** Backend (NestJS/Express), Frontend (React)
**Prioridad:** OBLIGATORIA
---
## PROPÓSITO
Definir patrones estándar de validación de datos para garantizar consistencia en toda la aplicación.
---
## PRINCIPIO FUNDAMENTAL
```
╔══════════════════════════════════════════════════════════════════════╗
║ VALIDACIÓN EN CAPAS ║
║ ║
║ 1. Frontend: UX inmediata (opcional pero recomendada) ║
║ 2. DTO: Validación de entrada (OBLIGATORIA) ║
║ 3. Service: Validación de negocio (cuando aplica) ║
║ 4. Database: Constraints (última línea de defensa) ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## 1. VALIDACIÓN EN DTO (NestJS - class-validator)
### Decoradores Básicos
```typescript
import {
IsString,
IsEmail,
IsNotEmpty,
IsOptional,
IsUUID,
IsInt,
IsNumber,
IsBoolean,
IsDate,
IsArray,
IsEnum,
IsUrl,
IsPhoneNumber,
} from 'class-validator';
```
### Decoradores de Longitud
```typescript
import {
Length,
MinLength,
MaxLength,
Min,
Max,
ArrayMinSize,
ArrayMaxSize,
} from 'class-validator';
```
### Decoradores de Formato
```typescript
import {
Matches,
IsAlpha,
IsAlphanumeric,
IsAscii,
Contains,
IsISO8601,
IsCreditCard,
IsHexColor,
IsJSON,
} from 'class-validator';
```
---
## 2. PATRONES POR TIPO DE DATO
### Email
```typescript
// PATRÓN ESTÁNDAR
@ApiProperty({
description: 'Correo electrónico del usuario',
example: 'usuario@empresa.com',
})
@IsEmail({}, { message: 'El correo electrónico no es válido' })
@IsNotEmpty({ message: 'El correo electrónico es requerido' })
@MaxLength(255, { message: 'El correo no puede exceder 255 caracteres' })
@Transform(({ value }) => value?.toLowerCase().trim())
email: string;
```
### Password
```typescript
// PATRÓN ESTÁNDAR - Contraseña segura
@ApiProperty({
description: 'Contraseña (mín 8 caracteres, 1 mayúscula, 1 número, 1 especial)',
example: 'MiPassword123!',
})
@IsString()
@IsNotEmpty({ message: 'La contraseña es requerida' })
@MinLength(8, { message: 'La contraseña debe tener al menos 8 caracteres' })
@MaxLength(100, { message: 'La contraseña no puede exceder 100 caracteres' })
@Matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/,
{ message: 'La contraseña debe contener mayúscula, minúscula, número y carácter especial' }
)
password: string;
```
### UUID
```typescript
// PATRÓN ESTÁNDAR
@ApiProperty({
description: 'ID único del recurso',
example: '550e8400-e29b-41d4-a716-446655440000',
})
@IsUUID('4', { message: 'El ID debe ser un UUID válido' })
@IsNotEmpty({ message: 'El ID es requerido' })
id: string;
// UUID Opcional (para referencias)
@ApiPropertyOptional({
description: 'ID del usuario relacionado',
})
@IsOptional()
@IsUUID('4', { message: 'El ID de usuario debe ser un UUID válido' })
userId?: string;
```
### Nombre/Texto Simple
```typescript
// PATRÓN ESTÁNDAR
@ApiProperty({
description: 'Nombre del usuario',
example: 'Juan Pérez',
minLength: 2,
maxLength: 100,
})
@IsString({ message: 'El nombre debe ser texto' })
@IsNotEmpty({ message: 'El nombre es requerido' })
@MinLength(2, { message: 'El nombre debe tener al menos 2 caracteres' })
@MaxLength(100, { message: 'El nombre no puede exceder 100 caracteres' })
@Transform(({ value }) => value?.trim())
name: string;
```
### Número Entero
```typescript
// PATRÓN ESTÁNDAR - Entero positivo
@ApiProperty({
description: 'Cantidad de items',
example: 10,
minimum: 1,
})
@IsInt({ message: 'La cantidad debe ser un número entero' })
@Min(1, { message: 'La cantidad mínima es 1' })
@Max(10000, { message: 'La cantidad máxima es 10,000' })
quantity: number;
// Entero con valor por defecto
@ApiPropertyOptional({
description: 'Página actual',
default: 1,
})
@IsOptional()
@IsInt()
@Min(1)
@Transform(({ value }) => parseInt(value) || 1)
page?: number = 1;
```
### Número Decimal (Dinero)
```typescript
// PATRÓN ESTÁNDAR - Precio/Dinero
@ApiProperty({
description: 'Precio del producto',
example: 99.99,
minimum: 0,
})
@IsNumber(
{ maxDecimalPlaces: 2 },
{ message: 'El precio debe tener máximo 2 decimales' }
)
@Min(0, { message: 'El precio no puede ser negativo' })
@Max(999999.99, { message: 'El precio máximo es 999,999.99' })
price: number;
```
### Boolean
```typescript
// PATRÓN ESTÁNDAR
@ApiProperty({
description: 'Estado activo del usuario',
example: true,
})
@IsBoolean({ message: 'El valor debe ser verdadero o falso' })
@IsNotEmpty()
isActive: boolean;
// Boolean opcional con default
@ApiPropertyOptional({
description: 'Enviar notificación',
default: false,
})
@IsOptional()
@IsBoolean()
@Transform(({ value }) => value === 'true' || value === true)
sendNotification?: boolean = false;
```
### Enum
```typescript
// PATRÓN ESTÁNDAR
export enum UserStatus {
ACTIVE = 'active',
INACTIVE = 'inactive',
SUSPENDED = 'suspended',
}
@ApiProperty({
description: 'Estado del usuario',
enum: UserStatus,
example: UserStatus.ACTIVE,
})
@IsEnum(UserStatus, { message: 'El estado debe ser: active, inactive o suspended' })
@IsNotEmpty()
status: UserStatus;
```
### Fecha
```typescript
// PATRÓN ESTÁNDAR - Fecha ISO
@ApiProperty({
description: 'Fecha de nacimiento',
example: '1990-05-15',
})
@IsISO8601({}, { message: 'La fecha debe estar en formato ISO 8601' })
@IsNotEmpty()
birthDate: string;
// Fecha como Date object
@ApiProperty({
description: 'Fecha de inicio',
example: '2024-01-15T10:30:00Z',
})
@IsDate({ message: 'Debe ser una fecha válida' })
@Type(() => Date)
@IsNotEmpty()
startDate: Date;
```
### URL
```typescript
// PATRÓN ESTÁNDAR
@ApiPropertyOptional({
description: 'URL del avatar',
example: 'https://example.com/avatar.jpg',
})
@IsOptional()
@IsUrl({}, { message: 'La URL no es válida' })
@MaxLength(500)
avatarUrl?: string;
```
### Teléfono
```typescript
// PATRÓN ESTÁNDAR - México
@ApiPropertyOptional({
description: 'Número de teléfono',
example: '+521234567890',
})
@IsOptional()
@IsPhoneNumber('MX', { message: 'El número de teléfono no es válido' })
phone?: string;
// Alternativa con Regex
@IsOptional()
@Matches(/^\+?[1-9]\d{1,14}$/, { message: 'Formato de teléfono inválido' })
phone?: string;
```
### Array
```typescript
// PATRÓN ESTÁNDAR - Array de strings
@ApiProperty({
description: 'Etiquetas del producto',
example: ['electrónica', 'ofertas'],
type: [String],
})
@IsArray({ message: 'Las etiquetas deben ser un arreglo' })
@IsString({ each: true, message: 'Cada etiqueta debe ser texto' })
@ArrayMinSize(1, { message: 'Debe haber al menos una etiqueta' })
@ArrayMaxSize(10, { message: 'Máximo 10 etiquetas permitidas' })
tags: string[];
// Array de UUIDs
@ApiProperty({
description: 'IDs de categorías',
type: [String],
})
@IsArray()
@IsUUID('4', { each: true, message: 'Cada ID debe ser un UUID válido' })
@ArrayMinSize(1)
categoryIds: string[];
```
### JSON/Object
```typescript
// PATRÓN ESTÁNDAR - Objeto flexible
@ApiPropertyOptional({
description: 'Metadatos adicionales',
example: { key: 'value' },
})
@IsOptional()
@IsObject({ message: 'Los metadatos deben ser un objeto' })
metadata?: Record<string, any>;
// Objeto con estructura definida (usar class anidada)
class AddressDto {
@IsString()
@IsNotEmpty()
street: string;
@IsString()
@IsNotEmpty()
city: string;
@IsString()
@Length(5, 5)
zipCode: string;
}
@ApiProperty({ type: AddressDto })
@ValidateNested()
@Type(() => AddressDto)
address: AddressDto;
```
---
## 3. DTOs ESTÁNDAR
### CreateDto Pattern
```typescript
/**
* DTO para crear usuario
*
* @example
* {
* "email": "user@example.com",
* "password": "SecurePass123!",
* "name": "Juan Pérez"
* }
*/
export class CreateUserDto {
@ApiProperty({ description: 'Email del usuario', example: 'user@example.com' })
@IsEmail()
@IsNotEmpty()
@MaxLength(255)
@Transform(({ value }) => value?.toLowerCase().trim())
email: string;
@ApiProperty({ description: 'Contraseña', example: 'SecurePass123!' })
@IsString()
@IsNotEmpty()
@MinLength(8)
@Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/)
password: string;
@ApiProperty({ description: 'Nombre completo', example: 'Juan Pérez' })
@IsString()
@IsNotEmpty()
@MinLength(2)
@MaxLength(100)
@Transform(({ value }) => value?.trim())
name: string;
@ApiPropertyOptional({ description: 'Teléfono', example: '+521234567890' })
@IsOptional()
@IsPhoneNumber('MX')
phone?: string;
}
```
### UpdateDto Pattern
```typescript
import { PartialType, OmitType } from '@nestjs/swagger';
/**
* DTO para actualizar usuario
*
* Todos los campos son opcionales.
* Email y password NO se pueden actualizar aquí.
*/
export class UpdateUserDto extends PartialType(
OmitType(CreateUserDto, ['email', 'password'] as const)
) {}
```
### QueryDto Pattern (Filtros y Paginación)
```typescript
/**
* DTO para búsqueda y paginación de usuarios
*/
export class QueryUsersDto {
@ApiPropertyOptional({ description: 'Página', default: 1 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number = 1;
@ApiPropertyOptional({ description: 'Items por página', default: 20 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
@Max(100)
limit?: number = 20;
@ApiPropertyOptional({ description: 'Buscar por nombre o email' })
@IsOptional()
@IsString()
@MaxLength(100)
search?: string;
@ApiPropertyOptional({ description: 'Filtrar por estado', enum: UserStatus })
@IsOptional()
@IsEnum(UserStatus)
status?: UserStatus;
@ApiPropertyOptional({ description: 'Ordenar por campo' })
@IsOptional()
@IsIn(['name', 'email', 'createdAt'])
sortBy?: string = 'createdAt';
@ApiPropertyOptional({ description: 'Dirección de orden', enum: ['asc', 'desc'] })
@IsOptional()
@IsIn(['asc', 'desc'])
sortOrder?: 'asc' | 'desc' = 'desc';
}
```
---
## 4. VALIDACIÓN EN SERVICE (Lógica de Negocio)
```typescript
@Injectable()
export class UserService {
async create(dto: CreateUserDto): Promise<UserEntity> {
// Validación de negocio (después de DTO)
// 1. Verificar unicidad
const existing = await this.repository.findOne({
where: { email: dto.email },
});
if (existing) {
throw new ConflictException('El email ya está registrado');
}
// 2. Validar reglas de negocio
if (await this.isEmailDomainBlocked(dto.email)) {
throw new BadRequestException('Dominio de email no permitido');
}
// 3. Validar límites
const userCount = await this.repository.count({
where: { tenantId: dto.tenantId },
});
if (userCount >= this.MAX_USERS_PER_TENANT) {
throw new ForbiddenException('Límite de usuarios alcanzado');
}
// Proceder con creación
const user = this.repository.create(dto);
return this.repository.save(user);
}
}
```
---
## 5. VALIDACIÓN EN FRONTEND (React)
### Con React Hook Form + Zod
```typescript
import { z } from 'zod';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
// Schema de validación (espejo del DTO backend)
const createUserSchema = z.object({
email: z
.string()
.min(1, 'El email es requerido')
.email('Email inválido')
.max(255),
password: z
.string()
.min(8, 'Mínimo 8 caracteres')
.regex(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/,
'Debe contener mayúscula, minúscula, número y carácter especial'
),
name: z
.string()
.min(2, 'Mínimo 2 caracteres')
.max(100, 'Máximo 100 caracteres'),
phone: z
.string()
.regex(/^\+?[1-9]\d{1,14}$/, 'Teléfono inválido')
.optional(),
});
type CreateUserForm = z.infer<typeof createUserSchema>;
// Uso en componente
const UserForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<CreateUserForm>({
resolver: zodResolver(createUserSchema),
});
const onSubmit = async (data: CreateUserForm) => {
// data ya está validado
await userService.create(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} />
{errors.email && <span>{errors.email.message}</span>}
{/* ... */}
</form>
);
};
```
---
## 6. MENSAJES DE ERROR ESTÁNDAR
```typescript
// Mensajes consistentes en español
const VALIDATION_MESSAGES = {
required: (field: string) => `${field} es requerido`,
minLength: (field: string, min: number) => `${field} debe tener al menos ${min} caracteres`,
maxLength: (field: string, max: number) => `${field} no puede exceder ${max} caracteres`,
email: 'El correo electrónico no es válido',
uuid: 'El ID no es válido',
enum: (values: string[]) => `El valor debe ser uno de: ${values.join(', ')}`,
min: (field: string, min: number) => `${field} debe ser mayor o igual a ${min}`,
max: (field: string, max: number) => `${field} debe ser menor o igual a ${max}`,
pattern: (field: string) => `${field} tiene un formato inválido`,
unique: (field: string) => `${field} ya existe`,
};
```
---
## CHECKLIST DE VALIDACIÓN
```
DTO:
[ ] Cada campo tiene @ApiProperty o @ApiPropertyOptional
[ ] Campos requeridos tienen @IsNotEmpty
[ ] Campos opcionales tienen @IsOptional
[ ] Tipos validados (@IsString, @IsNumber, etc.)
[ ] Longitudes validadas (@MinLength, @MaxLength)
[ ] Formatos validados (@IsEmail, @IsUUID, etc.)
[ ] Transformaciones aplicadas (@Transform)
[ ] Mensajes de error en español
Service:
[ ] Validación de unicidad
[ ] Validación de existencia (referencias)
[ ] Validación de reglas de negocio
[ ] Validación de permisos
Frontend:
[ ] Schema Zod espeja DTO backend
[ ] Mensajes de error visibles
[ ] Validación en submit
[ ] Validación en blur (opcional)
```
---
**Versión:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Patrón de Validación

View File

@ -0,0 +1,413 @@
# ORDEN DE IMPLEMENTACIÓN
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** OBLIGATORIA - DDL-First
**Sistema:** SIMCO + CAPVED
---
## PROPÓSITO
Definir el orden correcto de implementación cuando una tarea toca múltiples capas.
---
## PRINCIPIO FUNDAMENTAL
```
╔══════════════════════════════════════════════════════════════════════╗
║ DDL-FIRST: La Base de Datos es la Fuente de Verdad ║
║ ║
║ ORDEN OBLIGATORIO: ║
║ ║
║ 1. DATABASE → Tablas existen ║
║ 2. BACKEND → Entity refleja DDL ║
║ 3. FRONTEND → Types reflejan DTOs ║
║ ║
║ ⚠️ NUNCA invertir este orden ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## 1. FLUJO COMPLETO DE IMPLEMENTACIÓN
```
┌─────────────────────────────────────────────────────────────────────┐
│ FASE 1: BASE DE DATOS │
│ (Database-Agent) │
├─────────────────────────────────────────────────────────────────────┤
│ [ ] 1.1 Crear/modificar archivo DDL │
│ [ ] 1.2 Definir columnas con tipos correctos │
│ [ ] 1.3 Definir PK, FK, constraints │
│ [ ] 1.4 Crear índices necesarios │
│ [ ] 1.5 Ejecutar carga limpia (recreate-database.sh) │
│ [ ] 1.6 Verificar con \dt, \d {tabla} │
│ [ ] 1.7 Actualizar DATABASE_INVENTORY.yml │
│ [ ] 1.8 Registrar en TRAZA-TAREAS-DATABASE.md │
│ │
│ GATE: Carga limpia exitosa antes de continuar │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ FASE 2: BACKEND │
│ (Backend-Agent) │
├─────────────────────────────────────────────────────────────────────┤
│ [ ] 2.1 Crear Entity alineada con DDL (ver MAPEO-TIPOS.md) │
│ [ ] 2.2 Crear CreateDto con validaciones │
│ [ ] 2.3 Crear UpdateDto (extends PartialType) │
│ [ ] 2.4 Crear ResponseDto │
│ [ ] 2.5 Crear Service con lógica de negocio │
│ [ ] 2.6 Crear Controller con Swagger decorators │
│ [ ] 2.7 Registrar en Module │
│ [ ] 2.8 Ejecutar: npm run build │
│ [ ] 2.9 Ejecutar: npm run lint │
│ [ ] 2.10 Ejecutar: npm run test (si hay tests) │
│ [ ] 2.11 Actualizar BACKEND_INVENTORY.yml │
│ [ ] 2.12 Registrar en TRAZA-TAREAS-BACKEND.md │
│ │
│ GATE: Build y lint pasan antes de continuar │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ FASE 3: FRONTEND │
│ (Frontend-Agent) │
├─────────────────────────────────────────────────────────────────────┤
│ [ ] 3.1 Crear types/interfaces (alineados con ResponseDto) │
│ [ ] 3.2 Crear Zod schema (alineado con CreateDto) │
│ [ ] 3.3 Crear API service │
│ [ ] 3.4 Crear custom hook (useQuery/useMutation) │
│ [ ] 3.5 Crear componentes necesarios │
│ [ ] 3.6 Crear página/formulario │
│ [ ] 3.7 Ejecutar: npm run build │
│ [ ] 3.8 Ejecutar: npm run lint │
│ [ ] 3.9 Ejecutar: npm run typecheck │
│ [ ] 3.10 Actualizar FRONTEND_INVENTORY.yml │
│ [ ] 3.11 Registrar en TRAZA-TAREAS-FRONTEND.md │
│ │
│ GATE: Build, lint y typecheck pasan │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ FASE 4: INTEGRACIÓN │
│ (Orquestador) │
├─────────────────────────────────────────────────────────────────────┤
│ [ ] 4.1 Verificar flujo completo funciona │
│ [ ] 4.2 Verificar Swagger documenta correctamente │
│ [ ] 4.3 Tests e2e (si existen) │
│ [ ] 4.4 Actualizar MASTER_INVENTORY.yml │
│ [ ] 4.5 Actualizar PROXIMA-ACCION.md │
│ [ ] 4.6 Propagar a niveles superiores (SIMCO-PROPAGACION.md) │
│ │
│ GATE: Todo funciona end-to-end │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 2. POR QUÉ DDL-FIRST
### Problema: Backend-First
```typescript
// Alguien crea Entity primero sin DDL...
@Entity()
export class ProductEntity {
@Column()
price: number; // ← Asume que existe en BD
}
// Resultado:
// - TypeORM syncronize: Crea tabla con tipos incorrectos
// - Sin syncronize: Error en runtime "column not found"
// - Nadie sabe qué tipo debería ser price (DECIMAL? NUMERIC? INTEGER?)
```
### Solución: DDL-First
```sql
-- 1. DDL define la verdad
CREATE TABLE products (
price DECIMAL(10,2) NOT NULL -- Explícito: 10 dígitos, 2 decimales
);
```
```typescript
// 2. Entity REFLEJA la verdad
@Column({ type: 'decimal', precision: 10, scale: 2 })
price: string; // String para precisión decimal
// 3. DTO documenta para Swagger
@ApiProperty({ example: 99.99 })
@IsNumber({ maxDecimalPlaces: 2 })
price: number;
```
---
## 3. DEPENDENCIAS ENTRE CAPAS
### Diagrama de Dependencias
```
┌──────────────┐
│ DATABASE │
│ (DDL) │
└──────┬───────┘
│ Entity refleja DDL
┌──────────────┐
│ BACKEND │
│ (Entity,DTO) │
└──────┬───────┘
│ Types reflejan DTOs
┌──────────────┐
│ FRONTEND │
│(Types,Forms) │
└──────────────┘
```
### Reglas de Dependencia
| Si cambias... | Debes actualizar... | En ese orden |
|---------------|---------------------|--------------|
| DDL columna | Entity → DTO → Types | DDL → BE → FE |
| Entity campo | DTO → Types | BE → FE |
| DTO campo | Types | BE → FE |
| Types | - | FE solo |
---
## 4. ANTI-PATRONES DE ORDEN
### Anti-Patrón 1: Frontend First
```
❌ INCORRECTO:
1. Frontend-Agent crea formulario
2. Backend-Agent crea endpoint
3. Database-Agent... ¿qué campos necesita?
RESULTADO:
- Frontend asume campos que no existen
- Backend inventa estructura
- Database no sabe qué crear
- Retrabajos múltiples
```
### Anti-Patrón 2: Parallel sin Coordinación
```
❌ INCORRECTO:
1. Database-Agent crea tabla (en paralelo)
2. Backend-Agent crea entity (en paralelo)
3. Frontend-Agent crea types (en paralelo)
RESULTADO:
- Cada uno asume diferente
- Tipos no coinciden
- Errores en integración
```
### Anti-Patrón 3: Saltar Validación
```
❌ INCORRECTO:
1. Database-Agent crea tabla (OK)
2. Backend-Agent crea entity (sin build)
3. Frontend-Agent crea types (sin typecheck)
RESULTADO:
- Errores no detectados hasta runtime
- Bugs en producción
- Debug difícil
```
---
## 5. TEMPLATES POR CASO
### Caso A: Feature Nueva Completa
```
TAREA: Crear sistema de comentarios
FASE 1: DATABASE (Database-Agent)
═══════════════════════════════════════════════════════════════
Crear: schemas/core/tables/05-comments.sql
CREATE TABLE core.comments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
content TEXT NOT NULL,
user_id UUID NOT NULL REFERENCES auth.users(id),
post_id UUID NOT NULL REFERENCES core.posts(id),
parent_id UUID REFERENCES core.comments(id),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_comments_post ON core.comments(post_id);
CREATE INDEX idx_comments_user ON core.comments(user_id);
Validar: ./recreate-database.sh && psql -c "\d core.comments"
FASE 2: BACKEND (Backend-Agent)
═══════════════════════════════════════════════════════════════
Crear:
- modules/comment/entities/comment.entity.ts
- modules/comment/dto/create-comment.dto.ts
- modules/comment/dto/update-comment.dto.ts
- modules/comment/dto/comment-response.dto.ts
- modules/comment/services/comment.service.ts
- modules/comment/controllers/comment.controller.ts
- modules/comment/comment.module.ts
Validar: npm run build && npm run lint
FASE 3: FRONTEND (Frontend-Agent)
═══════════════════════════════════════════════════════════════
Crear:
- shared/types/comment.types.ts
- shared/services/comment.service.ts
- apps/web/hooks/useComments.ts
- apps/web/components/comments/CommentCard.tsx
- apps/web/components/comments/CommentForm.tsx
- apps/web/components/comments/CommentList.tsx
Validar: npm run build && npm run lint && npm run typecheck
FASE 4: INTEGRACIÓN
═══════════════════════════════════════════════════════════════
- Test flujo completo
- Verificar Swagger
- Actualizar inventarios
```
### Caso B: Agregar Campo a Feature Existente
```
TAREA: Agregar campo "rating" a comments
FASE 1: DATABASE
═══════════════════════════════════════════════════════════════
ALTER TABLE core.comments ADD COLUMN rating INTEGER CHECK (rating BETWEEN 1 AND 5);
Validar: Carga limpia
FASE 2: BACKEND
═══════════════════════════════════════════════════════════════
- Agregar @Column en CommentEntity
- Agregar campo en CreateCommentDto con @IsInt @Min(1) @Max(5)
- Agregar en ResponseDto
Validar: npm run build
FASE 3: FRONTEND
═══════════════════════════════════════════════════════════════
- Agregar rating en Comment interface
- Agregar input en CommentForm
- Mostrar en CommentCard
Validar: npm run build && npm run typecheck
```
### Caso C: Cambio Solo en Backend (Nueva Lógica)
```
TAREA: Agregar validación de spam en comentarios
FASE 1: DATABASE
═══════════════════════════════════════════════════════════════
- SIN CAMBIOS (lógica no afecta schema)
FASE 2: BACKEND
═══════════════════════════════════════════════════════════════
- Agregar SpamService
- Modificar CommentService.create() para validar
Validar: npm run build && npm run test
FASE 3: FRONTEND
═══════════════════════════════════════════════════════════════
- SIN CAMBIOS (endpoint sigue igual)
- Posible: Mostrar error si spam detectado
```
---
## 6. CHECKLIST RÁPIDO
```
Antes de empezar implementación:
[ ] ¿La tabla existe en DDL? Si no → DATABASE primero
[ ] ¿Entity refleja DDL actual? Si no → Actualizar Entity
[ ] ¿DTOs coinciden con Entity? Si no → Actualizar DTOs
[ ] ¿Types coinciden con DTOs? Si no → Actualizar Types
Durante implementación:
[ ] No crear Entity sin DDL
[ ] No crear Types sin DTOs
[ ] No modificar DDL sin actualizar Entity
[ ] No modificar DTO sin actualizar Types
Después de cada capa:
[ ] Build pasa
[ ] Lint pasa
[ ] Tests pasan (si existen)
[ ] Inventario actualizado
```
---
## 7. COMANDOS DE VALIDACIÓN
```bash
# FASE 1: Validar Database
cd {DB_SCRIPTS_PATH}
./recreate-database.sh
psql -d {DB_NAME} -c "\dt {schema}.*"
psql -d {DB_NAME} -c "\d {schema}.{tabla}"
# FASE 2: Validar Backend
cd {BACKEND_ROOT}
npm run build
npm run lint
npm run test
# FASE 3: Validar Frontend
cd {FRONTEND_ROOT}
npm run build
npm run lint
npm run typecheck
# FASE 4: Validar Integración
curl http://localhost:3000/api/{endpoint}
# Verificar respuesta correcta
```
---
## 8. MATRIZ DE DELEGACIÓN
| Tarea | Database-Agent | Backend-Agent | Frontend-Agent | Orquestador |
|-------|----------------|---------------|----------------|-------------|
| Crear tabla | ✅ | ❌ | ❌ | Coordina |
| Modificar DDL | ✅ | ❌ | ❌ | Coordina |
| Crear Entity | ❌ | ✅ | ❌ | Verifica |
| Crear DTO | ❌ | ✅ | ❌ | Verifica |
| Crear Service | ❌ | ✅ | ❌ | Verifica |
| Crear Controller | ❌ | ✅ | ❌ | Verifica |
| Crear Types | ❌ | ❌ | ✅ | Verifica |
| Crear Component | ❌ | ❌ | ✅ | Verifica |
| Integración | ❌ | ❌ | ❌ | ✅ |
---
**Versión:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Guía de Proceso

View File

@ -79,6 +79,38 @@ global:
"@CHK_API": "core/orchestration/checklists/CHECKLIST-CODE-REVIEW-API.md"
"@CHK_REFACTOR": "core/orchestration/checklists/CHECKLIST-REFACTORIZACION.md"
# ═══════════════════════════════════════════════════════════════════
# PATRONES DE CÓDIGO (CONSULTAR ANTES DE IMPLEMENTAR)
# ═══════════════════════════════════════════════════════════════════
"@PATRONES": "core/orchestration/patrones/"
"@MAPEO_TIPOS": "core/orchestration/patrones/MAPEO-TIPOS-DDL-TYPESCRIPT.md"
"@PATRON_VALIDACION": "core/orchestration/patrones/PATRON-VALIDACION.md"
"@PATRON_EXCEPTIONS": "core/orchestration/patrones/PATRON-EXCEPTION-HANDLING.md"
"@PATRON_TESTING": "core/orchestration/patrones/PATRON-TESTING.md"
"@PATRON_LOGGING": "core/orchestration/patrones/PATRON-LOGGING.md"
"@PATRON_CONFIG": "core/orchestration/patrones/PATRON-CONFIGURACION.md"
"@PATRON_SEGURIDAD": "core/orchestration/patrones/PATRON-SEGURIDAD.md"
"@PATRON_PERFORMANCE": "core/orchestration/patrones/PATRON-PERFORMANCE.md"
"@PATRON_TRANSACCIONES": "core/orchestration/patrones/PATRON-TRANSACCIONES.md"
"@ANTIPATRONES": "core/orchestration/patrones/ANTIPATRONES.md"
"@NOMENCLATURA": "core/orchestration/patrones/NOMENCLATURA-UNIFICADA.md"
# ═══════════════════════════════════════════════════════════════════
# IMPACTOS DE CAMBIOS (CONSULTAR ANTES DE MODIFICAR)
# ═══════════════════════════════════════════════════════════════════
"@IMPACTOS": "core/orchestration/impactos/"
"@IMPACTO_DDL": "core/orchestration/impactos/IMPACTO-CAMBIOS-DDL.md"
"@IMPACTO_BACKEND": "core/orchestration/impactos/IMPACTO-CAMBIOS-BACKEND.md"
"@IMPACTO_ENTITY": "core/orchestration/impactos/IMPACTO-CAMBIOS-ENTITY.md"
"@IMPACTO_API": "core/orchestration/impactos/IMPACTO-CAMBIOS-API.md"
"@MATRIZ_DEPENDENCIAS": "core/orchestration/impactos/MATRIZ-DEPENDENCIAS.md"
# ═══════════════════════════════════════════════════════════════════
# PROCESOS DE TRABAJO
# ═══════════════════════════════════════════════════════════════════
"@PROCESOS": "core/orchestration/procesos/"
"@ORDEN_IMPLEMENTACION": "core/orchestration/procesos/ORDEN-IMPLEMENTACION.md"
# ─────────────────────────────────────────────────────────────────────────────────
# ALIAS DE OPERACIONES (atajos directos a directivas SIMCO)
# ─────────────────────────────────────────────────────────────────────────────────

View File

@ -0,0 +1,434 @@
# Análisis de Alineación del Workspace
**Fecha:** 2025-12-08
**Versión Sistema:** NEXUS v3.2 + SIMCO + CAPVED
---
## RESUMEN EJECUTIVO
Este documento presenta el análisis exhaustivo de alineación entre el sistema de orquestación central (`core/orchestration/`) y su implementación en todos los proyectos del workspace.
### Hallazgos Clave
| Área | Estado | Acción Requerida |
|------|--------|------------------|
| Sistema SIMCO/CAPVED | Completo (20+ directivas) | Base sólida |
| ERP-Core | 100% docs, 0% código | Implementar |
| Verticales | Parcialmente alineadas | Propagar estándares |
| SaaS Layer | 0% (vacío) | Crear desde cero |
| Proyectos Standalone | Variable | Homologar |
---
## 1. SISTEMA DE ORQUESTACIÓN (CORE)
### 1.1 Componentes Implementados
| Componente | Ubicación | Archivos | Estado |
|------------|-----------|----------|--------|
| Directivas SIMCO | `core/orchestration/directivas/simco/` | 17 archivos | Completo |
| Principios | `core/orchestration/directivas/principios/` | 5 archivos | Completo |
| Perfiles Agentes | `core/orchestration/agents/perfiles/` | 12 archivos | Completo |
| Templates | `core/orchestration/templates/` | 14 archivos | Completo |
| Checklists | `core/orchestration/checklists/` | 3 archivos | Completo |
| Catálogo | `core/catalog/` | 8 funcionalidades | Documentado |
### 1.2 Directivas SIMCO Disponibles
```
SIMCO-TAREA.md # Ciclo CAPVED (obligatorio)
SIMCO-CREAR.md # Crear archivos nuevos
SIMCO-MODIFICAR.md # Modificar existentes
SIMCO-VALIDAR.md # Validación obligatoria
SIMCO-DOCUMENTAR.md # Documentación
SIMCO-DELEGACION.md # Delegar a subagentes
SIMCO-BUSCAR.md # Exploración
SIMCO-REUTILIZAR.md # Uso del catálogo
SIMCO-DDL.md # Base de datos
SIMCO-BACKEND.md # NestJS/TypeORM
SIMCO-FRONTEND.md # React/TypeScript
SIMCO-NIVELES.md # Jerarquía de proyectos
SIMCO-PROPAGACION.md # Propagación de cambios
SIMCO-ML.md # Machine Learning
SIMCO-MOBILE.md # Aplicaciones móviles
SIMCO-ALINEACION.md # Alineación entre capas
SIMCO-DECISION-MATRIZ.md # Matriz de decisiones
```
### 1.3 Principios Fundamentales (5)
1. **PRINCIPIO-CAPVED** - Ciclo obligatorio: Contexto→Análisis→Planeación→Validación→Ejecución→Documentación
2. **PRINCIPIO-DOC-PRIMERO** - Documentar antes de implementar
3. **PRINCIPIO-ANTI-DUPLICACION** - Verificar catálogo/inventarios antes de crear
4. **PRINCIPIO-VALIDACION-OBLIGATORIA** - Build+Lint+Tests deben pasar
5. **PRINCIPIO-ECONOMIA-TOKENS** - Desglosar tareas grandes
### 1.4 Catálogo de Funcionalidades Reutilizables
| Funcionalidad | Ubicación | Estado |
|---------------|-----------|--------|
| auth | `core/catalog/auth/` | Documentado |
| session-management | `core/catalog/session-management/` | Documentado |
| rate-limiting | `core/catalog/rate-limiting/` | Documentado |
| notifications | `core/catalog/notifications/` | Documentado |
| multi-tenancy | `core/catalog/multi-tenancy/` | Documentado |
| feature-flags | `core/catalog/feature-flags/` | Documentado |
| websocket | `core/catalog/websocket/` | Documentado |
| payments | `core/catalog/payments/` | Documentado |
---
## 2. ANÁLISIS POR PROYECTO
### 2.1 ERP-SUITE
#### Estado General
| Componente | Documentación | Código | BD | Orquestación |
|------------|---------------|--------|-----|--------------|
| ERP-Core | 100% (827 MD) | 0% | 0% | 100% |
| Construcción | 100% (449 MD) | 25% | 0% | 100% |
| Mecánicas Diesel | 95% (75 MD) | 0% | 0% | 100% |
| Vidrio Templado | 0% | 0% | 0% | 50% |
| Retail | 0% | 0% | 0% | 50% |
| Clínicas | 0% | 0% | 0% | 50% |
| SaaS Layer | 10% | 0% | 0% | 0% |
#### Gaps Identificados en ERP-Suite
| ID | Gap | Severidad | Ubicación |
|----|-----|-----------|-----------|
| GAP-ERP-001 | SaaS layer vacío | CRÍTICO | `apps/saas/` |
| GAP-ERP-002 | shared-libs vacío | ALTO | `apps/shared-libs/` |
| GAP-ERP-003 | Verticales sin HERENCIA-ERP-CORE.md | MEDIO | 3 de 5 verticales |
| GAP-ERP-004 | Inventarios desactualizados | MEDIO | Varios |
| GAP-ERP-005 | No existe POS básico minimalista | CRÍTICO | No existe |
### 2.2 Otros Proyectos
| Proyecto | Estado | Docs | Código | Alineación |
|----------|--------|------|--------|------------|
| Gamilit | Producción | 470 MD | Completo | Alta |
| Trading-Platform | Desarrollo | 259 MD | 70% | Alta |
| Betting-Analytics | Planificación | 1 MD | 0% | Parcial |
| Inmobiliaria-Analytics | Planificación | 1 MD | 0% | Parcial |
---
## 3. GAPS DE ALINEACIÓN CRÍTICOS
### 3.1 Gaps Estructurales
| # | Descripción | Proyectos Afectados | Impacto |
|---|-------------|---------------------|---------|
| 1 | Falta estructura SaaS multi-tier | erp-suite | Bloqueante para comercialización |
| 2 | Falta POS minimalista (100 MXN) | erp-suite | Mercado desatendido |
| 3 | Inventarios no propagados | Todos | Trazabilidad incompleta |
| 4 | HERENCIA-DIRECTIVAS inconsistente | Verticales | Duplicación de esfuerzo |
### 3.2 Gaps de Documentación
| # | Descripción | Ubicación |
|---|-------------|-----------|
| 1 | ERP-Core sin README actualizado con arquitectura SaaS | `erp-suite/apps/erp-core/` |
| 2 | Verticales sin docs de herencia | 3 verticales |
| 3 | Catálogo sin código de referencia | `core/catalog/*/` |
| 4 | betting/inmobiliaria sin documentación | 2 proyectos |
### 3.3 Gaps de Implementación
| # | Descripción | Prioridad |
|---|-------------|-----------|
| 1 | ERP-Core 0% código con 100% docs | P0 |
| 2 | SaaS Layer inexistente | P0 |
| 3 | POS Básico no existe | P0 |
| 4 | Mecánicas Diesel 0% código | P1 |
---
## 4. PROPUESTA: ARQUITECTURA SAAS MULTI-TIER
### 4.1 Estructura Propuesta
```
erp-suite/
├── apps/
│ ├── erp-core/ # Base compartida (EXISTENTE)
│ │ ├── backend/ # API Core
│ │ ├── frontend/ # UI Core (componentes base)
│ │ └── database/ # Schemas compartidos
│ │
│ ├── saas/ # Capa SaaS (CREAR)
│ │ ├── billing/ # Facturación y suscripciones
│ │ ├── portal/ # Portal de clientes
│ │ ├── admin/ # Admin multi-tenant
│ │ └── onboarding/ # Autoregistro
│ │
│ ├── products/ # Productos SaaS (CREAR)
│ │ │
│ │ ├── erp-basico/ # ERP SaaS Mediano (~300-500 MXN/mes)
│ │ │ ├── backend/ # Hereda erp-core + módulos seleccionados
│ │ │ ├── frontend/ # UI simplificada
│ │ │ └── config/ # Feature flags para módulos
│ │ │
│ │ └── pos-micro/ # POS Ultra Básico (~100 MXN/mes)
│ │ ├── backend/ # Mínimo: ventas, inventario básico, reportes
│ │ ├── frontend/ # UI ultra simple (móvil-first)
│ │ ├── pwa/ # Progressive Web App
│ │ └── whatsapp/ # Integración WhatsApp Business
│ │
│ └── verticales/ # Extensiones por industria (EXISTENTE)
│ ├── construccion/
│ ├── mecanicas-diesel/
│ ├── vidrio-templado/
│ ├── retail/
│ └── clinicas/
```
### 4.2 Producto: ERP SaaS Mediano
**Target:** PyMEs que necesitan ERP integral pero económico
**Precio:** ~300-500 MXN/mes
**Características:**
| Módulo | Incluido | Descripción |
|--------|----------|-------------|
| Auth | Obligatorio | Login, roles básicos |
| Usuarios | Obligatorio | Gestión de usuarios |
| Multi-tenant | Obligatorio | Aislamiento por empresa |
| Inventario | Incluido | Control de stock básico |
| Ventas | Incluido | Cotizaciones, pedidos, facturas |
| Compras | Incluido | Órdenes de compra básicas |
| Clientes/Proveedores | Incluido | CRM básico |
| Reportes | Incluido | Reportes esenciales |
| Contabilidad | Opcional | +100 MXN/mes |
| RRHH | Opcional | +100 MXN/mes |
| WhatsApp Bot | Opcional | Por consumo de tokens |
### 4.3 Producto: POS Micro (Ultra Básico)
**Target:** Mercado informal mexicano
- Puestos de calle
- Tiendas de abarrotes/misceláneas
- Puestos de comida
- Pequeños locales
**Precio:** ~100 MXN/mes
**Modelo:** SaaS + consumo de IA
**Características MÍNIMAS:**
| Característica | Descripción |
|----------------|-------------|
| Punto de Venta | Vender productos, calcular cambio |
| Inventario Básico | Agregar productos, ver stock |
| Catálogo Simple | Lista de productos con precio |
| Corte de Caja | Resumen diario de ventas |
| Reportes Básicos | Ventas del día/semana/mes |
| WhatsApp Bot | Consultas de precio, stock, ventas |
| PWA Offline | Funciona sin internet (sincroniza después) |
**Arquitectura Minimalista:**
```
pos-micro/
├── backend/
│ ├── src/
│ │ ├── modules/
│ │ │ ├── auth/ # Login simple (email/WhatsApp)
│ │ │ ├── products/ # CRUD productos
│ │ │ ├── sales/ # Registrar ventas
│ │ │ ├── inventory/ # Stock básico
│ │ │ └── reports/ # Reportes simples
│ │ └── shared/
│ │ ├── tenant/ # Multi-tenant básico
│ │ └── whatsapp/ # Integración WA Business
├── frontend/
│ ├── pwa/ # Progressive Web App
│ │ ├── pages/
│ │ │ ├── pos/ # Pantalla de venta
│ │ │ ├── products/ # Gestión productos
│ │ │ ├── reports/ # Ver reportes
│ │ │ └── settings/ # Configuración
│ │ └── offline/ # Service Worker
└── database/
└── schemas/
└── pos_micro/ # ~10 tablas máximo
```
**Base de Datos Minimalista (~10 tablas):**
```sql
-- Schema: pos_micro
CREATE TABLE tenants (id, name, whatsapp_number, plan, created_at);
CREATE TABLE users (id, tenant_id, email, password_hash, role);
CREATE TABLE products (id, tenant_id, name, price, stock, barcode);
CREATE TABLE sales (id, tenant_id, user_id, total, payment_method, created_at);
CREATE TABLE sale_items (id, sale_id, product_id, quantity, price);
CREATE TABLE inventory_movements (id, tenant_id, product_id, quantity, type, created_at);
CREATE TABLE daily_closures (id, tenant_id, date, total_sales, total_cash);
CREATE TABLE whatsapp_sessions (id, tenant_id, phone, token, expires_at);
CREATE TABLE ai_usage (id, tenant_id, tokens_used, model, created_at);
CREATE TABLE subscriptions (id, tenant_id, plan, amount, status, next_billing);
```
### 4.4 Integración WhatsApp Business
```
Flujo Usuario POS Micro:
1. Usuario envía "hola" a número de WhatsApp
2. Bot responde: "Hola! Soy tu asistente. Puedo ayudarte con:
- Ver ventas del día
- Consultar stock de producto
- Agregar producto
- Ver reporte semanal"
3. Usuario: "ventas del día"
4. Bot: "Ventas hoy: $2,450 MXN (23 tickets)"
5. Usuario: "stock de coca cola"
6. Bot: "Coca Cola 600ml: 45 unidades en stock"
Costo: Tokens consumidos por consulta (~0.01-0.05 USD por consulta)
```
---
## 5. PLAN DE IMPLEMENTACIÓN
### Fase 1: Preparación (Inmediato)
| Tarea | Descripción | Prioridad |
|-------|-------------|-----------|
| 1.1 | Actualizar HERENCIA-ERP-CORE.md en todas las verticales | P0 |
| 1.2 | Crear estructura `apps/products/` | P0 |
| 1.3 | Crear documentación base para pos-micro | P0 |
| 1.4 | Crear documentación base para erp-basico | P0 |
| 1.5 | Actualizar SaaS layer con billing básico | P0 |
### Fase 2: POS Micro (Prioridad Alta)
| Tarea | Descripción | Estimación |
|-------|-------------|------------|
| 2.1 | Diseñar schema BD (~10 tablas) | 2h |
| 2.2 | Implementar backend mínimo | 8h |
| 2.3 | Implementar PWA frontend | 8h |
| 2.4 | Integrar WhatsApp Business API | 4h |
| 2.5 | Implementar offline-first | 4h |
| 2.6 | Testing y validación | 4h |
### Fase 3: ERP Básico SaaS
| Tarea | Descripción | Estimación |
|-------|-------------|------------|
| 3.1 | Definir módulos incluidos vs opcionales | 2h |
| 3.2 | Configurar feature flags | 4h |
| 3.3 | Implementar billing/suscripciones | 8h |
| 3.4 | Portal de onboarding | 8h |
| 3.5 | Testing multi-tenant | 4h |
### Fase 4: Propagación
| Tarea | Descripción |
|-------|-------------|
| 4.1 | Actualizar inventarios en todos los proyectos |
| 4.2 | Verificar HERENCIA-DIRECTIVAS en cada vertical |
| 4.3 | Ejecutar CHECKLIST-PROPAGACION en todos los niveles |
| 4.4 | Documentar dependencias entre productos |
---
## 6. MÉTRICAS DE ALINEACIÓN
### 6.1 Checklist de Alineación por Proyecto
```yaml
Proyecto Alineado:
Estructura:
[ ] apps/backend/ existe
[ ] apps/frontend/ existe
[ ] apps/database/ existe
[ ] docs/ con estructura estándar
[ ] orchestration/ con estructura NEXUS
Orchestration:
[ ] 00-guidelines/CONTEXTO-PROYECTO.md
[ ] PROXIMA-ACCION.md
[ ] inventarios/MASTER_INVENTORY.yml
[ ] trazas/ con archivos por capa
[ ] HERENCIA-DIRECTIVAS.md
Documentación:
[ ] README.md actualizado
[ ] Épicas documentadas
[ ] Historias de usuario
[ ] Especificaciones técnicas
[ ] Schemas de BD
Código:
[ ] Sigue estándares de nomenclatura
[ ] Entities alineadas con DDL
[ ] DTOs con validaciones
[ ] Tests implementados
[ ] Build + Lint pasan
```
### 6.2 Estado Actual de Alineación
| Proyecto | Estructura | Orchestration | Docs | Código | TOTAL |
|----------|------------|---------------|------|--------|-------|
| Gamilit | 100% | 100% | 100% | 90% | **97%** |
| Trading | 100% | 90% | 95% | 70% | **89%** |
| ERP-Core | 100% | 100% | 100% | 0% | **75%** |
| Construcción | 100% | 100% | 100% | 25% | **81%** |
| Mecánicas Diesel | 100% | 100% | 95% | 0% | **74%** |
| Vidrio Templado | 80% | 50% | 0% | 0% | **33%** |
| Retail | 80% | 50% | 0% | 0% | **33%** |
| Clínicas | 80% | 50% | 0% | 0% | **33%** |
| Betting | 80% | 40% | 5% | 0% | **31%** |
| Inmobiliaria | 80% | 40% | 5% | 0% | **31%** |
---
## 7. ACCIONES INMEDIATAS
### 7.1 Crear Productos SaaS
```bash
# Crear estructura de productos
mkdir -p projects/erp-suite/apps/products/erp-basico/{backend,frontend,database,docs,orchestration}
mkdir -p projects/erp-suite/apps/products/pos-micro/{backend,frontend,pwa,database,docs,orchestration}
```
### 7.2 Propagar HERENCIA-ERP-CORE.md
Verticales que necesitan el archivo:
- [ ] vidrio-templado
- [ ] retail
- [ ] clinicas
### 7.3 Actualizar SaaS Layer
```bash
mkdir -p projects/erp-suite/apps/saas/{billing,portal,admin,onboarding}
```
---
## 8. CONCLUSIONES
1. **El sistema de orquestación está completo** - SIMCO, CAPVED, perfiles y catálogo bien definidos
2. **La documentación es excelente** - ERP-Core tiene 827 MD, gap analysis completo vs Odoo
3. **Falta implementación de código** - 0% en ERP-Core a pesar de 100% documentación
4. **Falta capa SaaS** - Crítico para comercialización
5. **Falta producto POS minimalista** - Oportunidad de mercado desatendida (100 MXN/mes)
6. **Verticales parcialmente alineadas** - 3 de 5 sin HERENCIA-ERP-CORE.md
7. **Proyectos standalone bien alineados** - Gamilit y Trading-Platform son ejemplos a seguir
---
*Documento generado: 2025-12-08*
*Sistema: NEXUS v3.2 + SIMCO + CAPVED*

View File

@ -1,8 +1,8 @@
# WORKSPACE STATUS
**Nivel:** 0 - Workspace Root
**Actualizado:** 2025-12-08 (Post-Limpieza)
**Sistema:** SIMCO v2.2.0 + CAPVED
**Actualizado:** 2025-12-08 (Análisis Alineación + Productos SaaS)
**Sistema:** SIMCO v3.2 + CAPVED + NEXUS
---
@ -10,10 +10,11 @@
```yaml
estado: "OPERATIVO"
version_simco: "2.2.0"
version_simco: "3.2"
ultima_actualizacion: "2025-12-08"
proyectos_activos: 5
verticales_activos: 5
productos_saas: 2 # NUEVO: pos-micro, erp-basico
catalogo_funcionalidades: 8
```
@ -23,7 +24,25 @@ catalogo_funcionalidades: 8
### 2025-12-08
#### Corrección de Gaps de Documentación (NUEVO)
#### Análisis de Alineación y Productos SaaS (NUEVO)
- **Acción:** Análisis exhaustivo del sistema de orquestación y alineación de proyectos
- **Documento generado:** `ANALISIS-ALINEACION-WORKSPACE-2025-12-08.md`
- **Estructuras creadas:**
- `apps/products/pos-micro/` - POS ultra básico (100 MXN/mes)
- `apps/products/erp-basico/` - ERP austero (300-500 MXN/mes)
- `apps/saas/` - Capa de billing, portal, admin, onboarding
- **Documentación:**
- README.md y CONTEXTO-PROYECTO.md para cada producto
- README.md para SaaS layer
- CONTEXTO-SAAS.md para orquestación SaaS
- **Hallazgos:**
- Sistema SIMCO/CAPVED completo (20+ directivas)
- ERP-Core: 100% docs, 0% código
- Verticales: Todas con HERENCIA-ERP-CORE.md
- Productos SaaS: Nueva línea para mercado mexicano
- **Agente:** Claude Code
#### Corrección de Gaps de Documentación
- **Acción:** Corrección de 7 gaps identificados en análisis de documentación
- **Archivos creados:**
- `PERFIL-REQUIREMENTS-ANALYST.md` (v1.4.0)

View File

@ -0,0 +1,211 @@
# Mapeo de Especificaciones Transversales a Verticales
**Fecha:** 2025-12-08
**Versión:** 1.0
**Autor:** Sistema SIMCO
**Ubicación SPECS:** `docs/04-modelado/especificaciones-tecnicas/transversal/`
---
## Propósito
Este documento define qué especificaciones transversales del ERP-Core aplican a cada vertical del ERP-Suite. Sirve como referencia para la propagación de funcionalidades y el desarrollo de cada proyecto vertical.
---
## Leyenda
| Símbolo | Significado |
|---------|-------------|
| ✓ | Aplica - Debe implementarse |
| ○ | Opcional - Puede implementarse según necesidad |
| ✗ | No aplica - No es relevante para esta vertical |
---
## Matriz de Aplicabilidad
### SPECS P0 - Funcionales (Críticos)
| SPEC | Descripción | Construcción | Mecánicas | Vidrio | Retail | Clínicas |
|------|-------------|:------------:|:---------:|:------:|:------:|:--------:|
| SPEC-SISTEMA-SECUENCIAS | Secuencias automáticas de documentos | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-VALORACION-INVENTARIO | FIFO/AVCO valorización | ✓ | ✓ | ✓ | ✓ | ○ |
| SPEC-SEGURIDAD-API-KEYS-PERMISOS | API Keys + ACL + RLS | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-REPORTES-FINANCIEROS | Balance/P&L SAT | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-PORTAL-PROVEEDORES | Portal RFQ | ✓ | ✓ | ✓ | ○ | ✗ |
| SPEC-NOMINA-BASICA | hr_payroll | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-GASTOS-EMPLEADOS | hr_expense | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-TAREAS-RECURRENTES | project.task.recurrence | ✓ | ✓ | ✓ | ○ | ○ |
| SPEC-SCHEDULER-REPORTES | ir.cron + mail | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-INTEGRACION-CALENDAR | calendar integration | ○ | ✗ | ✗ | ✗ | ✓ |
### SPECS P1 - Complementarios
| SPEC | Descripción | Construcción | Mecánicas | Vidrio | Retail | Clínicas |
|------|-------------|:------------:|:---------:|:------:|:------:|:--------:|
| SPEC-CONTABILIDAD-ANALITICA-MULTIDIMENSIONAL | Centros de costo | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-CONCILIACION-BANCARIA | Conciliación automática | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-FIRMA-ELECTRONICA-NOM151 | Firma electrónica | ✓ | ○ | ○ | ○ | ✓ |
| SPEC-TWO-FACTOR-AUTHENTICATION | 2FA, TOTP, SMS | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-TRAZABILIDAD-LOTES-SERIES | Lotes y números de serie | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-PRICING-RULES | Reglas de precios | ○ | ✓ | ✓ | ✓ | ○ |
| SPEC-BLANKET-ORDERS | Órdenes marco | ✓ | ✓ | ✓ | ✓ | ✗ |
| SPEC-OAUTH2-SOCIAL-LOGIN | OAuth2, Google, Microsoft | ○ | ○ | ○ | ○ | ✓ |
| SPEC-INVENTARIOS-CICLICOS | Conteo cíclico | ○ | ✓ | ○ | ✓ | ○ |
| SPEC-IMPUESTOS-AVANZADOS | IVA, ISR configurables | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-PLANTILLAS-CUENTAS | Plan de cuentas por país | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-CONSOLIDACION-FINANCIERA | Multi-empresa | ○ | ○ | ○ | ○ | ○ |
| SPEC-TASAS-CAMBIO-AUTOMATICAS | Tipos de cambio | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-ALERTAS-PRESUPUESTO | Alertas de exceso | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-PRESUPUESTOS-REVISIONES | Aprobación de presupuestos | ✓ | ✓ | ✓ | ○ | ○ |
| SPEC-RRHH-EVALUACIONES-SKILLS | Evaluaciones, skills | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | Dependencias, burndown | ✓ | ✗ | ✓ | ✗ | ✗ |
| SPEC-LOCALIZACION-PAISES | Configuración por país | ✓ | ✓ | ✓ | ✓ | ✓ |
### Patrones Técnicos P0
| SPEC | Descripción | Construcción | Mecánicas | Vidrio | Retail | Clínicas |
|------|-------------|:------------:|:---------:|:------:|:------:|:--------:|
| SPEC-MAIL-THREAD-TRACKING | mail.thread mixin | ✓ | ✓ | ✓ | ✓ | ✓ |
| SPEC-WIZARD-TRANSIENT-MODEL | TransientModel | ✓ | ✓ | ✓ | ✓ | ✓ |
---
## Resumen por Vertical
### Construcción (MAI/MAE)
- **SPECS Aplicables:** 26/30
- **SPECS Obligatorias:** 22
- **SPECS Opcionales:** 4
- **SPECS No Aplican:** 4
- **Enfoque:** Proyectos, control de obra, estimaciones, RRHH construcción
### Mecánicas-Diesel (MMD)
- **SPECS Aplicables:** 25/30
- **SPECS Obligatorias:** 23
- **SPECS Opcionales:** 2
- **SPECS No Aplican:** 5
- **Enfoque:** Órdenes de trabajo, inventario refacciones, diagnósticos
### Vidrio-Templado (VT)
- **SPECS Aplicables:** 25/30
- **SPECS Obligatorias:** 22
- **SPECS Opcionales:** 3
- **SPECS No Aplican:** 5
- **Enfoque:** Producción, control de calidad, hornos de templado
### Retail (RT)
- **SPECS Aplicables:** 24/30
- **SPECS Obligatorias:** 21
- **SPECS Opcionales:** 3
- **SPECS No Aplican:** 6
- **Enfoque:** POS, inventario multi-sucursal, promociones, caja
### Clínicas (CL)
- **SPECS Aplicables:** 24/30
- **SPECS Obligatorias:** 20
- **SPECS Opcionales:** 4
- **SPECS No Aplican:** 6
- **Enfoque:** Expediente clínico, citas, calendario, cumplimiento normativo
---
## Detalle por Vertical
### Construcción
**SPECS Críticas para el Dominio:**
1. `SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN` - Control de obra y avances
2. `SPEC-VALORACION-INVENTARIO` - Costeo de materiales de construcción
3. `SPEC-TRAZABILIDAD-LOTES-SERIES` - Trazabilidad de materiales
4. `SPEC-PRESUPUESTOS-REVISIONES` - Control presupuestal de obras
**Adaptaciones Requeridas:**
- Proyectos = Obras/Fraccionamientos
- Tareas = Etapas de construcción
- Productos = Materiales de construcción
### Mecánicas-Diesel
**SPECS Críticas para el Dominio:**
1. `SPEC-VALORACION-INVENTARIO` - Costeo de refacciones
2. `SPEC-TRAZABILIDAD-LOTES-SERIES` - Tracking de partes OEM
3. `SPEC-INVENTARIOS-CICLICOS` - Control de stock
4. `SPEC-PRICING-RULES` - Reglas de precio por tipo de servicio
**Adaptaciones Requeridas:**
- Productos = Refacciones, partes
- Órdenes de venta = Órdenes de servicio
- Partners = Clientes con vehículos
### Vidrio-Templado
**SPECS Críticas para el Dominio:**
1. `SPEC-VALORACION-INVENTARIO` - Costeo de materia prima y producto terminado
2. `SPEC-TRAZABILIDAD-LOTES-SERIES` - Lotes de producción de vidrio
3. `SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN` - Órdenes de producción
4. `SPEC-PRICING-RULES` - Precios por dimensiones y tipos de vidrio
**Adaptaciones Requeridas:**
- Productos = Tipos de vidrio (templado, laminado, etc.)
- Producción = Control de hornos y parámetros
- Calidad = Inspecciones de fragmentación
### Retail
**SPECS Críticas para el Dominio:**
1. `SPEC-PRICING-RULES` - Promociones y descuentos
2. `SPEC-INVENTARIOS-CICLICOS` - Conteos en sucursales
3. `SPEC-TRAZABILIDAD-LOTES-SERIES` - Productos con lote/serie
4. `SPEC-VALORACION-INVENTARIO` - Costeo de mercancía
**Adaptaciones Requeridas:**
- Almacenes = Sucursales
- Ventas = Transacciones POS
- Clientes = Programa de lealtad
### Clínicas
**SPECS Críticas para el Dominio:**
1. `SPEC-INTEGRACION-CALENDAR` - Agenda de citas médicas
2. `SPEC-MAIL-THREAD-TRACKING` - Historial de comunicación con pacientes
3. `SPEC-RRHH-EVALUACIONES-SKILLS` - Credenciales médicas
4. `SPEC-FIRMA-ELECTRONICA-NOM151` - Firma de expedientes
**Adaptaciones Requeridas:**
- Partners = Pacientes
- Productos = Servicios médicos, medicamentos
- Calendario = Agenda de consultas
- Cumplimiento = NOM-024-SSA3-2012
---
## Workflows Aplicables
| Workflow | Construcción | Mecánicas | Vidrio | Retail | Clínicas |
|----------|:------------:|:---------:|:------:|:------:|:--------:|
| WORKFLOW-CIERRE-PERIODO-CONTABLE | ✓ | ✓ | ✓ | ✓ | ✓ |
| WORKFLOW-3-WAY-MATCH | ✓ | ✓ | ✓ | ○ | ○ |
| WORKFLOW-PAGOS-ANTICIPADOS | ✓ | ✓ | ✓ | ✗ | ✓ |
---
## Próximos Pasos
1. Crear `HERENCIA-SPECS-CORE.md` en cada vertical con detalle de implementación
2. Actualizar `HERENCIA-ERP-CORE.md` con referencia a SPECS aplicables
3. Documentar adaptaciones específicas por vertical en carpeta `transversal-core/`
---
## Referencias
- SPECS del Core: `erp-core/docs/04-modelado/especificaciones-tecnicas/transversal/`
- Análisis de Gaps: `erp-core/orchestration/01-analisis/ANALISIS-GAPS-CONSOLIDADO.md`
- Directiva de Extensión: `erp-core/orchestration/directivas/DIRECTIVA-EXTENSION-VERTICALES.md`
---
**Documento de referencia canónico para propagación de SPECS**
**Última actualización:** 2025-12-08

View File

@ -231,24 +231,74 @@ modulos:
verticales_dependientes:
- nombre: construccion
estado: 35%
codigo: MAI/MAE
estado: 40%
fase: EN_DESARROLLO
path: ../verticales/construccion/
- nombre: vidrio-templado
estado: 0%
path: ../verticales/vidrio-templado/
modulos: 18
specs_aplicables: 26
specs_implementadas: 0
tablas_heredadas: 124
tablas_especificas: 33
- nombre: mecanicas-diesel
estado: 0%
codigo: MMD
estado: 20%
fase: DDL_IMPLEMENTADO
path: ../verticales/mecanicas-diesel/
modulos: 6
specs_aplicables: 25
specs_implementadas: 0
tablas_heredadas: 97
tablas_especificas: 30
- nombre: vidrio-templado
codigo: VT
estado: 15%
fase: PLANIFICACION_COMPLETA
path: ../verticales/vidrio-templado/
modulos: 8
specs_aplicables: 25
specs_implementadas: 0
tablas_heredadas: 97
tablas_especificas: 25
- nombre: retail
estado: 0%
codigo: RT
estado: 15%
fase: PLANIFICACION_COMPLETA
path: ../verticales/retail/
modulos: 10
specs_aplicables: 24
specs_implementadas: 0
tablas_heredadas: 102
tablas_especificas: 30
- nombre: clinicas
estado: 0%
codigo: CL
estado: 15%
fase: PLANIFICACION_COMPLETA
path: ../verticales/clinicas/
modulos: 12
specs_aplicables: 24
specs_implementadas: 0
tablas_heredadas: 100
tablas_especificas: 35
# ============================================================================
# MAPEO DE SPECS A VERTICALES (Referencia)
# ============================================================================
mapeo_specs_verticales:
documento_completo: docs/04-modelado/MAPEO-SPECS-VERTICALES.md
fecha_actualizacion: 2025-12-08
resumen:
total_specs: 30
por_vertical:
construccion: {aplicables: 26, obligatorias: 22, opcionales: 4}
mecanicas_diesel: {aplicables: 25, obligatorias: 23, opcionales: 2}
vidrio_templado: {aplicables: 25, obligatorias: 22, opcionales: 3}
retail: {aplicables: 24, obligatorias: 21, opcionales: 3}
clinicas: {aplicables: 24, obligatorias: 20, opcionales: 4}
documentacion:
total_archivos: 630

View File

@ -0,0 +1,191 @@
# ERP Básico SaaS - Solución Integral Austera
## Descripción
Sistema ERP completo pero austero, diseñado para PyMEs que necesitan funcionalidad integral sin la complejidad ni el costo de soluciones enterprise.
## Target de Mercado
- PyMEs con 5-50 empleados
- Negocios que necesitan más que un POS
- Empresas que buscan digitalización económica
- Comercios con operaciones de compra-venta
- Pequeñas manufacturas
## Precio
**~300-500 MXN/mes** (según módulos activos)
## Plan Base (300 MXN/mes)
| Módulo | Incluido | Descripción |
|--------|----------|-------------|
| Autenticación | Obligatorio | Login, 2FA, roles básicos |
| Usuarios | Obligatorio | Hasta 5 usuarios |
| Multi-tenant | Obligatorio | Aislamiento por empresa |
| Catálogos | Incluido | Productos, categorías, unidades |
| Inventario | Incluido | Stock, movimientos, alertas |
| Ventas | Incluido | Cotizaciones, pedidos, facturas |
| Compras | Incluido | Órdenes de compra, proveedores |
| Clientes | Incluido | CRM básico, contactos |
| Reportes | Incluido | Dashboard, reportes esenciales |
## Módulos Opcionales
| Módulo | Precio | Descripción |
|--------|--------|-------------|
| Contabilidad | +150 MXN/mes | Pólizas, balances, estados financieros |
| RRHH | +100 MXN/mes | Empleados, nómina básica, asistencia |
| Facturación CFDI | +100 MXN/mes | Timbrado SAT México |
| Usuarios extra | +50 MXN/usuario | Más de 5 usuarios |
| WhatsApp Bot | Por consumo | Consultas y notificaciones |
| Soporte Premium | +200 MXN/mes | Atención prioritaria |
## Stack Tecnológico
- **Backend:** Node.js + Express/NestJS + TypeScript
- **Frontend:** React 18 + Vite + Tailwind CSS
- **Database:** PostgreSQL 15+ con RLS
- **Cache:** Redis (compartido)
- **Auth:** JWT + bcrypt
## Arquitectura
```
erp-basico/
├── backend/
│ ├── src/
│ │ ├── modules/
│ │ │ ├── auth/ # Autenticación
│ │ │ ├── users/ # Gestión usuarios
│ │ │ ├── companies/ # Multi-tenant
│ │ │ ├── catalogs/ # Catálogos maestros
│ │ │ ├── inventory/ # Inventario
│ │ │ ├── sales/ # Ventas
│ │ │ ├── purchases/ # Compras
│ │ │ ├── partners/ # Clientes/Proveedores
│ │ │ └── reports/ # Reportes
│ │ └── shared/
│ │ ├── guards/
│ │ ├── decorators/
│ │ └── utils/
├── frontend/
│ ├── src/
│ │ ├── features/ # Por módulo
│ │ ├── shared/ # Componentes base
│ │ └── app/ # Layout, routing
├── database/
│ └── ddl/
│ ├── 00-extensions.sql
│ ├── 01-schemas.sql
│ ├── 02-core-tables.sql
│ └── 03-business-tables.sql
└── orchestration/
```
## Base de Datos (~40 tablas)
### Schema: `auth`
- users, roles, permissions, sessions, tokens
### Schema: `core`
- companies, settings, sequences, audit_logs
### Schema: `catalog`
- products, categories, units, taxes, payment_methods
### Schema: `inventory`
- warehouses, stock_moves, stock_quants, adjustments
### Schema: `sales`
- quotations, sale_orders, invoices, payments
### Schema: `purchases`
- purchase_orders, supplier_invoices, receipts
### Schema: `partners`
- partners, contacts, addresses
### Schema: `reports`
- report_configs, saved_reports
## Diferenciación vs POS Micro
| Aspecto | POS Micro | ERP Básico |
|---------|-----------|------------|
| Precio | 100 MXN | 300-500 MXN |
| Tablas BD | ~10 | ~40 |
| Módulos | 4 | 10+ |
| Usuarios | 1 | 5+ |
| Compras | No | Sí |
| Inventario | Básico | Completo |
| Reportes | Mínimos | Dashboard |
| Facturación | No | Opcional |
| Contabilidad | No | Opcional |
## Herencia del Core
Este producto hereda **directamente** de `erp-core`:
| Componente | % Herencia | Adaptación |
|------------|------------|------------|
| Auth | 100% | Ninguna |
| Users | 100% | Ninguna |
| Multi-tenant | 100% | Ninguna |
| Catálogos | 80% | Simplificado |
| Inventario | 70% | Sin lotes/series |
| Ventas | 70% | Sin workflows complejos |
| Compras | 70% | Sin aprobaciones |
| Partners | 90% | Ninguna |
| Reportes | 50% | Subset de reportes |
## Feature Flags
```yaml
# Configuración por tenant
features:
accounting: false # +150 MXN
hr: false # +100 MXN
cfdi: false # +100 MXN
whatsapp_bot: false # Por consumo
advanced_reports: false
multi_warehouse: false
serial_numbers: false
lot_tracking: false
```
## Limitaciones (Por diseño)
- Máximo 10,000 productos
- Máximo 5 usuarios en plan base
- Sin multi-sucursal en plan base
- Sin contabilidad avanzada (solo opcional)
- Sin manufactura
- Sin proyectos
- Sin e-commerce integrado
## Roadmap
### MVP (v1.0)
- [x] Auth completo (heredado de core)
- [ ] Catálogos básicos
- [ ] Inventario simple
- [ ] Ventas (cotización → pedido → factura)
- [ ] Compras básicas
- [ ] Dashboard inicial
### v1.1
- [ ] Módulo contabilidad (opcional)
- [ ] CFDI México (opcional)
- [ ] Reportes adicionales
### v1.2
- [ ] RRHH básico (opcional)
- [ ] Multi-almacén
- [ ] Integraciones bancarias
---
*Producto: ERP Básico SaaS v1.0*
*Precio Target: 300-500 MXN/mes*
*Mercado: PyMEs México*

View File

@ -0,0 +1,238 @@
# Contexto del Proyecto: ERP Básico SaaS
## Identificación
| Campo | Valor |
|-------|-------|
| **Nombre** | ERP Básico SaaS |
| **Tipo** | Producto SaaS |
| **Nivel** | 2B.2 (Producto dentro de Suite) |
| **Suite Padre** | erp-suite |
| **Ruta Base** | `projects/erp-suite/apps/products/erp-basico/` |
| **Estado** | En Planificación |
## Descripción
ERP completo pero austero para PyMEs. Hereda directamente de erp-core con configuración simplificada y precios accesibles.
## Target de Mercado
- PyMEs con 5-50 empleados
- Comercios con operaciones compra-venta
- Pequeñas manufacturas
- Distribuidores
- Empresas en proceso de digitalización
## Propuesta de Valor
1. **ERP completo** - No solo POS, gestión integral
2. **Precio accesible** - 300-500 MXN/mes vs 2,000+ de SAP/Odoo
3. **Sin complejidad** - Configuración mínima
4. **Modular** - Paga solo lo que usas
5. **Mexicanizado** - CFDI, bancos mexicanos
## Stack Tecnológico
```yaml
backend:
runtime: Node.js 20+
framework: NestJS (heredado de core)
language: TypeScript 5.3+
orm: TypeORM
validation: class-validator
frontend:
framework: React 18
bundler: Vite
styling: Tailwind CSS
state: Zustand
forms: React Hook Form
database:
engine: PostgreSQL 15+
multi_tenant: true (RLS)
schemas: 8
tables: ~40
cache:
engine: Redis
usage: Sessions, rate-limiting
```
## Variables del Proyecto
```yaml
# Identificadores
PROJECT_NAME: erp-basico
PROJECT_CODE: ERPB
SUITE: erp-suite
# Database
DB_NAME: erp_suite_db # Compartida
SCHEMAS:
- auth
- core
- catalog
- inventory
- sales
- purchases
- partners
- reports
# Paths
BACKEND_ROOT: apps/products/erp-basico/backend
FRONTEND_ROOT: apps/products/erp-basico/frontend
DATABASE_ROOT: apps/products/erp-basico/database
# Business
BASE_PRICE_MXN: 300
MAX_USERS_BASE: 5
MAX_PRODUCTS: 10000
```
## Herencia del Core
### Módulos Heredados (100%)
| Módulo Core | Uso en ERP Básico |
|-------------|-------------------|
| MGN-001 Auth | Completo |
| MGN-002 Users | Completo |
| MGN-003 Roles | Simplificado (3 roles) |
| MGN-004 Tenants | Completo |
| MGN-005 Catalogs | 80% (sin variantes) |
| MGN-008 Notifications | Simplificado |
### Módulos Adaptados
| Módulo Core | Adaptación |
|-------------|------------|
| MGN-007 Audit | Solo logs críticos |
| MGN-009 Reports | Subset de reportes |
| Inventory | Sin lotes/series |
| Sales | Sin workflows aprobación |
| Purchases | Sin aprobaciones multi-nivel |
### Módulos NO Incluidos
| Módulo Core | Razón |
|-------------|-------|
| MGN-010 Financial | Opcional (+150 MXN) |
| Projects | Complejidad innecesaria |
| Manufacturing | Fuera de scope |
| Advanced HR | Opcional (+100 MXN) |
## Módulos del Producto
### Obligatorios (Plan Base)
| Módulo | Tablas | Endpoints | Componentes |
|--------|--------|-----------|-------------|
| auth | 5 | 8 | 4 |
| users | 2 | 6 | 3 |
| companies | 3 | 5 | 2 |
| catalogs | 5 | 12 | 6 |
| inventory | 4 | 10 | 5 |
| sales | 4 | 12 | 6 |
| purchases | 3 | 8 | 4 |
| partners | 3 | 8 | 4 |
| reports | 2 | 6 | 3 |
### Opcionales (Feature Flags)
| Módulo | Precio | Tablas Extra |
|--------|--------|--------------|
| accounting | +150 MXN | 8 |
| hr | +100 MXN | 6 |
| cfdi | +100 MXN | 3 |
## Feature Flags
```typescript
interface TenantFeatures {
// Plan base
base_erp: true;
max_users: 5;
max_products: 10000;
// Opcionales
accounting: boolean; // +150 MXN
hr: boolean; // +100 MXN
cfdi: boolean; // +100 MXN
extra_users: number; // +50 MXN c/u
multi_warehouse: boolean; // +100 MXN
whatsapp_bot: boolean; // Por consumo
advanced_reports: boolean;// +50 MXN
}
```
## Diferenciación
### vs POS Micro
| Aspecto | POS Micro | ERP Básico |
|---------|-----------|------------|
| Complejidad | Mínima | Media |
| Módulos | 4 | 10+ |
| Usuarios | 1 | 5+ |
| Compras | No | Sí |
| Multi-almacén | No | Opcional |
| Contabilidad | No | Opcional |
| Precio | 100 MXN | 300+ MXN |
### vs ERP Enterprise (Verticales)
| Aspecto | ERP Básico | Verticales |
|---------|------------|------------|
| Industria | General | Especializado |
| Complejidad | Media | Alta |
| Customización | Baja | Alta |
| Workflows | Simples | Complejos |
| Precio | 300-500 MXN | 1,000+ MXN |
## Métricas de Éxito
| Métrica | Target |
|---------|--------|
| Tiempo de onboarding | < 30 minutos |
| Usuarios activos diarios | > 60% |
| NPS | > 40 |
| Churn mensual | < 3% |
| Tickets soporte/usuario | < 0.5/mes |
## Roadmap
### MVP (v1.0)
- [ ] Herencia completa de auth/users/tenants
- [ ] Catálogos (productos, categorías, unidades)
- [ ] Inventario básico (stock, movimientos)
- [ ] Ventas (cotización → pedido → factura)
- [ ] Compras básicas
- [ ] Dashboard inicial
- [ ] Billing/suscripciones
### v1.1
- [ ] Módulo contabilidad (opcional)
- [ ] CFDI México (opcional)
- [ ] Reportes financieros
### v1.2
- [ ] RRHH básico (opcional)
- [ ] Multi-almacén (opcional)
- [ ] Integraciones bancarias México
### v2.0
- [ ] App móvil
- [ ] Integraciones marketplace
- [ ] IA para predicciones
## Documentos Relacionados
- `../README.md` - Descripción general
- `../../erp-core/` - Core heredado
- `../../erp-core/docs/` - Documentación detallada de módulos
- `../../../orchestration/` - Orquestación suite level
---
*Última actualización: 2025-12-08*

View File

@ -0,0 +1,139 @@
# POS Micro - Punto de Venta Ultra Básico
## Descripción
Sistema de punto de venta minimalista diseñado para el mercado informal mexicano. Enfocado en simplicidad extrema, bajo costo y funcionalidad offline.
## Target de Mercado
- Puestos de calle y ambulantes
- Tiendas de abarrotes y misceláneas
- Puestos de comida (tacos, tortas, etc.)
- Pequeños locales comerciales
- Vendedores independientes
## Precio
**~100 MXN/mes** + consumo de IA (opcional)
## Características
### Incluidas en Plan Base (100 MXN/mes)
| Característica | Descripción |
|----------------|-------------|
| Punto de Venta | Registrar ventas, calcular cambio |
| Catálogo | Lista de productos con precios |
| Inventario Básico | Control de stock simple |
| Corte de Caja | Resumen diario |
| Reportes | Ventas día/semana/mes |
| PWA Offline | Funciona sin internet |
| 1 Usuario | Operador principal |
### Opcionales (Por Consumo)
| Característica | Costo |
|----------------|-------|
| WhatsApp Bot | ~0.02 USD por consulta |
| Usuario adicional | +30 MXN/mes |
| Soporte prioritario | +50 MXN/mes |
## Stack Tecnológico
- **Backend:** Node.js + Express + TypeScript
- **Frontend:** React + PWA + Tailwind CSS
- **Database:** PostgreSQL (compartida multi-tenant)
- **WhatsApp:** WhatsApp Business API
- **IA:** Claude API (para bot)
## Arquitectura
```
pos-micro/
├── backend/ # API mínima
├── frontend/ # SPA React
├── pwa/ # Service Worker + Offline
├── database/ # ~10 tablas
├── whatsapp/ # Integración WA Business
├── docs/ # Documentación
└── orchestration/ # Sistema NEXUS
```
## Base de Datos (~10 tablas)
1. `tenants` - Empresas/negocios
2. `users` - Usuarios del sistema
3. `products` - Catálogo de productos
4. `sales` - Ventas registradas
5. `sale_items` - Detalle de ventas
6. `inventory_movements` - Movimientos de inventario
7. `daily_closures` - Cortes de caja
8. `whatsapp_sessions` - Sesiones WA
9. `ai_usage` - Consumo de tokens IA
10. `subscriptions` - Suscripciones y pagos
## Flujo de Usuario
### Registro
1. Usuario accede a landing page
2. Ingresa número de WhatsApp
3. Recibe código de verificación
4. Configura nombre del negocio
5. Agrega primeros productos
6. Listo para vender
### Venta Típica
1. Abrir PWA (funciona offline)
2. Seleccionar productos
3. Ver total automático
4. Registrar pago (efectivo/tarjeta)
5. Calcular cambio
6. Venta registrada
### Consulta por WhatsApp
```
Usuario: "ventas de hoy"
Bot: "Ventas hoy: $1,250 MXN (15 tickets)
Producto más vendido: Coca Cola 600ml (23 unidades)"
Usuario: "stock de sabritas"
Bot: "Sabritas Original: 12 unidades
Sabritas Adobadas: 8 unidades
Sabritas Limón: 15 unidades"
```
## Principios de Diseño
1. **Simplicidad extrema** - Máximo 3 clicks para cualquier acción
2. **Mobile-first** - Diseñado para celulares
3. **Offline-first** - Funciona sin internet
4. **Bajo costo** - Infraestructura mínima
5. **Sin fricción** - Onboarding en 5 minutos
## Limitaciones (Por diseño)
- Máximo 500 productos
- Máximo 1,000 ventas/mes en plan base
- Sin facturación electrónica (CFDI)
- Sin contabilidad
- Sin multi-sucursal
- Sin CRM avanzado
## Herencia del Core
Este producto hereda de `erp-core`:
- Sistema de autenticación básico
- Multi-tenancy (RLS)
- Estructura de proyectos
NO hereda (por simplicidad):
- Módulos financieros
- RRHH
- CRM completo
- Reportes avanzados
---
*Producto: POS Micro v1.0*
*Precio Target: 100 MXN/mes*
*Mercado: Informal mexicano*

View File

@ -0,0 +1,164 @@
# Contexto del Proyecto: POS Micro
## Identificación
| Campo | Valor |
|-------|-------|
| **Nombre** | POS Micro |
| **Tipo** | Producto SaaS |
| **Nivel** | 2B.2 (Producto dentro de Suite) |
| **Suite Padre** | erp-suite |
| **Ruta Base** | `projects/erp-suite/apps/products/pos-micro/` |
| **Estado** | En Planificación |
## Descripción
Sistema de punto de venta ultra-minimalista diseñado para el mercado informal mexicano. Precio target: **100 MXN/mes**.
## Target de Mercado
- Puestos ambulantes
- Tiendas de abarrotes
- Misceláneas
- Puestos de comida
- Pequeños comercios
## Propuesta de Valor
1. **Precio accesible** - 100 MXN/mes (vs 500+ de competidores)
2. **Simplicidad** - Solo lo esencial
3. **Offline** - Funciona sin internet
4. **WhatsApp** - Consultas por chat
5. **Sin fricción** - Registro en 5 minutos
## Stack Tecnológico
```yaml
backend:
runtime: Node.js 20+
framework: Express
language: TypeScript
orm: TypeORM (simplificado)
frontend:
framework: React 18
bundler: Vite
styling: Tailwind CSS
pwa: Workbox
database:
engine: PostgreSQL 15+
multi_tenant: true (RLS)
max_tables: 10
integrations:
whatsapp: WhatsApp Business API
ai: Claude API (opcional)
payments: Stripe/Conekta
```
## Variables del Proyecto
```yaml
# Identificadores
PROJECT_NAME: pos-micro
PROJECT_CODE: POS
SUITE: erp-suite
# Database
DB_SCHEMA: pos_micro
DB_NAME: erp_suite_db # Compartida
MAX_TABLES: 10
# Paths
BACKEND_ROOT: apps/products/pos-micro/backend
FRONTEND_ROOT: apps/products/pos-micro/frontend
PWA_ROOT: apps/products/pos-micro/pwa
DATABASE_ROOT: apps/products/pos-micro/database
# Business
PRICE_MXN: 100
PRICE_USD: 6
MAX_PRODUCTS: 500
MAX_SALES_MONTH: 1000
```
## Herencia del Core
### SÍ Hereda
| Componente | Origen | Adaptación |
|------------|--------|------------|
| Auth básico | erp-core/auth | Simplificado (solo email/WA) |
| Multi-tenant | erp-core/tenant | RLS básico |
| API patterns | erp-core/shared | Endpoints mínimos |
### NO Hereda (Por Diseño)
| Componente | Razón |
|------------|-------|
| Contabilidad | Demasiado complejo |
| RRHH | No aplica |
| CRM | Simplificar |
| Compras | No necesario |
| Reportes avanzados | Overkill |
## Módulos del Producto
| Módulo | Prioridad | Tablas | Endpoints |
|--------|-----------|--------|-----------|
| auth | P0 | 2 | 4 |
| products | P0 | 1 | 5 |
| sales | P0 | 2 | 4 |
| inventory | P0 | 1 | 3 |
| reports | P1 | 1 | 3 |
| whatsapp | P1 | 2 | 2 |
| billing | P1 | 1 | 2 |
## Restricciones de Diseño
1. **Máximo 10 tablas** - Simplicidad de BD
2. **Máximo 20 endpoints** - API mínima
3. **Máximo 10 pantallas** - UI simple
4. **Offline-first** - Service Worker obligatorio
5. **Mobile-first** - Diseño responsivo primero móvil
6. **3-click rule** - Cualquier acción en máximo 3 clicks
## Métricas de Éxito
| Métrica | Target |
|---------|--------|
| Tiempo de onboarding | < 5 minutos |
| Tiempo carga PWA | < 2 segundos |
| Funcionalidad offline | 100% ventas |
| Costo infraestructura/usuario | < $1 USD/mes |
| Churn mensual | < 5% |
## Roadmap
### MVP (v1.0)
- [ ] Auth por WhatsApp
- [ ] CRUD productos
- [ ] Registro de ventas
- [ ] Corte de caja
- [ ] PWA offline
### v1.1
- [ ] WhatsApp Bot básico
- [ ] Reportes por WhatsApp
- [ ] Notificaciones stock bajo
### v1.2
- [ ] Dashboard web simple
- [ ] Exportar datos CSV
- [ ] Backup automático
## Documentos Relacionados
- `../README.md` - Descripción general
- `../../erp-core/orchestration/` - Core heredado
- `../../../orchestration/` - Suite level
---
*Última actualización: 2025-12-08*

View File

@ -0,0 +1,198 @@
# SaaS Layer - ERP Suite
## Descripción
Capa de servicios SaaS que gestiona multi-tenancy, billing, suscripciones y portal de clientes para todos los productos del ERP Suite.
## Componentes
```
saas/
├── billing/ # Facturación y cobros
├── portal/ # Portal de clientes
├── admin/ # Administración multi-tenant
├── onboarding/ # Registro y configuración inicial
├── docs/ # Documentación
└── orchestration/ # Sistema NEXUS
```
## Billing
Gestión de suscripciones y cobros.
### Funcionalidades
- Planes de suscripción (POS Micro, ERP Básico, Verticales)
- Cobro recurrente (mensual/anual)
- Integración con Stripe/Conekta
- Facturación automática (CFDI México)
- Gestión de módulos opcionales
### Planes
| Plan | Precio Base | Productos |
|------|-------------|-----------|
| POS Micro | 100 MXN/mes | pos-micro |
| ERP Básico | 300 MXN/mes | erp-basico |
| ERP Pro | 500 MXN/mes | erp-basico + módulos |
| Vertical | 1,000+ MXN/mes | erp-core + vertical |
### Módulos Opcionales
| Módulo | Precio | Disponible en |
|--------|--------|---------------|
| Contabilidad | +150 MXN/mes | ERP Básico, Verticales |
| RRHH | +100 MXN/mes | ERP Básico, Verticales |
| CFDI | +100 MXN/mes | Todos |
| WhatsApp Bot | Por consumo | Todos |
| Usuario extra | +50 MXN/mes | Todos |
## Portal
Portal self-service para clientes.
### Funcionalidades
- Dashboard de cuenta
- Gestión de suscripción
- Historial de facturas
- Cambio de plan
- Soporte/tickets
- Configuración de módulos
## Admin
Panel de administración para operadores.
### Funcionalidades
- Gestión de tenants
- Métricas de uso
- Facturación manual
- Soporte nivel 1
- Configuración global
- Feature flags por tenant
## Onboarding
Flujo de registro y configuración inicial.
### Flujo
1. **Registro** - Email o WhatsApp
2. **Selección de plan** - POS Micro, ERP Básico, etc.
3. **Datos de empresa** - RFC, dirección, giro
4. **Configuración inicial** - Productos, usuarios
5. **Pago** - Tarjeta o transferencia
6. **Activación** - Acceso inmediato
## Stack Tecnológico
```yaml
backend:
runtime: Node.js 20+
framework: NestJS
language: TypeScript
payments: Stripe + Conekta
invoicing: PAC CFDI
frontend:
framework: React 18
bundler: Vite
styling: Tailwind CSS
database:
engine: PostgreSQL 15+
schema: saas
tables: ~15
```
## Base de Datos
### Schema: `saas`
```sql
-- Gestión de tenants y suscripciones
saas.tenants -- Empresas/clientes
saas.subscriptions -- Suscripciones activas
saas.plans -- Catálogo de planes
saas.plan_features -- Features por plan
saas.invoices -- Facturas emitidas
saas.payments -- Pagos recibidos
saas.payment_methods -- Métodos de pago guardados
saas.usage_tracking -- Tracking de consumo
saas.support_tickets -- Tickets de soporte
saas.onboarding_sessions -- Sesiones de registro
```
## Integración con Productos
```
┌─────────────────────────────────────────────────────────────┐
│ SAAS LAYER │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ billing │ │ portal │ │ admin │ │onboarding│ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │ │
│ └───────────┴───────────┴───────────┘ │
│ │ │
│ API Gateway │
│ │ │
└─────────────────────────┼───────────────────────────────────┘
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ POS Micro│ │ERP Básico│ │Verticales│
└──────────┘ └──────────┘ └──────────┘
```
## Variables de Entorno
```env
# Payments
STRIPE_SECRET_KEY=sk_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
CONEKTA_API_KEY=key_xxx
# CFDI
PAC_RFC=XXX
PAC_API_KEY=xxx
PAC_ENVIRONMENT=sandbox|production
# Database
DATABASE_URL=postgresql://...
SAAS_SCHEMA=saas
# General
ONBOARDING_URL=https://registro.erp-suite.com
PORTAL_URL=https://portal.erp-suite.com
```
## Roadmap
### MVP (v1.0)
- [ ] Modelo de datos billing
- [ ] Integración Stripe básica
- [ ] Portal mínimo (ver facturas)
- [ ] Onboarding POS Micro
- [ ] Admin básico
### v1.1
- [ ] Integración Conekta
- [ ] CFDI automático
- [ ] Onboarding ERP Básico
- [ ] Métricas de uso
### v1.2
- [ ] Portal completo
- [ ] Cambio de plan self-service
- [ ] Soporte integrado
- [ ] Referidos
---
*SaaS Layer v1.0*
*ERP Suite*

View File

@ -0,0 +1,122 @@
# Contexto del Proyecto: SaaS Layer
## Identificación
| Campo | Valor |
|-------|-------|
| **Nombre** | SaaS Layer |
| **Tipo** | Infraestructura |
| **Nivel** | 2B.1 (Core de Suite) |
| **Suite** | erp-suite |
| **Ruta Base** | `projects/erp-suite/apps/saas/` |
| **Estado** | En Planificación |
## Descripción
Capa de servicios compartidos para gestión de multi-tenancy, billing, suscripciones y portal de clientes.
## Responsabilidades
1. **Billing** - Cobros, suscripciones, facturación
2. **Portal** - Self-service para clientes
3. **Admin** - Gestión de tenants
4. **Onboarding** - Registro de nuevos clientes
## Stack Tecnológico
```yaml
backend:
runtime: Node.js 20+
framework: NestJS
language: TypeScript 5.3+
frontend:
framework: React 18
bundler: Vite
styling: Tailwind CSS
database:
engine: PostgreSQL 15+
schema: saas
integrations:
payments: Stripe, Conekta
invoicing: PAC CFDI (México)
notifications: Email, WhatsApp
```
## Variables del Proyecto
```yaml
PROJECT_NAME: saas-layer
PROJECT_CODE: SAAS
SUITE: erp-suite
# Paths
BILLING_ROOT: apps/saas/billing
PORTAL_ROOT: apps/saas/portal
ADMIN_ROOT: apps/saas/admin
ONBOARDING_ROOT: apps/saas/onboarding
# Database
DB_SCHEMA: saas
MAX_TABLES: 15
```
## Módulos
| Módulo | Descripción | Prioridad |
|--------|-------------|-----------|
| billing | Suscripciones y cobros | P0 |
| portal | Portal de clientes | P1 |
| admin | Panel de administración | P1 |
| onboarding | Registro de clientes | P0 |
## Planes de Suscripción
| ID | Plan | Precio | Target |
|----|------|--------|--------|
| pos-micro | POS Micro | 100 MXN/mes | Mercado informal |
| erp-basic | ERP Básico | 300 MXN/mes | PyMEs |
| erp-pro | ERP Pro | 500 MXN/mes | PyMEs+ |
| vertical-x | Vertical | 1,000+ MXN/mes | Industrias específicas |
## Dependencias
### Productos que dependen de SaaS Layer
- `products/pos-micro` - Billing, onboarding
- `products/erp-basico` - Billing, portal, onboarding
- `verticales/*` - Billing, portal, admin
### Servicios externos
- Stripe - Pagos internacionales
- Conekta - Pagos México
- PAC CFDI - Facturación electrónica
- SendGrid - Email transaccional
- WhatsApp Business API - Notificaciones
## Roadmap
### Sprint 1: Billing MVP
- [ ] Modelo de datos
- [ ] Integración Stripe básica
- [ ] Webhook de pagos
- [ ] API de suscripciones
### Sprint 2: Onboarding
- [ ] Flujo de registro
- [ ] Selección de plan
- [ ] Configuración inicial
- [ ] Activación automática
### Sprint 3: Portal
- [ ] Dashboard cliente
- [ ] Ver facturas
- [ ] Cambiar plan
- [ ] Soporte básico
---
*Última actualización: 2025-12-08*

View File

@ -0,0 +1,213 @@
# Herencia de Base de Datos - ERP Core -> Clínicas
**Fecha:** 2025-12-08
**Versión:** 1.0
**Vertical:** Clínicas
**Nivel:** 2B.2
---
## RESUMEN
La vertical de Clínicas hereda los schemas base del ERP Core y extiende con schemas específicos del dominio de gestión médica y expediente clínico.
**Ubicación DDL Core:** `apps/erp-core/database/ddl/`
---
## ARQUITECTURA DE HERENCIA
```
┌─────────────────────────────────────────────────────────────────┐
│ ERP CORE (Base) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ auth │ │ core │ │financial│ │inventory│ │ hr │ │
│ │ 26 tbl │ │ 12 tbl │ │ 15 tbl │ │ 15 tbl │ │ 6 tbl │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ sales │ │analytics│ │ system │ │ crm │ │
│ │ 6 tbl │ │ 5 tbl │ │ 10 tbl │ │ 5 tbl │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ TOTAL: ~100 tablas heredadas │
└─────────────────────────────────────────────────────────────────┘
│ HEREDA
┌─────────────────────────────────────────────────────────────────┐
│ CLÍNICAS (Extensiones) │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ medical │ │ appointments │ │ patients │ │
│ │ (expediente) │ │ (citas) │ │ (pacientes) │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
│ EXTENSIONES: ~35 tablas (planificadas) │
└─────────────────────────────────────────────────────────────────┘
```
---
## SCHEMAS HEREDADOS DEL CORE
| Schema | Tablas | Uso en Clínicas |
|--------|--------|-----------------|
| `auth` | 26 | Autenticación, usuarios médicos |
| `core` | 12 | Partners (pacientes), catálogos |
| `financial` | 15 | Facturas de servicios médicos |
| `inventory` | 15 | Medicamentos, insumos |
| `hr` | 6 | Personal médico |
| `sales` | 6 | Servicios médicos |
| `crm` | 5 | Seguimiento de pacientes |
| `analytics` | 5 | Estadísticas médicas |
| `system` | 10 | Recordatorios, notificaciones |
**Total heredado:** ~100 tablas
---
## SCHEMAS ESPECÍFICOS DE CLÍNICAS (Planificados)
### 1. Schema `patients` (estimado 10+ tablas)
**Propósito:** Gestión de pacientes
```sql
-- Tablas principales planificadas:
patients.patients -- Pacientes (extiende core.partners)
patients.patient_contacts -- Contactos de emergencia
patients.insurance_policies -- Pólizas de seguro
patients.medical_history -- Antecedentes médicos
patients.allergies -- Alergias
patients.family_history -- Antecedentes familiares
```
### 2. Schema `medical` (estimado 15+ tablas)
**Propósito:** Expediente clínico electrónico
```sql
-- Tablas principales planificadas:
medical.consultations -- Consultas médicas
medical.diagnoses -- Diagnósticos (CIE-10)
medical.prescriptions -- Recetas médicas
medical.prescription_lines -- Medicamentos recetados
medical.vital_signs -- Signos vitales
medical.lab_results -- Resultados de laboratorio
medical.imaging_studies -- Estudios de imagen
medical.clinical_notes -- Notas clínicas
medical.treatments -- Tratamientos
```
### 3. Schema `appointments` (estimado 10+ tablas)
**Propósito:** Gestión de citas
```sql
-- Tablas principales planificadas:
appointments.doctors -- Médicos
appointments.specialties -- Especialidades
appointments.doctor_schedules -- Horarios de médicos
appointments.consulting_rooms -- Consultorios
appointments.appointments -- Citas
appointments.appointment_types -- Tipos de cita
appointments.reminders -- Recordatorios
```
---
## SPECS DEL CORE APLICABLES
**Documento detallado:** `orchestration/00-guidelines/HERENCIA-SPECS-CORE.md`
### SPECS Obligatorias
| Spec Core | Aplicación en Clínicas | SP | Estado |
|-----------|----------------------|----:|--------|
| SPEC-SISTEMA-SECUENCIAS | Foliado de expedientes y citas | 8 | PENDIENTE |
| SPEC-SEGURIDAD-API-KEYS-PERMISOS | Control de acceso a expedientes | 31 | PENDIENTE |
| SPEC-INTEGRACION-CALENDAR | Agenda de citas médicas | 8 | PENDIENTE |
| SPEC-RRHH-EVALUACIONES-SKILLS | Credenciales médicas | 26 | PENDIENTE |
| SPEC-MAIL-THREAD-TRACKING | Historial de comunicación | 13 | PENDIENTE |
| SPEC-WIZARD-TRANSIENT-MODEL | Wizards de receta y referencia | 8 | PENDIENTE |
| SPEC-FIRMA-ELECTRONICA-NOM151 | Firma de expedientes clínicos | 13 | PENDIENTE |
| SPEC-TWO-FACTOR-AUTHENTICATION | Seguridad de acceso | 13 | PENDIENTE |
| SPEC-OAUTH2-SOCIAL-LOGIN | Portal de pacientes | 8 | PENDIENTE |
### SPECS Opcionales
| Spec Core | Decisión | Razón |
|-----------|----------|-------|
| SPEC-VALORACION-INVENTARIO | EVALUAR | Solo si hay farmacia interna |
| SPEC-PRICING-RULES | EVALUAR | Para paquetes de servicios |
| SPEC-TAREAS-RECURRENTES | EVALUAR | Para citas periódicas |
### SPECS No Aplican
| Spec Core | Razón |
|-----------|-------|
| SPEC-PORTAL-PROVEEDORES | No hay compras complejas |
| SPEC-BLANKET-ORDERS | No aplica en servicios médicos |
| SPEC-INVENTARIOS-CICLICOS | Solo si hay farmacia grande |
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | No hay proyectos de este tipo |
### Cumplimiento Normativo
| Norma | Descripción | SPECS Relacionadas |
|-------|-------------|-------------------|
| NOM-024-SSA3-2012 | Expediente clínico electrónico | SPEC-SEGURIDAD, SPEC-MAIL-THREAD |
| LFPDPPP | Protección de datos personales | SPEC-SEGURIDAD, SPEC-2FA |
| NOM-004-SSA3-2012 | Expediente clínico | SPEC-FIRMA-ELECTRONICA |
---
## CUMPLIMIENTO NORMATIVO
Este sistema debe cumplir con:
| Norma | Descripción | Impacto |
|-------|-------------|---------|
| NOM-024-SSA3-2012 | Expediente clínico electrónico | Estructura de datos |
| LFPDPPP | Protección de datos personales | Seguridad y acceso |
| NOM-004-SSA3-2012 | Expediente clínico | Contenido mínimo |
---
## ORDEN DE EJECUCIÓN DDL (Futuro)
```bash
# PASO 1: Cargar ERP Core (base)
cd apps/erp-core/database
./scripts/reset-database.sh --force
# PASO 2: Cargar extensiones de Clínicas
cd apps/verticales/clinicas/database
psql $DATABASE_URL -f init/00-extensions.sql
psql $DATABASE_URL -f init/01-create-schemas.sql
psql $DATABASE_URL -f init/02-patients-tables.sql
psql $DATABASE_URL -f init/03-medical-tables.sql
psql $DATABASE_URL -f init/04-appointments-tables.sql
```
---
## MAPEO DE NOMENCLATURA
| Core | Clínicas |
|------|----------|
| `core.partners` | Pacientes base |
| `hr.employees` | Personal médico |
| `inventory.products` | Medicamentos, insumos |
| `sales.sale_orders` | Servicios médicos |
| `financial.invoices` | Facturas de consultas |
---
## REFERENCIAS
- ERP Core DDL: `apps/erp-core/database/ddl/`
- ERP Core README: `apps/erp-core/database/README.md`
- Directivas: `orchestration/directivas/`
- Inventarios: `orchestration/inventarios/`
---
**Documento de herencia oficial**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,199 @@
# Herencia de SPECS del Core - Clínicas
**Fecha:** 2025-12-08
**Versión:** 1.0
**Vertical:** Clínicas (CL)
**Nivel:** 2B.2
---
## Resumen
| Métrica | Valor |
|---------|-------|
| SPECS Aplicables | 24/30 |
| SPECS Obligatorias | 20 |
| SPECS Opcionales | 4 |
| SPECS No Aplican | 6 |
| Estado Implementación | 0% |
---
## SPECS Obligatorias (Deben Implementarse)
### P0 - Críticas
| SPEC | Gap Original | SP | Estado | Módulos Afectados |
|------|-------------|----:|--------|-------------------|
| SPEC-SISTEMA-SECUENCIAS | ir.sequence | 8 | PENDIENTE | CL-001, CL-002, CL-005 |
| SPEC-SEGURIDAD-API-KEYS-PERMISOS | API Keys + ACL | 31 | PENDIENTE | CL-001, CL-011 |
| SPEC-REPORTES-FINANCIEROS | Balance/P&L SAT | 13 | PENDIENTE | CL-008, CL-009 |
| SPEC-NOMINA-BASICA | hr_payroll | 21 | PENDIENTE | CL-001 |
| SPEC-GASTOS-EMPLEADOS | hr_expense | 13 | PENDIENTE | CL-001 |
| SPEC-SCHEDULER-REPORTES | ir.cron + mail | 8 | PENDIENTE | CL-009 |
| SPEC-INTEGRACION-CALENDAR | calendar integration | 8 | PENDIENTE | CL-003 |
### P1 - Complementarias
| SPEC | Gap Original | SP | Estado | Módulos Afectados |
|------|-------------|----:|--------|-------------------|
| SPEC-CONTABILIDAD-ANALITICA | Centros de costo | 21 | PENDIENTE | CL-008, CL-009 |
| SPEC-CONCILIACION-BANCARIA | Conciliación | 21 | PENDIENTE | CL-008 |
| SPEC-FIRMA-ELECTRONICA-NOM151 | e.firma | 13 | PENDIENTE | CL-011 |
| SPEC-TWO-FACTOR-AUTHENTICATION | 2FA | 13 | PENDIENTE | CL-001 |
| SPEC-TRAZABILIDAD-LOTES-SERIES | Lotes/Series | 13 | PENDIENTE | CL-007 |
| SPEC-OAUTH2-SOCIAL-LOGIN | OAuth2 | 8 | PENDIENTE | CL-002, CL-010 |
| SPEC-IMPUESTOS-AVANZADOS | IVA, ISR | 8 | PENDIENTE | CL-008 |
| SPEC-PLANTILLAS-CUENTAS | Plan contable | 8 | PENDIENTE | CL-008 |
| SPEC-TASAS-CAMBIO-AUTOMATICAS | Tipos cambio | 5 | PENDIENTE | CL-008 |
| SPEC-ALERTAS-PRESUPUESTO | Alertas | 8 | PENDIENTE | CL-008 |
| SPEC-RRHH-EVALUACIONES-SKILLS | Evaluaciones | 26 | PENDIENTE | CL-001 |
| SPEC-LOCALIZACION-PAISES | Localización | 13 | PENDIENTE | CL-001, CL-008 |
### Patrones Técnicos
| SPEC | Patrón | SP | Estado | Aplicación |
|------|--------|----:|--------|------------|
| SPEC-MAIL-THREAD-TRACKING | mail.thread | 13 | PENDIENTE | Expedientes, Citas, Comunicación |
| SPEC-WIZARD-TRANSIENT-MODEL | TransientModel | 8 | PENDIENTE | Wizards de receta, referencia |
---
## SPECS Opcionales
| SPEC | Descripción | SP | Decisión | Razón |
|------|-------------|----:|----------|-------|
| SPEC-VALORACION-INVENTARIO | FIFO/AVCO | 21 | EVALUAR | Solo para farmacia interna |
| SPEC-PRICING-RULES | Reglas precio | 8 | EVALUAR | Para paquetes de servicios |
| SPEC-TAREAS-RECURRENTES | Recurrencia | 13 | EVALUAR | Para citas periódicas |
| SPEC-PRESUPUESTOS-REVISIONES | Aprobación | 8 | EVALUAR | Para tratamientos largos |
---
## SPECS No Aplicables
| SPEC | Razón |
|------|-------|
| SPEC-PORTAL-PROVEEDORES | No hay compras complejas |
| SPEC-BLANKET-ORDERS | No aplica en servicios médicos |
| SPEC-INVENTARIOS-CICLICOS | Solo si hay farmacia grande |
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | No hay proyectos de este tipo |
| SPEC-CONSOLIDACION-FINANCIERA | Generalmente una clínica |
---
## Adaptaciones Requeridas
### Mapeo de Conceptos Core → Clínicas
| Concepto Core | Concepto Clínicas |
|---------------|-------------------|
| `core.partners` | Pacientes |
| `sales.sale_orders` | Consultas/Servicios |
| `inventory.products` | Medicamentos, servicios médicos |
| `hr.employees` | Personal médico |
| `calendar.events` | Citas médicas |
| `financial.invoices` | Facturas de consulta |
### Extensiones de Entidad
```sql
-- Pacientes (extiende partners)
patients.patients (
partner_id → core.partners,
numero_expediente VARCHAR UNIQUE,
fecha_nacimiento DATE,
sexo ENUM('M', 'F'),
tipo_sangre VARCHAR(5),
alergias TEXT[],
antecedentes JSONB,
seguro_medico_id → insurance_policies
)
-- Expediente clínico
medical.clinical_records (
id UUID,
patient_id → patients,
fecha TIMESTAMPTZ,
tipo ENUM('consulta', 'urgencia', 'hospitalizacion'),
motivo_consulta TEXT,
diagnostico TEXT,
tratamiento TEXT,
medico_id → hr.employees,
signos_vitales JSONB
)
-- Citas médicas
appointments.appointments (
id UUID,
patient_id → patients,
doctor_id → hr.employees,
specialty_id → specialties,
fecha_hora TIMESTAMPTZ,
duracion_minutos INTEGER,
estado ENUM('programada', 'confirmada', 'en_progreso', 'completada', 'cancelada'),
notas TEXT
)
-- Recetas médicas
medical.prescriptions (
id UUID,
clinical_record_id → clinical_records,
fecha TIMESTAMPTZ,
vigencia_dias INTEGER,
firma_electronica BYTEA,
productos JSONB
)
```
---
## Cumplimiento Normativo
Esta vertical debe cumplir con normas específicas:
| Norma | Descripción | SPECS Relacionadas |
|-------|-------------|-------------------|
| NOM-024-SSA3-2012 | Expediente clínico electrónico | SPEC-SEGURIDAD, SPEC-MAIL-THREAD |
| LFPDPPP | Protección de datos personales | SPEC-SEGURIDAD, SPEC-2FA |
| NOM-004-SSA3-2012 | Expediente clínico | SPEC-FIRMA-ELECTRONICA |
---
## Plan de Implementación
### Fase 1: Fundamentos (SP: 60)
1. SPEC-SISTEMA-SECUENCIAS
2. SPEC-SEGURIDAD-API-KEYS-PERMISOS
3. SPEC-TWO-FACTOR-AUTHENTICATION
4. SPEC-OAUTH2-SOCIAL-LOGIN
### Fase 2: Agenda y Comunicación (SP: 34)
5. SPEC-INTEGRACION-CALENDAR
6. SPEC-MAIL-THREAD-TRACKING
7. SPEC-WIZARD-TRANSIENT-MODEL
### Fase 3: Expediente y Cumplimiento (SP: 39)
8. SPEC-FIRMA-ELECTRONICA-NOM151
9. SPEC-RRHH-EVALUACIONES-SKILLS
### Fase 4: Financiero (SP: 65)
10. SPEC-REPORTES-FINANCIEROS
11. SPEC-CONTABILIDAD-ANALITICA
12. SPEC-CONCILIACION-BANCARIA
13. SPEC-IMPUESTOS-AVANZADOS
---
## Referencias
- Documento Core: `erp-core/docs/04-modelado/MAPEO-SPECS-VERTICALES.md`
- SPECS del Core: `erp-core/docs/04-modelado/especificaciones-tecnicas/transversal/`
- Herencia DB: `database/HERENCIA-ERP-CORE.md`
- Directivas: `orchestration/directivas/`
- Normatividad: NOM-024-SSA3-2012, LFPDPPP, NOM-004-SSA3-2012
---
**Documento de herencia de SPECS oficial**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,103 @@
# Inventarios - ERP Clínicas
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Nivel SIMCO:** 2B.2
---
## Descripción
Este directorio contiene los inventarios YAML que sirven como **Single Source of Truth (SSOT)** para el proyecto ERP Clínicas. Estos archivos son la referencia canónica para métricas, trazabilidad y componentes del sistema.
---
## Archivos de Inventario
| Archivo | Descripción | Estado |
|---------|-------------|--------|
| [MASTER_INVENTORY.yml](./MASTER_INVENTORY.yml) | Inventario maestro con métricas globales | Completo |
| [DATABASE_INVENTORY.yml](./DATABASE_INVENTORY.yml) | Inventario de objetos de base de datos | Planificado |
| [BACKEND_INVENTORY.yml](./BACKEND_INVENTORY.yml) | Inventario de componentes backend | Planificado |
| [FRONTEND_INVENTORY.yml](./FRONTEND_INVENTORY.yml) | Inventario de componentes frontend | Planificado |
| [TRACEABILITY_MATRIX.yml](./TRACEABILITY_MATRIX.yml) | Matriz de trazabilidad RF->ET->US | Completo |
| [DEPENDENCY_GRAPH.yml](./DEPENDENCY_GRAPH.yml) | Grafo de dependencias entre módulos | Completo |
---
## Herencia del Core
Este proyecto hereda del **ERP Core** (nivel 2B.1):
| Aspecto | Heredado | Específico |
|---------|----------|------------|
| **Tablas DB** | ~100 | Planificado |
| **Schemas** | 8+ | Planificado |
| **Specs** | 3 | - |
### Specs Heredadas
1. SPEC-RRHH-EVALUACIONES-SKILLS.md
2. SPEC-INTEGRACION-CALENDAR.md
3. SPEC-MAIL-THREAD-TRACKING.md
---
## Resumen Ejecutivo
### Métricas del Proyecto
| Métrica | Valor |
|---------|-------|
| **Módulos** | 5 (CL-001 a CL-005) |
| **Estado** | PLANIFICACION_COMPLETA |
| **Completitud** | 15% |
### Dominio del Negocio
- Expediente clínico electrónico
- Gestión de citas médicas
- Control de consultorios
- Facturación de servicios médicos
---
## Directivas Específicas
1. [DIRECTIVA-EXPEDIENTE-CLINICO.md](../directivas/DIRECTIVA-EXPEDIENTE-CLINICO.md)
2. [DIRECTIVA-GESTION-CITAS.md](../directivas/DIRECTIVA-GESTION-CITAS.md)
---
## Configuración de Puertos (Planificado)
| Servicio | Puerto |
|----------|--------|
| Backend API | 3500 |
| Frontend Web | 5179 |
| Patient Portal | 5180 |
---
## Cumplimiento Normativo
Este proyecto debe cumplir con:
- NOM-024-SSA3-2012 (Expediente clínico electrónico)
- Ley de Protección de Datos Personales en Posesión de Particulares
---
## Alineación con ERP Core
Estos inventarios siguen la misma estructura que:
- `/erp-core/orchestration/inventarios/` (proyecto padre)
### Referencias
- Suite Master: `orchestration/inventarios/SUITE_MASTER_INVENTORY.yml`
- Core: `apps/erp-core/orchestration/inventarios/`
- Status Global: `orchestration/inventarios/STATUS.yml`
---
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,157 @@
/**
* Fraccionamiento Controller
* API endpoints para gestión de fraccionamientos/obras
*
* @module Construction
* @prefix /api/v1/fraccionamientos
*/
import { Router, Request, Response, NextFunction } from 'express';
import {
FraccionamientoService,
CreateFraccionamientoDto,
UpdateFraccionamientoDto
} from '../services/fraccionamiento.service';
const router = Router();
const fraccionamientoService = new FraccionamientoService();
/**
* GET /api/v1/fraccionamientos
* Lista todos los fraccionamientos del tenant
*/
router.get('/', async (req: Request, res: Response, next: NextFunction) => {
try {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'X-Tenant-Id header required' });
}
const { proyectoId, estado } = req.query;
const fraccionamientos = await fraccionamientoService.findAll({
tenantId,
proyectoId: proyectoId as string,
estado: estado as any,
});
return res.json({
success: true,
data: fraccionamientos,
count: fraccionamientos.length,
});
} catch (error) {
next(error);
}
});
/**
* GET /api/v1/fraccionamientos/:id
* Obtiene un fraccionamiento por ID
*/
router.get('/:id', async (req: Request, res: Response, next: NextFunction) => {
try {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'X-Tenant-Id header required' });
}
const fraccionamiento = await fraccionamientoService.findById(req.params.id, tenantId);
if (!fraccionamiento) {
return res.status(404).json({ error: 'Fraccionamiento no encontrado' });
}
return res.json({ success: true, data: fraccionamiento });
} catch (error) {
next(error);
}
});
/**
* POST /api/v1/fraccionamientos
* Crea un nuevo fraccionamiento
*/
router.post('/', async (req: Request, res: Response, next: NextFunction) => {
try {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'X-Tenant-Id header required' });
}
const data: CreateFraccionamientoDto = {
...req.body,
tenantId,
createdById: (req as any).user?.id,
};
// Validate required fields
if (!data.codigo || !data.nombre || !data.proyectoId) {
return res.status(400).json({
error: 'codigo, nombre y proyectoId son requeridos'
});
}
// Check if codigo already exists
const existing = await fraccionamientoService.findByCodigo(data.codigo, tenantId);
if (existing) {
return res.status(409).json({ error: 'Ya existe un fraccionamiento con ese código' });
}
const fraccionamiento = await fraccionamientoService.create(data);
return res.status(201).json({ success: true, data: fraccionamiento });
} catch (error) {
next(error);
}
});
/**
* PATCH /api/v1/fraccionamientos/:id
* Actualiza un fraccionamiento
*/
router.patch('/:id', async (req: Request, res: Response, next: NextFunction) => {
try {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'X-Tenant-Id header required' });
}
const data: UpdateFraccionamientoDto = req.body;
const fraccionamiento = await fraccionamientoService.update(
req.params.id,
tenantId,
data
);
if (!fraccionamiento) {
return res.status(404).json({ error: 'Fraccionamiento no encontrado' });
}
return res.json({ success: true, data: fraccionamiento });
} catch (error) {
next(error);
}
});
/**
* DELETE /api/v1/fraccionamientos/:id
* Elimina un fraccionamiento
*/
router.delete('/:id', async (req: Request, res: Response, next: NextFunction) => {
try {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'X-Tenant-Id header required' });
}
const deleted = await fraccionamientoService.delete(req.params.id, tenantId);
if (!deleted) {
return res.status(404).json({ error: 'Fraccionamiento no encontrado' });
}
return res.json({ success: true, message: 'Fraccionamiento eliminado' });
} catch (error) {
next(error);
}
});
export default router;

View File

@ -0,0 +1,7 @@
/**
* Construction Controllers Index
* @module Construction
*/
export { default as proyectoController } from './proyecto.controller';
export { default as fraccionamientoController } from './fraccionamiento.controller';

View File

@ -0,0 +1,165 @@
/**
* Proyecto Controller
* API endpoints para gestión de proyectos
*
* @module Construction
* @prefix /api/v1/proyectos
*/
import { Router, Request, Response, NextFunction } from 'express';
import { ProyectoService, CreateProyectoDto, UpdateProyectoDto } from '../services/proyecto.service';
const router = Router();
const proyectoService = new ProyectoService();
/**
* GET /api/v1/proyectos
* Lista todos los proyectos del tenant
*/
router.get('/', async (req: Request, res: Response, next: NextFunction) => {
try {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'X-Tenant-Id header required' });
}
const { estadoProyecto, ciudad } = req.query;
const proyectos = await proyectoService.findAll({
tenantId,
estadoProyecto: estadoProyecto as any,
ciudad: ciudad as string,
});
return res.json({
success: true,
data: proyectos,
count: proyectos.length,
});
} catch (error) {
next(error);
}
});
/**
* GET /api/v1/proyectos/statistics
* Estadísticas de proyectos
*/
router.get('/statistics', async (req: Request, res: Response, next: NextFunction) => {
try {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'X-Tenant-Id header required' });
}
const stats = await proyectoService.getStatistics(tenantId);
return res.json({ success: true, data: stats });
} catch (error) {
next(error);
}
});
/**
* GET /api/v1/proyectos/:id
* Obtiene un proyecto por ID
*/
router.get('/:id', async (req: Request, res: Response, next: NextFunction) => {
try {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'X-Tenant-Id header required' });
}
const proyecto = await proyectoService.findById(req.params.id, tenantId);
if (!proyecto) {
return res.status(404).json({ error: 'Proyecto no encontrado' });
}
return res.json({ success: true, data: proyecto });
} catch (error) {
next(error);
}
});
/**
* POST /api/v1/proyectos
* Crea un nuevo proyecto
*/
router.post('/', async (req: Request, res: Response, next: NextFunction) => {
try {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'X-Tenant-Id header required' });
}
const data: CreateProyectoDto = {
...req.body,
tenantId,
createdById: (req as any).user?.id,
};
// Validate required fields
if (!data.codigo || !data.nombre) {
return res.status(400).json({ error: 'codigo y nombre son requeridos' });
}
// Check if codigo already exists
const existing = await proyectoService.findByCodigo(data.codigo, tenantId);
if (existing) {
return res.status(409).json({ error: 'Ya existe un proyecto con ese código' });
}
const proyecto = await proyectoService.create(data);
return res.status(201).json({ success: true, data: proyecto });
} catch (error) {
next(error);
}
});
/**
* PATCH /api/v1/proyectos/:id
* Actualiza un proyecto
*/
router.patch('/:id', async (req: Request, res: Response, next: NextFunction) => {
try {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'X-Tenant-Id header required' });
}
const data: UpdateProyectoDto = req.body;
const proyecto = await proyectoService.update(req.params.id, tenantId, data);
if (!proyecto) {
return res.status(404).json({ error: 'Proyecto no encontrado' });
}
return res.json({ success: true, data: proyecto });
} catch (error) {
next(error);
}
});
/**
* DELETE /api/v1/proyectos/:id
* Elimina un proyecto
*/
router.delete('/:id', async (req: Request, res: Response, next: NextFunction) => {
try {
const tenantId = req.headers['x-tenant-id'] as string;
if (!tenantId) {
return res.status(400).json({ error: 'X-Tenant-Id header required' });
}
const deleted = await proyectoService.delete(req.params.id, tenantId);
if (!deleted) {
return res.status(404).json({ error: 'Proyecto no encontrado' });
}
return res.json({ success: true, message: 'Proyecto eliminado' });
} catch (error) {
next(error);
}
});
export default router;

View File

@ -0,0 +1,117 @@
/**
* Fraccionamiento Service
* Servicio para gestión de fraccionamientos/obras
*
* @module Construction
*/
import { Repository, FindOptionsWhere } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import { Fraccionamiento, EstadoFraccionamiento } from '../entities/fraccionamiento.entity';
export interface CreateFraccionamientoDto {
tenantId: string;
proyectoId: string;
codigo: string;
nombre: string;
descripcion?: string;
direccion?: string;
ubicacionGeo?: string;
fechaInicio?: Date;
fechaFinEstimada?: Date;
createdById?: string;
}
export interface UpdateFraccionamientoDto {
nombre?: string;
descripcion?: string;
direccion?: string;
ubicacionGeo?: string;
fechaInicio?: Date;
fechaFinEstimada?: Date;
estado?: EstadoFraccionamiento;
}
export interface FraccionamientoFilters {
tenantId: string;
proyectoId?: string;
estado?: EstadoFraccionamiento;
}
export class FraccionamientoService {
private repository: Repository<Fraccionamiento>;
constructor() {
this.repository = AppDataSource.getRepository(Fraccionamiento);
}
async findAll(filters: FraccionamientoFilters): Promise<Fraccionamiento[]> {
const where: FindOptionsWhere<Fraccionamiento> = {
tenantId: filters.tenantId,
};
if (filters.proyectoId) {
where.proyectoId = filters.proyectoId;
}
if (filters.estado) {
where.estado = filters.estado;
}
return this.repository.find({
where,
relations: ['proyecto'],
order: { createdAt: 'DESC' },
});
}
async findById(id: string, tenantId: string): Promise<Fraccionamiento | null> {
return this.repository.findOne({
where: { id, tenantId },
relations: ['proyecto', 'createdBy'],
});
}
async findByCodigo(codigo: string, tenantId: string): Promise<Fraccionamiento | null> {
return this.repository.findOne({
where: { codigo, tenantId },
});
}
async findByProyecto(proyectoId: string, tenantId: string): Promise<Fraccionamiento[]> {
return this.repository.find({
where: { proyectoId, tenantId },
order: { codigo: 'ASC' },
});
}
async create(data: CreateFraccionamientoDto): Promise<Fraccionamiento> {
const fraccionamiento = this.repository.create(data);
return this.repository.save(fraccionamiento);
}
async update(
id: string,
tenantId: string,
data: UpdateFraccionamientoDto
): Promise<Fraccionamiento | null> {
const fraccionamiento = await this.findById(id, tenantId);
if (!fraccionamiento) {
return null;
}
Object.assign(fraccionamiento, data);
return this.repository.save(fraccionamiento);
}
async delete(id: string, tenantId: string): Promise<boolean> {
const result = await this.repository.delete({ id, tenantId });
return result.affected ? result.affected > 0 : false;
}
async countByProyecto(proyectoId: string, tenantId: string): Promise<number> {
return this.repository.count({
where: { proyectoId, tenantId },
});
}
}

View File

@ -0,0 +1,7 @@
/**
* Construction Services Index
* @module Construction
*/
export * from './proyecto.service';
export * from './fraccionamiento.service';

View File

@ -0,0 +1,117 @@
/**
* Proyecto Service
* Servicio para gestión de proyectos de construcción
*
* @module Construction
*/
import { Repository, FindOptionsWhere } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import { Proyecto, EstadoProyecto } from '../entities/proyecto.entity';
export interface CreateProyectoDto {
tenantId: string;
codigo: string;
nombre: string;
descripcion?: string;
direccion?: string;
ciudad?: string;
estado?: string;
fechaInicio?: Date;
fechaFinEstimada?: Date;
createdById?: string;
}
export interface UpdateProyectoDto {
nombre?: string;
descripcion?: string;
direccion?: string;
ciudad?: string;
estado?: string;
fechaInicio?: Date;
fechaFinEstimada?: Date;
estadoProyecto?: EstadoProyecto;
}
export interface ProyectoFilters {
tenantId: string;
estadoProyecto?: EstadoProyecto;
ciudad?: string;
}
export class ProyectoService {
private repository: Repository<Proyecto>;
constructor() {
this.repository = AppDataSource.getRepository(Proyecto);
}
async findAll(filters: ProyectoFilters): Promise<Proyecto[]> {
const where: FindOptionsWhere<Proyecto> = {
tenantId: filters.tenantId,
};
if (filters.estadoProyecto) {
where.estadoProyecto = filters.estadoProyecto;
}
if (filters.ciudad) {
where.ciudad = filters.ciudad;
}
return this.repository.find({
where,
relations: ['fraccionamientos'],
order: { createdAt: 'DESC' },
});
}
async findById(id: string, tenantId: string): Promise<Proyecto | null> {
return this.repository.findOne({
where: { id, tenantId },
relations: ['fraccionamientos', 'createdBy'],
});
}
async findByCodigo(codigo: string, tenantId: string): Promise<Proyecto | null> {
return this.repository.findOne({
where: { codigo, tenantId },
});
}
async create(data: CreateProyectoDto): Promise<Proyecto> {
const proyecto = this.repository.create(data);
return this.repository.save(proyecto);
}
async update(id: string, tenantId: string, data: UpdateProyectoDto): Promise<Proyecto | null> {
const proyecto = await this.findById(id, tenantId);
if (!proyecto) {
return null;
}
Object.assign(proyecto, data);
return this.repository.save(proyecto);
}
async delete(id: string, tenantId: string): Promise<boolean> {
const result = await this.repository.delete({ id, tenantId });
return result.affected ? result.affected > 0 : false;
}
async getStatistics(tenantId: string): Promise<{
total: number;
activos: number;
completados: number;
pausados: number;
}> {
const proyectos = await this.repository.find({ where: { tenantId } });
return {
total: proyectos.length,
activos: proyectos.filter(p => p.estadoProyecto === 'activo').length,
completados: proyectos.filter(p => p.estadoProyecto === 'completado').length,
pausados: proyectos.filter(p => p.estadoProyecto === 'pausado').length,
};
}
}

View File

@ -0,0 +1,74 @@
/**
* Capacitacion Entity
* Catálogo de capacitaciones HSE
*
* @module HSE
* @table hse.capacitaciones
* @ddl schemas/03-hse-schema-ddl.sql
* @rf RF-MAA017-002
*/
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
JoinColumn,
Index,
} from 'typeorm';
import { Tenant } from '../../core/entities/tenant.entity';
export type TipoCapacitacion = 'induccion' | 'especifica' | 'certificacion' | 'reentrenamiento';
@Entity({ schema: 'hse', name: 'capacitaciones' })
@Index(['tenantId', 'codigo'], { unique: true })
export class Capacitacion {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'tenant_id', type: 'uuid' })
tenantId: string;
@Column({ type: 'varchar', length: 20 })
codigo: string;
@Column({ type: 'varchar', length: 200 })
nombre: string;
@Column({ type: 'text', nullable: true })
descripcion: string;
@Column({
type: 'enum',
enum: ['induccion', 'especifica', 'certificacion', 'reentrenamiento']
})
tipo: TipoCapacitacion;
@Column({ name: 'duracion_horas', type: 'integer', default: 1 })
duracionHoras: number;
@Column({ name: 'vigencia_meses', type: 'integer', nullable: true })
vigenciaMeses: number;
@Column({ name: 'requiere_evaluacion', type: 'boolean', default: false })
requiereEvaluacion: boolean;
@Column({ name: 'calificacion_minima', type: 'integer', nullable: true })
calificacionMinima: number;
@Column({ type: 'boolean', default: true })
activo: boolean;
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
updatedAt: Date;
// Relations
@ManyToOne(() => Tenant)
@JoinColumn({ name: 'tenant_id' })
tenant: Tenant;
}

View File

@ -0,0 +1,71 @@
/**
* IncidenteAccion Entity
* Acciones correctivas de incidentes
*
* @module HSE
* @table hse.incidente_acciones
* @ddl schemas/03-hse-schema-ddl.sql
* @rf RF-MAA017-001
*/
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
JoinColumn,
} from 'typeorm';
import { Incidente } from './incidente.entity';
import { Employee } from '../../hr/entities/employee.entity';
export type EstadoAccion = 'pendiente' | 'en_progreso' | 'completada' | 'verificada';
@Entity({ schema: 'hse', name: 'incidente_acciones' })
export class IncidenteAccion {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'incidente_id', type: 'uuid' })
incidenteId: string;
@Column({ type: 'text' })
descripcion: string;
@Column({ type: 'varchar', length: 50 })
tipo: string;
@Column({ name: 'responsable_id', type: 'uuid', nullable: true })
responsableId: string;
@Column({ name: 'fecha_compromiso', type: 'date' })
fechaCompromiso: Date;
@Column({ name: 'fecha_cierre', type: 'date', nullable: true })
fechaCierre: Date;
@Column({ type: 'varchar', length: 20, default: 'pendiente' })
estado: EstadoAccion;
@Column({ name: 'evidencia_url', type: 'varchar', length: 500, nullable: true })
evidenciaUrl: string;
@Column({ type: 'text', nullable: true })
observaciones: string;
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
updatedAt: Date;
// Relations
@ManyToOne(() => Incidente, (i) => i.acciones, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'incidente_id' })
incidente: Incidente;
@ManyToOne(() => Employee)
@JoinColumn({ name: 'responsable_id' })
responsable: Employee;
}

View File

@ -0,0 +1,58 @@
/**
* IncidenteInvolucrado Entity
* Personas involucradas en incidentes
*
* @module HSE
* @table hse.incidente_involucrados
* @ddl schemas/03-hse-schema-ddl.sql
* @rf RF-MAA017-001
*/
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
ManyToOne,
JoinColumn,
} from 'typeorm';
import { Incidente } from './incidente.entity';
import { Employee } from '../../hr/entities/employee.entity';
export type RolInvolucrado = 'lesionado' | 'testigo' | 'responsable';
@Entity({ schema: 'hse', name: 'incidente_involucrados' })
export class IncidenteInvolucrado {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'incidente_id', type: 'uuid' })
incidenteId: string;
@Column({ name: 'employee_id', type: 'uuid' })
employeeId: string;
@Column({ type: 'enum', enum: ['lesionado', 'testigo', 'responsable'] })
rol: RolInvolucrado;
@Column({ name: 'descripcion_lesion', type: 'text', nullable: true })
descripcionLesion: string;
@Column({ name: 'parte_cuerpo', type: 'varchar', length: 100, nullable: true })
parteCuerpo: string;
@Column({ name: 'dias_incapacidad', type: 'integer', default: 0 })
diasIncapacidad: number;
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
createdAt: Date;
// Relations
@ManyToOne(() => Incidente, (i) => i.involucrados, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'incidente_id' })
incidente: Incidente;
@ManyToOne(() => Employee)
@JoinColumn({ name: 'employee_id' })
employee: Employee;
}

View File

@ -0,0 +1,111 @@
/**
* Incidente Entity
* Gestión de incidentes de seguridad
*
* @module HSE
* @table hse.incidentes
* @ddl schemas/03-hse-schema-ddl.sql
* @rf RF-MAA017-001
*/
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
OneToMany,
JoinColumn,
Index,
} from 'typeorm';
import { Tenant } from '../../core/entities/tenant.entity';
import { User } from '../../core/entities/user.entity';
import { Fraccionamiento } from '../../construction/entities/fraccionamiento.entity';
import { IncidenteInvolucrado } from './incidente-involucrado.entity';
import { IncidenteAccion } from './incidente-accion.entity';
export type TipoIncidente = 'accidente' | 'incidente' | 'casi_accidente';
export type GravedadIncidente = 'leve' | 'moderado' | 'grave' | 'fatal';
export type EstadoIncidente = 'abierto' | 'en_investigacion' | 'cerrado';
@Entity({ schema: 'hse', name: 'incidentes' })
@Index(['tenantId', 'folio'], { unique: true })
export class Incidente {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'tenant_id', type: 'uuid' })
tenantId: string;
@Column({ type: 'varchar', length: 20 })
folio: string;
@Column({ name: 'fecha_hora', type: 'timestamptz' })
fechaHora: Date;
@Column({ name: 'fraccionamiento_id', type: 'uuid' })
fraccionamientoId: string;
@Column({ name: 'ubicacion_descripcion', type: 'text', nullable: true })
ubicacionDescripcion: string;
@Column({
name: 'ubicacion_geo',
type: 'geometry',
spatialFeatureType: 'Point',
srid: 4326,
nullable: true
})
ubicacionGeo: string;
@Column({ type: 'enum', enum: ['accidente', 'incidente', 'casi_accidente'] })
tipo: TipoIncidente;
@Column({ type: 'enum', enum: ['leve', 'moderado', 'grave', 'fatal'] })
gravedad: GravedadIncidente;
@Column({ type: 'text' })
descripcion: string;
@Column({ name: 'causa_inmediata', type: 'text', nullable: true })
causaInmediata: string;
@Column({ name: 'causa_basica', type: 'text', nullable: true })
causaBasica: string;
@Column({
type: 'enum',
enum: ['abierto', 'en_investigacion', 'cerrado'],
default: 'abierto'
})
estado: EstadoIncidente;
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
createdAt: Date;
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
updatedAt: Date;
@Column({ name: 'created_by', type: 'uuid', nullable: true })
createdById: string;
// Relations
@ManyToOne(() => Tenant)
@JoinColumn({ name: 'tenant_id' })
tenant: Tenant;
@ManyToOne(() => Fraccionamiento)
@JoinColumn({ name: 'fraccionamiento_id' })
fraccionamiento: Fraccionamiento;
@ManyToOne(() => User)
@JoinColumn({ name: 'created_by' })
createdBy: User;
@OneToMany(() => IncidenteInvolucrado, (ii) => ii.incidente)
involucrados: IncidenteInvolucrado[];
@OneToMany(() => IncidenteAccion, (ia) => ia.incidente)
acciones: IncidenteAccion[];
}

View File

@ -0,0 +1,23 @@
/**
* HSE Entities Index
* @module HSE
*
* Entities for Health, Safety & Environment module
* Based on RF-MAA017-001 to RF-MAA017-008
*/
// RF-MAA017-001: Gestión de Incidentes
export * from './incidente.entity';
export * from './incidente-involucrado.entity';
export * from './incidente-accion.entity';
// RF-MAA017-002: Control de Capacitaciones
export * from './capacitacion.entity';
// TODO: Implementar entities adicionales según se necesiten:
// - RF-MAA017-003: Inspecciones de Seguridad
// - RF-MAA017-004: Control de EPP
// - RF-MAA017-005: Cumplimiento STPS
// - RF-MAA017-006: Gestión Ambiental
// - RF-MAA017-007: Permisos de Trabajo
// - RF-MAA017-008: Indicadores HSE

View File

@ -47,8 +47,10 @@ app.get('/health', (req, res) => {
/**
* API Routes
* TODO: Agregar rutas de módulos aquí
*/
import { proyectoController, fraccionamientoController } from './modules/construction/controllers';
// Root API info
app.get(`/api/${API_VERSION}`, (req, res) => {
res.status(200).json({
message: 'API MVP Sistema Administración de Obra',
@ -57,12 +59,16 @@ app.get(`/api/${API_VERSION}`, (req, res) => {
health: '/health',
docs: `/api/${API_VERSION}/docs`,
auth: `/api/${API_VERSION}/auth`,
projects: `/api/${API_VERSION}/projects`,
budgets: `/api/${API_VERSION}/budgets`,
proyectos: `/api/${API_VERSION}/proyectos`,
fraccionamientos: `/api/${API_VERSION}/fraccionamientos`,
},
});
});
// Construction Module Routes
app.use(`/api/${API_VERSION}/proyectos`, proyectoController);
app.use(`/api/${API_VERSION}/fraccionamientos`, fraccionamientoController);
/**
* 404 Handler
*/

View File

@ -0,0 +1,159 @@
# Herencia de SPECS del Core - Construcción
**Fecha:** 2025-12-08
**Versión:** 1.0
**Vertical:** Construcción (MAI/MAE)
**Nivel:** 2B.2
---
## Resumen
| Métrica | Valor |
|---------|-------|
| SPECS Aplicables | 26/30 |
| SPECS Obligatorias | 22 |
| SPECS Opcionales | 4 |
| SPECS No Aplican | 4 |
| Estado Implementación | 0% |
---
## SPECS Obligatorias (Deben Implementarse)
### P0 - Críticas
| SPEC | Gap Original | SP | Estado | Módulos Afectados |
|------|-------------|----:|--------|-------------------|
| SPEC-SISTEMA-SECUENCIAS | ir.sequence | 8 | PENDIENTE | MAI-001, MAE-001 |
| SPEC-VALORACION-INVENTARIO | FIFO/AVCO | 21 | PENDIENTE | MAI-004, MAI-012 |
| SPEC-SEGURIDAD-API-KEYS-PERMISOS | API Keys + ACL | 31 | PENDIENTE | MAI-001 |
| SPEC-REPORTES-FINANCIEROS | Balance/P&L SAT | 13 | PENDIENTE | MAE-003 |
| SPEC-PORTAL-PROVEEDORES | Portal RFQ | 13 | PENDIENTE | MAI-006 |
| SPEC-NOMINA-BASICA | hr_payroll | 21 | PENDIENTE | MAI-008 |
| SPEC-GASTOS-EMPLEADOS | hr_expense | 13 | PENDIENTE | MAI-008 |
| SPEC-TAREAS-RECURRENTES | project.task.recurrence | 13 | PENDIENTE | MAI-002, MAI-005 |
| SPEC-SCHEDULER-REPORTES | ir.cron + mail | 8 | PENDIENTE | MAE-003 |
### P1 - Complementarias
| SPEC | Gap Original | SP | Estado | Módulos Afectados |
|------|-------------|----:|--------|-------------------|
| SPEC-CONTABILIDAD-ANALITICA | Centros de costo | 21 | PENDIENTE | MAE-003 |
| SPEC-CONCILIACION-BANCARIA | Conciliación | 21 | PENDIENTE | MAE-003 |
| SPEC-FIRMA-ELECTRONICA-NOM151 | e.firma | 13 | PENDIENTE | MAE-001, MAI-007 |
| SPEC-TWO-FACTOR-AUTHENTICATION | 2FA | 13 | PENDIENTE | MAI-001 |
| SPEC-TRAZABILIDAD-LOTES-SERIES | Lotes/Series | 13 | PENDIENTE | MAI-004, MAI-012 |
| SPEC-BLANKET-ORDERS | Órdenes marco | 13 | PENDIENTE | MAI-006 |
| SPEC-IMPUESTOS-AVANZADOS | IVA, ISR | 8 | PENDIENTE | MAE-003 |
| SPEC-PLANTILLAS-CUENTAS | Plan contable | 8 | PENDIENTE | MAE-003 |
| SPEC-TASAS-CAMBIO-AUTOMATICAS | Tipos cambio | 5 | PENDIENTE | MAE-003 |
| SPEC-ALERTAS-PRESUPUESTO | Alertas | 8 | PENDIENTE | MAI-012 |
| SPEC-PRESUPUESTOS-REVISIONES | Aprobación | 8 | PENDIENTE | MAI-005, MAI-012 |
| SPEC-RRHH-EVALUACIONES-SKILLS | Evaluaciones | 26 | PENDIENTE | MAI-008 |
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | Burndown | 13 | PENDIENTE | MAI-002, MAI-005 |
| SPEC-LOCALIZACION-PAISES | Localización | 13 | PENDIENTE | MAE-001 |
### Patrones Técnicos
| SPEC | Patrón | SP | Estado | Aplicación |
|------|--------|----:|--------|------------|
| SPEC-MAIL-THREAD-TRACKING | mail.thread | 13 | PENDIENTE | Proyectos, Estimaciones, Obras |
| SPEC-WIZARD-TRANSIENT-MODEL | TransientModel | 8 | PENDIENTE | Wizards de cierre, aprobación |
---
## SPECS Opcionales
| SPEC | Descripción | SP | Decisión | Razón |
|------|-------------|----:|----------|-------|
| SPEC-INTEGRACION-CALENDAR | Calendario | 8 | EVALUAR | Útil para programación de obra |
| SPEC-PRICING-RULES | Reglas precio | 8 | EVALUAR | Para cotizaciones complejas |
| SPEC-OAUTH2-SOCIAL-LOGIN | OAuth2 | 8 | DIFERIR | No prioritario |
| SPEC-CONSOLIDACION-FINANCIERA | Multi-empresa | 13 | DIFERIR | Futuro para constructoras grandes |
---
## SPECS No Aplicables
| SPEC | Razón |
|------|-------|
| SPEC-INVENTARIOS-CICLICOS | No hay inventario tradicional de productos |
| SPEC-INTEGRACION-CALENDAR | El módulo de proyectos maneja calendario propio |
---
## Adaptaciones Requeridas
### Mapeo de Conceptos Core → Construcción
| Concepto Core | Concepto Construcción |
|---------------|----------------------|
| `projects.projects` | Obras, Fraccionamientos |
| `projects.tasks` | Etapas de construcción |
| `inventory.products` | Materiales de construcción |
| `inventory.lots` | Lotes de materiales |
| `hr.employees` | Trabajadores de obra |
| `sales.sale_orders` | Contratos de obra |
| `purchase.purchase_orders` | Órdenes de compra de materiales |
### Extensiones de Entidad
```sql
-- Extensión de projects para construcción
construction.project_extensions (
project_id → projects.projects,
tipo_obra ENUM,
numero_licencia VARCHAR,
fecha_inicio_obra DATE,
fecha_fin_estimada DATE,
m2_construccion DECIMAL,
presupuesto_aprobado DECIMAL
)
-- Extensión de employees para construcción
construction.employee_extensions (
employee_id → hr.employees,
numero_imss VARCHAR,
categoria_obra ENUM,
especialidad VARCHAR,
certificaciones JSONB
)
```
---
## Plan de Implementación
### Fase 1: Fundamentos (SP: 60)
1. SPEC-SISTEMA-SECUENCIAS
2. SPEC-SEGURIDAD-API-KEYS-PERMISOS
3. SPEC-TWO-FACTOR-AUTHENTICATION
### Fase 2: Core de Negocio (SP: 80)
4. SPEC-VALORACION-INVENTARIO
5. SPEC-TRAZABILIDAD-LOTES-SERIES
6. SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN
### Fase 3: Financiero (SP: 65)
7. SPEC-REPORTES-FINANCIEROS
8. SPEC-CONTABILIDAD-ANALITICA
9. SPEC-IMPUESTOS-AVANZADOS
### Fase 4: RRHH (SP: 60)
10. SPEC-NOMINA-BASICA
11. SPEC-GASTOS-EMPLEADOS
12. SPEC-RRHH-EVALUACIONES-SKILLS
---
## Referencias
- Documento Core: `erp-core/docs/04-modelado/MAPEO-SPECS-VERTICALES.md`
- SPECS del Core: `erp-core/docs/04-modelado/especificaciones-tecnicas/transversal/`
- Herencia DB: `database/HERENCIA-ERP-CORE.md`
---
**Documento de herencia de SPECS oficial**
**Última actualización:** 2025-12-08

View File

@ -53,20 +53,37 @@ resumen:
# ESTADO REAL DE IMPLEMENTACIÓN (2025-12-08)
estado_implementacion:
porcentaje: "5%"
archivos_ts: 7
entities_implementadas: 2
services_implementados: 0
controllers_implementados: 0
porcentaje: "15%"
archivos_ts: 25
entities_implementadas: 12
services_implementados: 2
controllers_implementados: 2
archivos_existentes:
- src/types/
- src/entities/ # Parcialmente definidas
modulos_implementados:
construction:
entities: [Proyecto, Fraccionamiento]
services: [ProyectoService, FraccionamientoService]
controllers: [ProyectoController, FraccionamientoController]
estado: "FUNCIONAL"
hr:
entities: [Employee, Puesto, EmployeeFraccionamiento]
services: []
controllers: []
estado: "ENTITIES_COMPLETAS"
hse:
entities: [Incidente, IncidenteInvolucrado, IncidenteAccion, Capacitacion]
services: []
controllers: []
estado: "ENTITIES_PARCIALES"
core:
entities: [User, Tenant]
estado: "BASE"
gap_documentacion_vs_codigo:
documentacion_md: 449
archivos_codigo: 7
ratio: "1.5%"
archivos_codigo: 25
ratio: "5.6%"
nota: "Gap reducido - entities y services base implementados"
herencia_core:
version_core: "1.1.0"

View File

@ -0,0 +1,169 @@
# Herencia de SPECS del Core - Mecánicas Diesel
**Fecha:** 2025-12-08
**Versión:** 1.0
**Vertical:** Mecánicas Diesel (MMD)
**Nivel:** 2B.2
---
## Resumen
| Métrica | Valor |
|---------|-------|
| SPECS Aplicables | 25/30 |
| SPECS Obligatorias | 23 |
| SPECS Opcionales | 2 |
| SPECS No Aplican | 5 |
| Estado Implementación | 0% |
---
## SPECS Obligatorias (Deben Implementarse)
### P0 - Críticas
| SPEC | Gap Original | SP | Estado | Módulos Afectados |
|------|-------------|----:|--------|-------------------|
| SPEC-SISTEMA-SECUENCIAS | ir.sequence | 8 | PENDIENTE | MMD-001, MMD-002 |
| SPEC-VALORACION-INVENTARIO | FIFO/AVCO | 21 | PENDIENTE | MMD-004 |
| SPEC-SEGURIDAD-API-KEYS-PERMISOS | API Keys + ACL | 31 | PENDIENTE | MMD-001 |
| SPEC-REPORTES-FINANCIEROS | Balance/P&L SAT | 13 | PENDIENTE | MMD-006 |
| SPEC-PORTAL-PROVEEDORES | Portal RFQ | 13 | PENDIENTE | MMD-004 |
| SPEC-NOMINA-BASICA | hr_payroll | 21 | PENDIENTE | MMD-001 |
| SPEC-GASTOS-EMPLEADOS | hr_expense | 13 | PENDIENTE | MMD-001 |
| SPEC-TAREAS-RECURRENTES | project.task.recurrence | 13 | PENDIENTE | MMD-002 |
| SPEC-SCHEDULER-REPORTES | ir.cron + mail | 8 | PENDIENTE | MMD-006 |
### P1 - Complementarias
| SPEC | Gap Original | SP | Estado | Módulos Afectados |
|------|-------------|----:|--------|-------------------|
| SPEC-CONTABILIDAD-ANALITICA | Centros de costo | 21 | PENDIENTE | MMD-006 |
| SPEC-CONCILIACION-BANCARIA | Conciliación | 21 | PENDIENTE | MMD-006 |
| SPEC-TWO-FACTOR-AUTHENTICATION | 2FA | 13 | PENDIENTE | MMD-001 |
| SPEC-TRAZABILIDAD-LOTES-SERIES | Lotes/Series | 13 | PENDIENTE | MMD-004 |
| SPEC-PRICING-RULES | Reglas precio | 8 | PENDIENTE | MMD-002, MMD-006 |
| SPEC-BLANKET-ORDERS | Órdenes marco | 13 | PENDIENTE | MMD-004 |
| SPEC-INVENTARIOS-CICLICOS | Conteo cíclico | 13 | PENDIENTE | MMD-004 |
| SPEC-IMPUESTOS-AVANZADOS | IVA, ISR | 8 | PENDIENTE | MMD-006 |
| SPEC-PLANTILLAS-CUENTAS | Plan contable | 8 | PENDIENTE | MMD-006 |
| SPEC-TASAS-CAMBIO-AUTOMATICAS | Tipos cambio | 5 | PENDIENTE | MMD-006 |
| SPEC-ALERTAS-PRESUPUESTO | Alertas | 8 | PENDIENTE | MMD-002, MMD-006 |
| SPEC-PRESUPUESTOS-REVISIONES | Aprobación | 8 | PENDIENTE | MMD-002 |
| SPEC-RRHH-EVALUACIONES-SKILLS | Evaluaciones | 26 | PENDIENTE | MMD-001 |
| SPEC-LOCALIZACION-PAISES | Localización | 13 | PENDIENTE | MMD-001 |
### Patrones Técnicos
| SPEC | Patrón | SP | Estado | Aplicación |
|------|--------|----:|--------|------------|
| SPEC-MAIL-THREAD-TRACKING | mail.thread | 13 | PENDIENTE | Órdenes de trabajo, Diagnósticos |
| SPEC-WIZARD-TRANSIENT-MODEL | TransientModel | 8 | PENDIENTE | Wizards de cotización, cierre |
---
## SPECS Opcionales
| SPEC | Descripción | SP | Decisión | Razón |
|------|-------------|----:|----------|-------|
| SPEC-FIRMA-ELECTRONICA-NOM151 | e.firma | 13 | EVALUAR | Para contratos de servicio |
| SPEC-OAUTH2-SOCIAL-LOGIN | OAuth2 | 8 | DIFERIR | No prioritario |
---
## SPECS No Aplicables
| SPEC | Razón |
|------|-------|
| SPEC-INTEGRACION-CALENDAR | No hay agenda de citas compleja |
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | No hay gestión de proyectos larga |
| SPEC-CONSOLIDACION-FINANCIERA | Negocio de una sola ubicación |
---
## Adaptaciones Requeridas
### Mapeo de Conceptos Core → Mecánicas
| Concepto Core | Concepto Mecánicas |
|---------------|-------------------|
| `sales.sale_orders` | Órdenes de servicio |
| `inventory.products` | Refacciones, partes |
| `inventory.lots` | Lotes OEM, garantías |
| `core.partners` | Clientes con vehículos |
| `projects.tasks` | Trabajos de servicio |
### Extensiones de Entidad
```sql
-- Vehículos de clientes
service_management.vehicles (
id UUID,
partner_id → core.partners,
vin VARCHAR(17),
marca VARCHAR,
modelo VARCHAR,
anio INTEGER,
motor_tipo VARCHAR,
placas VARCHAR
)
-- Órdenes de servicio
service_management.service_orders (
id UUID,
vehicle_id → vehicles,
sale_order_id → sales.sale_orders,
tipo_servicio ENUM,
km_entrada INTEGER,
diagnostico TEXT,
estado ENUM
)
-- Refacciones con compatibilidad
parts_management.parts (
product_id → inventory.products,
oem_number VARCHAR,
aftermarket_number VARCHAR,
compatibilidad JSONB
)
```
---
## Plan de Implementación
### Fase 1: Fundamentos (SP: 52)
1. SPEC-SISTEMA-SECUENCIAS
2. SPEC-SEGURIDAD-API-KEYS-PERMISOS
3. SPEC-TWO-FACTOR-AUTHENTICATION
### Fase 2: Inventario (SP: 55)
4. SPEC-VALORACION-INVENTARIO
5. SPEC-TRAZABILIDAD-LOTES-SERIES
6. SPEC-INVENTARIOS-CICLICOS
7. SPEC-PRICING-RULES
### Fase 3: Operaciones (SP: 34)
8. SPEC-MAIL-THREAD-TRACKING
9. SPEC-WIZARD-TRANSIENT-MODEL
10. SPEC-TAREAS-RECURRENTES
### Fase 4: Financiero (SP: 65)
11. SPEC-REPORTES-FINANCIEROS
12. SPEC-CONTABILIDAD-ANALITICA
13. SPEC-CONCILIACION-BANCARIA
---
## Referencias
- Documento Core: `erp-core/docs/04-modelado/MAPEO-SPECS-VERTICALES.md`
- SPECS del Core: `erp-core/docs/04-modelado/especificaciones-tecnicas/transversal/`
- Herencia DB: `database/HERENCIA-ERP-CORE.md`
- Directivas: `orchestration/directivas/`
---
**Documento de herencia de SPECS oficial**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,106 @@
# Inventarios - ERP Mecánicas Diesel
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Nivel SIMCO:** 2B.2
---
## Descripción
Este directorio contiene los inventarios YAML que sirven como **Single Source of Truth (SSOT)** para el proyecto ERP Mecánicas Diesel. Estos archivos son la referencia canónica para métricas, trazabilidad y componentes del sistema.
---
## Archivos de Inventario
| Archivo | Descripción | Estado |
|---------|-------------|--------|
| [MASTER_INVENTORY.yml](./MASTER_INVENTORY.yml) | Inventario maestro con métricas globales | Completo |
| [DATABASE_INVENTORY.yml](./DATABASE_INVENTORY.yml) | Inventario de objetos de base de datos | Completo |
| [BACKEND_INVENTORY.yml](./BACKEND_INVENTORY.yml) | Inventario de componentes backend | Planificado |
| [FRONTEND_INVENTORY.yml](./FRONTEND_INVENTORY.yml) | Inventario de componentes frontend | Planificado |
| [TRACEABILITY_MATRIX.yml](./TRACEABILITY_MATRIX.yml) | Matriz de trazabilidad RF->ET->US | Completo |
| [DEPENDENCY_GRAPH.yml](./DEPENDENCY_GRAPH.yml) | Grafo de dependencias entre módulos | Completo |
---
## Herencia del Core
Este proyecto hereda del **ERP Core** (nivel 2B.1):
| Aspecto | Heredado | Específico |
|---------|----------|------------|
| **Tablas DB** | 97 | 30+ |
| **Schemas** | 8 | 3 |
| **Specs** | 5 | - |
### Specs Heredadas
1. SPEC-VALORACION-INVENTARIO.md
2. SPEC-TRAZABILIDAD-LOTES-SERIES.md
3. SPEC-INVENTARIOS-CICLICOS.md
4. SPEC-MAIL-THREAD-TRACKING.md
5. SPEC-TAREAS-RECURRENTES.md
### Documento de Herencia
Ver: [`database/HERENCIA-ERP-CORE.md`](../../database/HERENCIA-ERP-CORE.md)
---
## Resumen Ejecutivo
### Métricas del Proyecto
| Métrica | Valor |
|---------|-------|
| **Módulos** | 5 (MD-001 a MD-005) |
| **Schemas específicos** | 3 |
| **Tablas específicas** | 30+ |
| **DDL implementado** | 1,561 líneas |
| **Estado** | DDL_IMPLEMENTADO |
### Schemas Específicos
| Schema | Propósito | Tablas |
|--------|-----------|--------|
| `service_management` | Órdenes de servicio | 10+ |
| `parts_management` | Inventario refacciones | 12+ |
| `vehicle_management` | Gestión de vehículos | 8+ |
---
## Configuración de Puertos (Planificado)
| Servicio | Puerto |
|----------|--------|
| Backend API | 3200 |
| Frontend Web | 5175 |
| PostgreSQL | Compartido con Core |
| Redis | Compartido con Core |
---
## Directivas Específicas
1. [DIRECTIVA-ORDENES-TRABAJO.md](../directivas/DIRECTIVA-ORDENES-TRABAJO.md)
2. [DIRECTIVA-INVENTARIO-REFACCIONES.md](../directivas/DIRECTIVA-INVENTARIO-REFACCIONES.md)
---
## Alineación con ERP Core
Estos inventarios siguen la misma estructura que:
- `/erp-core/orchestration/inventarios/` (proyecto padre)
- `/verticales/construccion/orchestration/inventarios/` (vertical hermana)
### Referencias
- Suite Master: `orchestration/inventarios/SUITE_MASTER_INVENTORY.yml`
- Core: `apps/erp-core/orchestration/inventarios/`
- Status Global: `orchestration/inventarios/STATUS.yml`
---
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,187 @@
# Herencia de Base de Datos - ERP Core -> Retail
**Fecha:** 2025-12-08
**Versión:** 1.0
**Vertical:** Retail
**Nivel:** 2B.2
---
## RESUMEN
La vertical de Retail hereda los schemas base del ERP Core y extiende con schemas específicos del dominio de punto de venta y comercio minorista.
**Ubicación DDL Core:** `apps/erp-core/database/ddl/`
---
## ARQUITECTURA DE HERENCIA
```
┌─────────────────────────────────────────────────────────────────┐
│ ERP CORE (Base) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ auth │ │ core │ │financial│ │inventory│ │ purchase │ │
│ │ 26 tbl │ │ 12 tbl │ │ 15 tbl │ │ 15 tbl │ │ 8 tbl │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ sales │ │analytics│ │ system │ │ crm │ │
│ │ 6 tbl │ │ 5 tbl │ │ 10 tbl │ │ 5 tbl │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ TOTAL: ~102 tablas heredadas │
└─────────────────────────────────────────────────────────────────┘
│ HEREDA
┌─────────────────────────────────────────────────────────────────┐
│ RETAIL (Extensiones) │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ pos │ │ stores │ │ pricing │ │
│ │ (punto venta) │ │ (sucursales) │ │ (promociones) │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
│ EXTENSIONES: ~30 tablas (planificadas) │
└─────────────────────────────────────────────────────────────────┘
```
---
## SCHEMAS HEREDADOS DEL CORE
| Schema | Tablas | Uso en Retail |
|--------|--------|---------------|
| `auth` | 26 | Autenticación, usuarios por sucursal |
| `core` | 12 | Partners (clientes), catálogos |
| `financial` | 15 | Facturas, cuentas, caja |
| `inventory` | 15 | Inventario multi-sucursal |
| `purchase` | 8 | Compras a proveedores |
| `sales` | 6 | Ventas base |
| `crm` | 5 | Clientes frecuentes |
| `analytics` | 5 | Métricas de venta |
| `system` | 10 | Notificaciones |
**Total heredado:** ~102 tablas
---
## SCHEMAS ESPECÍFICOS DE RETAIL (Planificados)
### 1. Schema `pos` (estimado 12+ tablas)
**Propósito:** Punto de venta y operaciones de caja
```sql
-- Tablas principales planificadas:
pos.cash_registers -- Cajas registradoras
pos.cash_sessions -- Sesiones de caja
pos.pos_orders -- Tickets/ventas POS
pos.pos_order_lines -- Líneas de ticket
pos.payment_methods -- Métodos de pago
pos.cash_movements -- Movimientos de caja
pos.cash_counts -- Cortes de caja
pos.receipts -- Recibos
```
### 2. Schema `stores` (estimado 8+ tablas)
**Propósito:** Gestión de sucursales
```sql
-- Tablas principales planificadas:
stores.branches -- Sucursales
stores.branch_inventory -- Inventario por sucursal
stores.transfers -- Transferencias entre sucursales
stores.transfer_lines -- Líneas de transferencia
stores.branch_employees -- Empleados por sucursal
```
### 3. Schema `pricing` (estimado 10+ tablas)
**Propósito:** Precios y promociones
```sql
-- Extiende: sales schema del core
pricing.price_lists -- Listas de precios
pricing.promotions -- Promociones
pricing.discounts -- Descuentos
pricing.loyalty_programs -- Programas de lealtad
pricing.coupons -- Cupones
pricing.price_history -- Historial de precios
```
---
## SPECS DEL CORE APLICABLES
**Documento detallado:** `orchestration/00-guidelines/HERENCIA-SPECS-CORE.md`
### SPECS Obligatorias
| Spec Core | Aplicación en Retail | SP | Estado |
|-----------|---------------------|----:|--------|
| SPEC-SISTEMA-SECUENCIAS | Foliado de tickets y facturas | 8 | PENDIENTE |
| SPEC-VALORACION-INVENTARIO | Costeo de mercancía | 21 | PENDIENTE |
| SPEC-SEGURIDAD-API-KEYS-PERMISOS | Control de acceso por sucursal | 31 | PENDIENTE |
| SPEC-PRICING-RULES | Precios y promociones | 8 | PENDIENTE |
| SPEC-INVENTARIOS-CICLICOS | Conteos en sucursales | 13 | PENDIENTE |
| SPEC-TRAZABILIDAD-LOTES-SERIES | Productos con lote/serie | 13 | PENDIENTE |
| SPEC-MAIL-THREAD-TRACKING | Comunicación con clientes | 13 | PENDIENTE |
| SPEC-WIZARD-TRANSIENT-MODEL | Wizards de cierre de caja | 8 | PENDIENTE |
### SPECS Opcionales
| Spec Core | Decisión | Razón |
|-----------|----------|-------|
| SPEC-PORTAL-PROVEEDORES | EVALUAR | Para compras centralizadas |
| SPEC-TAREAS-RECURRENTES | EVALUAR | Para reorden automático |
### SPECS No Aplican
| Spec Core | Razón |
|-----------|-------|
| SPEC-INTEGRACION-CALENDAR | No requiere calendario de citas |
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | No hay proyectos largos |
| SPEC-FIRMA-ELECTRONICA-NOM151 | No aplica para tickets POS |
---
## ORDEN DE EJECUCIÓN DDL (Futuro)
```bash
# PASO 1: Cargar ERP Core (base)
cd apps/erp-core/database
./scripts/reset-database.sh --force
# PASO 2: Cargar extensiones de Retail
cd apps/verticales/retail/database
psql $DATABASE_URL -f init/00-extensions.sql
psql $DATABASE_URL -f init/01-create-schemas.sql
psql $DATABASE_URL -f init/02-pos-tables.sql
psql $DATABASE_URL -f init/03-stores-tables.sql
psql $DATABASE_URL -f init/04-pricing-tables.sql
```
---
## MAPEO DE NOMENCLATURA
| Core | Retail |
|------|--------|
| `core.partners` | Clientes, proveedores |
| `inventory.products` | Productos de venta |
| `inventory.locations` | Almacenes de sucursal |
| `sales.sale_orders` | Base para POS orders |
| `financial.invoices` | Facturas de venta |
---
## REFERENCIAS
- ERP Core DDL: `apps/erp-core/database/ddl/`
- ERP Core README: `apps/erp-core/database/README.md`
- Directivas: `orchestration/directivas/`
- Inventarios: `orchestration/inventarios/`
---
**Documento de herencia oficial**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,97 @@
# Visión General - ERP Retail
**Versión:** 1.0
**Fecha:** 2025-12-08
**Nivel:** 2B.2 (Vertical)
---
## Propósito del Sistema
Sistema ERP especializado para comercio minorista con punto de venta (POS), gestión de inventario multi-sucursal, control de caja y programas de fidelización. Optimizado para operación en tienda física con capacidad offline.
---
## Dominio del Negocio
### Procesos Principales
1. **Punto de Venta (POS)**
- Venta rápida en mostrador
- Múltiples métodos de pago
- Facturación CFDI 4.0
- Operación offline
2. **Inventario Multi-Sucursal**
- Control de stock por sucursal
- Transferencias entre tiendas
- Conteos cíclicos
- Alertas de reorden
3. **Compras y Reabastecimiento**
- Órdenes de compra centralizadas
- Distribución a sucursales
- Control de proveedores
4. **Clientes y Fidelización**
- Programa de lealtad
- Puntos y recompensas
- Historial de compras
5. **Gestión de Caja**
- Apertura y cierre de caja
- Arqueos
- Control de efectivo
---
## Arquitectura de Módulos
```
RT-001 Fundamentos → Auth, Users, Tenants (hereda 100% core)
RT-002 POS → Punto de venta (20% core)
RT-003 Inventario → Stock multi-sucursal (60% core)
RT-004 Compras → Reabastecimiento (80% core)
RT-005 Clientes → Programa fidelidad (40% core)
RT-006 Precios → Promociones, descuentos (30% core)
RT-007 Caja → Arqueos, cortes (10% core)
RT-008 Reportes → Dashboard ventas (70% core)
RT-009 E-commerce → Tienda online (20% core)
RT-010 Facturación → CFDI 4.0 (60% core)
```
---
## Stack Tecnológico
- **Backend:** NestJS + TypeORM + PostgreSQL
- **Frontend POS:** React + PWA (offline-first)
- **Base de Datos:** PostgreSQL 15+ (hereda ERP Core)
- **Hardware:** Impresora térmica, lector de códigos, cajón
---
## Métricas Objetivo
| Métrica | Valor Objetivo |
|---------|----------------|
| Módulos | 10 |
| Tablas Específicas | ~30 |
| Tablas Heredadas | ~102 |
| Story Points Est. | ~280 |
| Tiempo Venta | < 30 segundos |
| Disponibilidad | 99.9% |
---
## Referencias
- ERP Core: `apps/erp-core/`
- Herencia DB: `database/HERENCIA-ERP-CORE.md`
- SPECS del Core: `HERENCIA-SPECS-CORE.md`
- Inventarios: `orchestration/inventarios/`
---
**Documento de visión oficial**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,116 @@
# Índice de Módulos - ERP Retail
**Versión:** 1.0
**Fecha:** 2025-12-08
**Total Módulos:** 10
---
## Resumen
| Código | Nombre | Descripción | Reutilización Core | Estado |
|--------|--------|-------------|-------------------|--------|
| RT-001 | Fundamentos | Auth, Users, Tenants | 100% | PLANIFICADO |
| RT-002 | POS | Punto de venta | 20% | PLANIFICADO |
| RT-003 | Inventario | Stock multi-sucursal | 60% | PLANIFICADO |
| RT-004 | Compras | Reabastecimiento | 80% | PLANIFICADO |
| RT-005 | Clientes | Programa fidelidad | 40% | PLANIFICADO |
| RT-006 | Precios | Promociones y descuentos | 30% | PLANIFICADO |
| RT-007 | Caja | Arqueos y cortes | 10% | PLANIFICADO |
| RT-008 | Reportes | Dashboard de ventas | 70% | PLANIFICADO |
| RT-009 | E-commerce | Tienda online | 20% | PLANIFICADO |
| RT-010 | Facturación | CFDI 4.0 | 60% | PLANIFICADO |
---
## Detalle por Módulo
### RT-001: Fundamentos
**Herencia:** 100% del core
- Usuarios por sucursal
- Roles: Cajero, Supervisor, Gerente, Admin
### RT-002: POS
**Herencia:** 20%
- Venta rápida en mostrador
- Múltiples formas de pago
- Operación offline (PWA)
- Integración con hardware
### RT-003: Inventario
**Herencia:** 60%
- Stock por sucursal
- Transferencias entre tiendas
- Conteos cíclicos
- Alertas de mínimos
### RT-004: Compras
**Herencia:** 80%
- Órdenes de compra centralizadas
- Distribución a sucursales
- Control de proveedores
### RT-005: Clientes
**Herencia:** 40%
- Programa de lealtad
- Puntos y recompensas
- Historial de compras
- Membresías
### RT-006: Precios
**Herencia:** 30%
- Listas de precios
- Promociones temporales
- Descuentos por volumen
- Cupones
### RT-007: Caja
**Herencia:** 10%
- Sesiones de caja
- Apertura/cierre
- Arqueos
- Movimientos de efectivo
### RT-008: Reportes
**Herencia:** 70%
- Dashboard de ventas
- Análisis por sucursal
- Top productos
- Métricas de cajeros
### RT-009: E-commerce
**Herencia:** 20%
- Tienda online
- Carrito de compras
- Checkout
- Sincronización de inventario
### RT-010: Facturación
**Herencia:** 60%
- CFDI 4.0
- Timbrado automático
- Notas de crédito
- Reportes fiscales
---
## Story Points Estimados
| Módulo | SP Backend | SP Frontend | SP Total |
|--------|-----------|-------------|----------|
| RT-001 | 0 | 0 | 0 |
| RT-002 | 34 | 21 | 55 |
| RT-003 | 21 | 13 | 34 |
| RT-004 | 13 | 8 | 21 |
| RT-005 | 21 | 13 | 34 |
| RT-006 | 21 | 13 | 34 |
| RT-007 | 21 | 13 | 34 |
| RT-008 | 13 | 13 | 26 |
| RT-009 | 34 | 21 | 55 |
| RT-010 | 21 | 8 | 29 |
| **Total** | **199** | **123** | **322** |
---
**Índice de módulos oficial**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,184 @@
# Herencia de SPECS del Core - Retail
**Fecha:** 2025-12-08
**Versión:** 1.0
**Vertical:** Retail (RT)
**Nivel:** 2B.2
---
## Resumen
| Métrica | Valor |
|---------|-------|
| SPECS Aplicables | 24/30 |
| SPECS Obligatorias | 21 |
| SPECS Opcionales | 3 |
| SPECS No Aplican | 6 |
| Estado Implementación | 0% |
---
## SPECS Obligatorias (Deben Implementarse)
### P0 - Críticas
| SPEC | Gap Original | SP | Estado | Módulos Afectados |
|------|-------------|----:|--------|-------------------|
| SPEC-SISTEMA-SECUENCIAS | ir.sequence | 8 | PENDIENTE | RT-001, RT-002, RT-007 |
| SPEC-VALORACION-INVENTARIO | FIFO/AVCO | 21 | PENDIENTE | RT-003 |
| SPEC-SEGURIDAD-API-KEYS-PERMISOS | API Keys + ACL | 31 | PENDIENTE | RT-001 |
| SPEC-REPORTES-FINANCIEROS | Balance/P&L SAT | 13 | PENDIENTE | RT-008, RT-010 |
| SPEC-NOMINA-BASICA | hr_payroll | 21 | PENDIENTE | RT-001 |
| SPEC-GASTOS-EMPLEADOS | hr_expense | 13 | PENDIENTE | RT-001 |
| SPEC-SCHEDULER-REPORTES | ir.cron + mail | 8 | PENDIENTE | RT-008 |
### P1 - Complementarias
| SPEC | Gap Original | SP | Estado | Módulos Afectados |
|------|-------------|----:|--------|-------------------|
| SPEC-CONTABILIDAD-ANALITICA | Centros de costo | 21 | PENDIENTE | RT-008 |
| SPEC-CONCILIACION-BANCARIA | Conciliación | 21 | PENDIENTE | RT-007, RT-008 |
| SPEC-TWO-FACTOR-AUTHENTICATION | 2FA | 13 | PENDIENTE | RT-001 |
| SPEC-TRAZABILIDAD-LOTES-SERIES | Lotes/Series | 13 | PENDIENTE | RT-003 |
| SPEC-PRICING-RULES | Reglas precio | 8 | PENDIENTE | RT-006 |
| SPEC-BLANKET-ORDERS | Órdenes marco | 13 | PENDIENTE | RT-004 |
| SPEC-INVENTARIOS-CICLICOS | Conteo cíclico | 13 | PENDIENTE | RT-003 |
| SPEC-IMPUESTOS-AVANZADOS | IVA, ISR | 8 | PENDIENTE | RT-010 |
| SPEC-PLANTILLAS-CUENTAS | Plan contable | 8 | PENDIENTE | RT-008 |
| SPEC-TASAS-CAMBIO-AUTOMATICAS | Tipos cambio | 5 | PENDIENTE | RT-008 |
| SPEC-ALERTAS-PRESUPUESTO | Alertas | 8 | PENDIENTE | RT-008 |
| SPEC-RRHH-EVALUACIONES-SKILLS | Evaluaciones | 26 | PENDIENTE | RT-001 |
| SPEC-LOCALIZACION-PAISES | Localización | 13 | PENDIENTE | RT-001, RT-010 |
### Patrones Técnicos
| SPEC | Patrón | SP | Estado | Aplicación |
|------|--------|----:|--------|------------|
| SPEC-MAIL-THREAD-TRACKING | mail.thread | 13 | PENDIENTE | Órdenes, Clientes |
| SPEC-WIZARD-TRANSIENT-MODEL | TransientModel | 8 | PENDIENTE | Wizards de cierre, arqueo |
---
## SPECS Opcionales
| SPEC | Descripción | SP | Decisión | Razón |
|------|-------------|----:|----------|-------|
| SPEC-PORTAL-PROVEEDORES | Portal RFQ | 13 | EVALUAR | Para compras centralizadas |
| SPEC-TAREAS-RECURRENTES | Recurrencia | 13 | EVALUAR | Para reorden automático |
| SPEC-PRESUPUESTOS-REVISIONES | Aprobación | 8 | DIFERIR | Menos relevante en retail |
---
## SPECS No Aplicables
| SPEC | Razón |
|------|-------|
| SPEC-INTEGRACION-CALENDAR | No requiere calendario de citas |
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | No hay proyectos largos |
| SPEC-FIRMA-ELECTRONICA-NOM151 | No aplica para tickets POS |
| SPEC-OAUTH2-SOCIAL-LOGIN | El personal usa login tradicional |
| SPEC-CONSOLIDACION-FINANCIERA | Generalmente una empresa |
---
## Adaptaciones Requeridas
### Mapeo de Conceptos Core → Retail
| Concepto Core | Concepto Retail |
|---------------|-----------------|
| `sales.sale_orders` | Tickets POS |
| `inventory.products` | Productos de venta |
| `inventory.locations` | Sucursales |
| `inventory.stock_moves` | Transferencias entre tiendas |
| `core.partners` | Clientes con membresía |
| `financial.payments` | Pagos en caja |
### Extensiones de Entidad
```sql
-- Sucursales
stores.branches (
id UUID,
location_id → inventory.locations,
nombre VARCHAR,
direccion TEXT,
gerente_id → hr.employees,
horario JSONB,
activa BOOLEAN
)
-- Sesiones de caja
pos.cash_sessions (
id UUID,
branch_id → branches,
cajero_id → hr.employees,
caja_id → cash_registers,
fecha_apertura TIMESTAMPTZ,
fecha_cierre TIMESTAMPTZ,
saldo_inicial DECIMAL,
saldo_final DECIMAL,
estado ENUM
)
-- Tickets POS
pos.pos_orders (
id UUID,
session_id → cash_sessions,
sale_order_id → sales.sale_orders,
numero_ticket VARCHAR,
subtotal DECIMAL,
descuentos DECIMAL,
impuestos DECIMAL,
total DECIMAL
)
-- Programa de lealtad
pricing.loyalty_programs (
id UUID,
nombre VARCHAR,
tipo ENUM('puntos', 'cashback', 'descuento'),
reglas JSONB,
activo BOOLEAN
)
```
---
## Plan de Implementación
### Fase 1: Fundamentos (SP: 52)
1. SPEC-SISTEMA-SECUENCIAS
2. SPEC-SEGURIDAD-API-KEYS-PERMISOS
3. SPEC-TWO-FACTOR-AUTHENTICATION
### Fase 2: Inventario (SP: 55)
4. SPEC-VALORACION-INVENTARIO
5. SPEC-TRAZABILIDAD-LOTES-SERIES
6. SPEC-INVENTARIOS-CICLICOS
7. SPEC-PRICING-RULES
### Fase 3: Operaciones POS (SP: 21)
8. SPEC-MAIL-THREAD-TRACKING
9. SPEC-WIZARD-TRANSIENT-MODEL
### Fase 4: Financiero (SP: 65)
10. SPEC-REPORTES-FINANCIEROS
11. SPEC-CONTABILIDAD-ANALITICA
12. SPEC-CONCILIACION-BANCARIA
13. SPEC-IMPUESTOS-AVANZADOS
---
## Referencias
- Documento Core: `erp-core/docs/04-modelado/MAPEO-SPECS-VERTICALES.md`
- SPECS del Core: `erp-core/docs/04-modelado/especificaciones-tecnicas/transversal/`
- Herencia DB: `database/HERENCIA-ERP-CORE.md`
- Directivas: `orchestration/directivas/`
---
**Documento de herencia de SPECS oficial**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,95 @@
# Inventarios - ERP Retail
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Nivel SIMCO:** 2B.2
---
## Descripción
Este directorio contiene los inventarios YAML que sirven como **Single Source of Truth (SSOT)** para el proyecto ERP Retail. Estos archivos son la referencia canónica para métricas, trazabilidad y componentes del sistema.
---
## Archivos de Inventario
| Archivo | Descripción | Estado |
|---------|-------------|--------|
| [MASTER_INVENTORY.yml](./MASTER_INVENTORY.yml) | Inventario maestro con métricas globales | Completo |
| [DATABASE_INVENTORY.yml](./DATABASE_INVENTORY.yml) | Inventario de objetos de base de datos | Planificado |
| [BACKEND_INVENTORY.yml](./BACKEND_INVENTORY.yml) | Inventario de componentes backend | Planificado |
| [FRONTEND_INVENTORY.yml](./FRONTEND_INVENTORY.yml) | Inventario de componentes frontend | Planificado |
| [TRACEABILITY_MATRIX.yml](./TRACEABILITY_MATRIX.yml) | Matriz de trazabilidad RF->ET->US | Completo |
| [DEPENDENCY_GRAPH.yml](./DEPENDENCY_GRAPH.yml) | Grafo de dependencias entre módulos | Completo |
---
## Herencia del Core
Este proyecto hereda del **ERP Core** (nivel 2B.1):
| Aspecto | Heredado | Específico |
|---------|----------|------------|
| **Tablas DB** | ~100 | Planificado |
| **Schemas** | 8+ | Planificado |
| **Specs** | 3 | - |
### Specs Heredadas
1. SPEC-PRICING-RULES.md
2. SPEC-INVENTARIOS-CICLICOS.md
3. SPEC-TRAZABILIDAD-LOTES-SERIES.md
---
## Resumen Ejecutivo
### Métricas del Proyecto
| Métrica | Valor |
|---------|-------|
| **Módulos** | 5 (RT-001 a RT-005) |
| **Estado** | PLANIFICACION_COMPLETA |
| **Completitud** | 15% |
### Dominio del Negocio
- Punto de venta (POS)
- Inventario multi-sucursal
- Gestión de precios y promociones
- Control de cajas
---
## Directivas Específicas
1. [DIRECTIVA-PUNTO-VENTA.md](../directivas/DIRECTIVA-PUNTO-VENTA.md)
2. [DIRECTIVA-INVENTARIO-SUCURSALES.md](../directivas/DIRECTIVA-INVENTARIO-SUCURSALES.md)
---
## Configuración de Puertos (Planificado)
| Servicio | Puerto |
|----------|--------|
| Backend API | 3400 |
| Frontend Web | 5177 |
| POS App | 5178 |
---
## Alineación con ERP Core
Estos inventarios siguen la misma estructura que:
- `/erp-core/orchestration/inventarios/` (proyecto padre)
### Referencias
- Suite Master: `orchestration/inventarios/SUITE_MASTER_INVENTORY.yml`
- Core: `apps/erp-core/orchestration/inventarios/`
- Status Global: `orchestration/inventarios/STATUS.yml`
---
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,182 @@
# Herencia de Base de Datos - ERP Core -> Vidrio Templado
**Fecha:** 2025-12-08
**Versión:** 1.0
**Vertical:** Vidrio Templado
**Nivel:** 2B.2
---
## RESUMEN
La vertical de Vidrio Templado hereda los schemas base del ERP Core y extiende con schemas específicos del dominio de producción de vidrio.
**Ubicación DDL Core:** `apps/erp-core/database/ddl/`
---
## ARQUITECTURA DE HERENCIA
```
┌─────────────────────────────────────────────────────────────────┐
│ ERP CORE (Base) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ auth │ │ core │ │financial│ │inventory│ │ purchase │ │
│ │ 26 tbl │ │ 12 tbl │ │ 15 tbl │ │ 15 tbl │ │ 8 tbl │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ sales │ │analytics│ │ system │ │
│ │ 6 tbl │ │ 5 tbl │ │ 10 tbl │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ TOTAL: ~97 tablas heredadas │
└─────────────────────────────────────────────────────────────────┘
│ HEREDA
┌─────────────────────────────────────────────────────────────────┐
│ VIDRIO TEMPLADO (Extensiones) │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ production │ │ quality │ │ glass │ │
│ │ management │ │ control │ │ inventory │ │
│ │ (hornos) │ │ (inspección) │ │ (lotes) │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
│ EXTENSIONES: ~25 tablas (planificadas) │
└─────────────────────────────────────────────────────────────────┘
```
---
## SCHEMAS HEREDADOS DEL CORE
| Schema | Tablas | Uso en Vidrio Templado |
|--------|--------|------------------------|
| `auth` | 26 | Autenticación, usuarios, roles, permisos |
| `core` | 12 | Partners (clientes), catálogos |
| `financial` | 15 | Facturas, cuentas contables |
| `inventory` | 15 | Base para materia prima y producto terminado |
| `purchase` | 8 | Compras de materiales |
| `sales` | 6 | Cotizaciones, órdenes de venta |
| `analytics` | 5 | Centros de costo |
| `system` | 10 | Mensajes, notificaciones |
**Total heredado:** ~97 tablas
---
## SCHEMAS ESPECÍFICOS DE VIDRIO TEMPLADO (Planificados)
### 1. Schema `production` (estimado 10+ tablas)
**Propósito:** Gestión de producción y hornos de templado
```sql
-- Tablas principales planificadas:
production.production_orders -- Órdenes de producción
production.production_lines -- Líneas de producción (hornos)
production.work_orders -- Órdenes de trabajo
production.cutting_plans -- Planes de corte
production.oven_schedules -- Programación de hornos
production.temperature_logs -- Registros de temperatura
```
### 2. Schema `quality` (estimado 8+ tablas)
**Propósito:** Control de calidad y trazabilidad
```sql
-- Tablas principales planificadas:
quality.inspections -- Inspecciones de calidad
quality.defect_types -- Catálogo de defectos
quality.quality_tests -- Pruebas de calidad
quality.certifications -- Certificaciones de producto
quality.non_conformities -- No conformidades
```
### 3. Schema `glass` (estimado 7+ tablas)
**Propósito:** Inventario especializado de vidrio
```sql
-- Extiende: inventory schema del core
glass.glass_types -- Tipos de vidrio
glass.glass_lots -- Lotes de producción
glass.glass_dimensions -- Dimensiones estándar
glass.raw_materials -- Materia prima
```
---
## SPECS DEL CORE APLICABLES
**Documento detallado:** `orchestration/00-guidelines/HERENCIA-SPECS-CORE.md`
### SPECS Obligatorias
| Spec Core | Aplicación en Vidrio Templado | SP | Estado |
|-----------|------------------------------|----:|--------|
| SPEC-SISTEMA-SECUENCIAS | Foliado de órdenes y lotes | 8 | PENDIENTE |
| SPEC-VALORACION-INVENTARIO | Costeo de materia prima y producto | 21 | PENDIENTE |
| SPEC-SEGURIDAD-API-KEYS-PERMISOS | Control de acceso | 31 | PENDIENTE |
| SPEC-TRAZABILIDAD-LOTES-SERIES | Lotes de producción de vidrio | 13 | PENDIENTE |
| SPEC-PRICING-RULES | Precios por dimensiones y tipo | 8 | PENDIENTE |
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | Control de producción | 13 | PENDIENTE |
| SPEC-MAIL-THREAD-TRACKING | Historial de órdenes | 13 | PENDIENTE |
| SPEC-WIZARD-TRANSIENT-MODEL | Wizards de corte y templado | 8 | PENDIENTE |
### SPECS Opcionales
| Spec Core | Decisión | Razón |
|-----------|----------|-------|
| SPEC-INVENTARIOS-CICLICOS | EVALUAR | Útil para materia prima |
| SPEC-FIRMA-ELECTRONICA-NOM151 | EVALUAR | Certificados de calidad |
### SPECS No Aplican
| Spec Core | Razón |
|-----------|-------|
| SPEC-INTEGRACION-CALENDAR | No requiere calendario externo |
| SPEC-CONSOLIDACION-FINANCIERA | Negocio de una sola planta |
---
## ORDEN DE EJECUCIÓN DDL (Futuro)
```bash
# PASO 1: Cargar ERP Core (base)
cd apps/erp-core/database
./scripts/reset-database.sh --force
# PASO 2: Cargar extensiones de Vidrio Templado
cd apps/verticales/vidrio-templado/database
psql $DATABASE_URL -f init/00-extensions.sql
psql $DATABASE_URL -f init/01-create-schemas.sql
psql $DATABASE_URL -f init/02-production-tables.sql
psql $DATABASE_URL -f init/03-quality-tables.sql
psql $DATABASE_URL -f init/04-glass-inventory.sql
```
---
## MAPEO DE NOMENCLATURA
| Core | Vidrio Templado |
|------|-----------------|
| `core.partners` | Clientes, proveedores |
| `inventory.products` | Producto terminado base |
| `inventory.locations` | Almacenes de vidrio |
| `sales.sale_orders` | Pedidos de vidrio |
| `purchase.purchase_orders` | Compras de materia prima |
---
## REFERENCIAS
- ERP Core DDL: `apps/erp-core/database/ddl/`
- ERP Core README: `apps/erp-core/database/README.md`
- Directivas: `orchestration/directivas/`
- Inventarios: `orchestration/inventarios/`
---
**Documento de herencia oficial**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,104 @@
# Visión General - ERP Vidrio Templado
**Versión:** 1.0
**Fecha:** 2025-12-08
**Nivel:** 2B.2 (Vertical)
---
## Propósito del Sistema
Sistema ERP especializado para empresas de manufactura de vidrio templado, laminado y procesado. Gestiona todo el ciclo desde la cotización hasta el despacho, incluyendo control de producción, optimización de corte, control de hornos de templado y gestión de calidad.
---
## Dominio del Negocio
### Procesos Principales
1. **Cotización y Ventas**
- Cotización por dimensiones, tipo de vidrio y acabados
- Cálculo automático de precios por m²
- Gestión de clientes y arquitectos
2. **Producción**
- Órdenes de producción
- Optimización de corte (nesting)
- Control de hornos de templado
- Programación de producción
3. **Inventario**
- Control de materia prima (láminas de vidrio)
- Trazabilidad de lotes
- Gestión de producto terminado
4. **Control de Calidad**
- Inspecciones de producto
- Pruebas de fragmentación
- Certificaciones
5. **Despacho**
- Logística de entrega
- Instalación (opcional)
---
## Tipos de Vidrio Manejados
| Tipo | Descripción |
|------|-------------|
| Templado | Tratamiento térmico para resistencia |
| Laminado | Capas con PVB/EVA |
| Insulado | Cámaras de aire |
| Curvo | Templado con curvatura |
| Esmerilado | Acabado mate |
| Serigrafiado | Con diseños impresos |
---
## Arquitectura de Módulos
```
VT-001 Fundamentos → Auth, Users, Tenants (hereda 100% core)
VT-002 Cotizaciones → Cotizador de vidrio (30% core)
VT-003 Producción → Órdenes de producción (20% core)
VT-004 Inventario → Stock de vidrio (70% core)
VT-005 Corte → Optimización de corte (0% core - nuevo)
VT-006 Templado → Control de hornos (0% core - nuevo)
VT-007 Calidad → Inspecciones, QC (40% core)
VT-008 Despacho → Logística (50% core)
```
---
## Stack Tecnológico
- **Backend:** NestJS + TypeORM + PostgreSQL
- **Frontend:** React + TypeScript + Vite
- **Base de Datos:** PostgreSQL 15+ (hereda ERP Core)
- **Extensiones:** PostGIS (dimensiones)
---
## Métricas Objetivo
| Métrica | Valor Objetivo |
|---------|----------------|
| Módulos | 8 |
| Tablas Específicas | ~25 |
| Tablas Heredadas | ~97 |
| Story Points Est. | ~200 |
---
## Referencias
- ERP Core: `apps/erp-core/`
- Herencia DB: `database/HERENCIA-ERP-CORE.md`
- SPECS del Core: `HERENCIA-SPECS-CORE.md`
- Inventarios: `orchestration/inventarios/`
---
**Documento de visión oficial**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,154 @@
# Índice de Módulos - ERP Vidrio Templado
**Versión:** 1.0
**Fecha:** 2025-12-08
**Total Módulos:** 8
---
## Resumen
| Código | Nombre | Descripción | Reutilización Core | Estado |
|--------|--------|-------------|-------------------|--------|
| VT-001 | Fundamentos | Auth, Users, Tenants | 100% | PLANIFICADO |
| VT-002 | Cotizaciones | Cotizador de vidrio | 30% | PLANIFICADO |
| VT-003 | Producción | Órdenes de producción | 20% | PLANIFICADO |
| VT-004 | Inventario | Stock de vidrio y materia prima | 70% | PLANIFICADO |
| VT-005 | Corte | Optimización de corte | 0% | PLANIFICADO |
| VT-006 | Templado | Control de hornos | 0% | PLANIFICADO |
| VT-007 | Calidad | Control de calidad | 40% | PLANIFICADO |
| VT-008 | Despacho | Logística y entregas | 50% | PLANIFICADO |
---
## Detalle por Módulo
### VT-001: Fundamentos
**Herencia:** 100% del core (MGN-001 a MGN-004)
**Propósito:** Autenticación, usuarios, roles, multi-tenancy
- Usuarios del sistema (operadores, supervisores, gerentes)
- Roles y permisos por planta
- Multi-tenancy para franquicias
### VT-002: Cotizaciones
**Herencia:** 30% del core (sales)
**Propósito:** Cotización de productos de vidrio
- Cotizador por dimensiones (alto × ancho)
- Tipos de vidrio y espesores
- Cálculo de precios por m²
- Acabados y procesamientos adicionales
- Generación de PDF para cliente
### VT-003: Producción
**Herencia:** 20% del core (projects)
**Propósito:** Gestión de órdenes de producción
- Órdenes de producción
- Estados: borrador → programado → corte → templado → QC → terminado
- Asignación de recursos
- Tiempos de producción
### VT-004: Inventario
**Herencia:** 70% del core (inventory)
**Propósito:** Control de materia prima y producto terminado
- Láminas de vidrio (materia prima)
- Control de lotes por proveedor
- Producto terminado
- Merma y desperdicio
- Alertas de reorden
### VT-005: Corte
**Herencia:** 0% - Módulo nuevo
**Propósito:** Optimización de corte de vidrio
- Algoritmo de nesting para optimizar cortes
- Planes de corte
- Reducción de desperdicio
- Integración con máquinas CNC (futuro)
### VT-006: Templado
**Herencia:** 0% - Módulo nuevo
**Propósito:** Control de hornos de templado
- Programación de hornos
- Parámetros de templado (temperatura, tiempo, velocidad)
- Registro de ciclos de templado
- Mantenimiento preventivo de hornos
### VT-007: Calidad
**Herencia:** 40% del core (system)
**Propósito:** Control de calidad
- Inspecciones de producto
- Pruebas de fragmentación
- No conformidades
- Certificaciones de producto
- Trazabilidad de defectos
### VT-008: Despacho
**Herencia:** 50% del core (inventory, sales)
**Propósito:** Logística de entregas
- Programación de entregas
- Rutas de entrega
- Confirmación de recepción
- Instalación (opcional)
- Evidencia fotográfica
---
## Dependencias entre Módulos
```
VT-001 (Fundamentos)
VT-002 (Cotizaciones) ←→ VT-004 (Inventario)
VT-003 (Producción)
VT-005 (Corte) → VT-006 (Templado)
VT-007 (Calidad)
VT-008 (Despacho)
```
---
## Story Points Estimados
| Módulo | SP Backend | SP Frontend | SP Total |
|--------|-----------|-------------|----------|
| VT-001 | 0 (hereda) | 0 (hereda) | 0 |
| VT-002 | 21 | 13 | 34 |
| VT-003 | 21 | 13 | 34 |
| VT-004 | 13 | 8 | 21 |
| VT-005 | 34 | 13 | 47 |
| VT-006 | 21 | 13 | 34 |
| VT-007 | 13 | 8 | 21 |
| VT-008 | 13 | 8 | 21 |
| **Total** | **136** | **76** | **212** |
---
## Referencias
- Documentación por módulo: `VT-XXX-nombre/README.md`
- SPECS heredadas: `orchestration/00-guidelines/HERENCIA-SPECS-CORE.md`
- Visión: `00-vision-general/VISION-VIDRIO.md`
---
**Índice de módulos oficial**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,21 @@
# VT-001: Fundamentos
**Módulo:** Fundamentos
**Herencia:** 100% del ERP Core
**Estado:** PLANIFICADO
## Descripción
Módulo base que hereda completamente del ERP Core. Proporciona autenticación, gestión de usuarios, roles y multi-tenancy.
## Componentes Heredados
- MGN-001: Auth (JWT, OAuth2, 2FA)
- MGN-002: Users (CRUD, perfiles)
- MGN-003: Roles (RBAC)
- MGN-004: Tenants (Multi-tenancy, RLS)
## Configuración Específica
- Roles por defecto: Operador, Supervisor, Gerente, Administrador
- Permisos por módulo de vidrio
## Referencias
- ERP Core: `apps/erp-core/`

View File

@ -0,0 +1,26 @@
# VT-002: Cotizaciones
**Módulo:** Cotizaciones de Vidrio
**Herencia:** 30% del ERP Core (sales)
**Estado:** PLANIFICADO
## Descripción
Cotizador especializado para productos de vidrio. Calcula precios por dimensiones, tipo de vidrio, espesor y acabados.
## Funcionalidades
- Cotización por dimensiones (alto × ancho × espesor)
- Catálogo de tipos de vidrio
- Acabados y procesamientos adicionales
- Cálculo automático de precios por m²
- Generación de PDF
- Conversión a orden de producción
## Entidades
- `quotes.quotes` - Cotizaciones
- `quotes.quote_lines` - Líneas de cotización
- `quotes.glass_types` - Tipos de vidrio
- `quotes.finishes` - Acabados disponibles
## SPECS Aplicables
- SPEC-PRICING-RULES
- SPEC-MAIL-THREAD-TRACKING

View File

@ -0,0 +1,33 @@
# VT-003: Producción
**Módulo:** Órdenes de Producción
**Herencia:** 20% del ERP Core (projects)
**Estado:** PLANIFICADO
## Descripción
Gestión de órdenes de producción desde la cotización aprobada hasta el producto terminado.
## Funcionalidades
- Creación de órdenes desde cotizaciones
- Estados de producción
- Asignación de recursos
- Programación de producción
- Seguimiento de tiempos
## Estados del Flujo
1. `draft` - Borrador
2. `scheduled` - Programado
3. `cutting` - En corte
4. `tempering` - En templado
5. `quality` - Control de calidad
6. `done` - Terminado
7. `delivered` - Entregado
## Entidades
- `production.production_orders` - Órdenes de producción
- `production.production_lines` - Líneas de horno
- `production.work_orders` - Órdenes de trabajo
## SPECS Aplicables
- SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN
- SPEC-TAREAS-RECURRENTES

View File

@ -0,0 +1,27 @@
# VT-004: Inventario
**Módulo:** Inventario de Vidrio
**Herencia:** 70% del ERP Core (inventory)
**Estado:** PLANIFICADO
## Descripción
Control de materia prima (láminas de vidrio) y producto terminado con trazabilidad de lotes.
## Funcionalidades
- Control de láminas de materia prima
- Trazabilidad de lotes por proveedor
- Producto terminado
- Control de merma y desperdicio
- Alertas de reorden
- Valorización FIFO/AVCO
## Entidades
- `glass.glass_inventory` - Inventario de vidrio
- `glass.glass_lots` - Lotes de materia prima
- `glass.raw_materials` - Láminas de vidrio
- `glass.finished_products` - Producto terminado
## SPECS Aplicables
- SPEC-VALORACION-INVENTARIO
- SPEC-TRAZABILIDAD-LOTES-SERIES
- SPEC-INVENTARIOS-CICLICOS (opcional)

View File

@ -0,0 +1,26 @@
# VT-005: Corte
**Módulo:** Optimización de Corte
**Herencia:** 0% - Módulo nuevo
**Estado:** PLANIFICADO
## Descripción
Módulo especializado para optimización de cortes de vidrio (nesting) que minimiza el desperdicio de materia prima.
## Funcionalidades
- Algoritmo de nesting para optimizar cortes
- Planes de corte por lámina
- Cálculo de desperdicio
- Visualización de patrones de corte
- Integración con CNC (futuro)
- Histórico de eficiencia
## Entidades
- `cutting.cutting_plans` - Planes de corte
- `cutting.cutting_patterns` - Patrones optimizados
- `cutting.waste_records` - Registro de desperdicio
## Algoritmos
- First Fit Decreasing (FFD)
- Guillotine cuts
- Optimización por rotación de piezas

View File

@ -0,0 +1,28 @@
# VT-006: Templado
**Módulo:** Control de Hornos
**Herencia:** 0% - Módulo nuevo
**Estado:** PLANIFICADO
## Descripción
Control de hornos de templado con registro de parámetros y ciclos de producción.
## Funcionalidades
- Programación de hornos
- Registro de parámetros (temperatura, tiempo, velocidad)
- Ciclos de templado por tipo de vidrio
- Registro de logs de producción
- Mantenimiento preventivo
- Alertas de parámetros fuera de rango
## Entidades
- `tempering.ovens` - Hornos de templado
- `tempering.oven_schedules` - Programación
- `tempering.tempering_cycles` - Ciclos de templado
- `tempering.temperature_logs` - Logs de temperatura
- `tempering.maintenance_records` - Mantenimientos
## Parámetros de Control
- Temperatura de calentamiento: 620-720°C
- Tiempo en horno: según espesor
- Velocidad de enfriamiento: presión de aire

View File

@ -0,0 +1,28 @@
# VT-007: Calidad
**Módulo:** Control de Calidad
**Herencia:** 40% del ERP Core (system)
**Estado:** PLANIFICADO
## Descripción
Control de calidad para producto de vidrio templado, incluyendo inspecciones, pruebas de fragmentación y certificaciones.
## Funcionalidades
- Inspecciones de producto
- Pruebas de fragmentación (NMX-R-2-1989)
- Registro de no conformidades
- Certificaciones de producto
- Trazabilidad de defectos
- Reportes de calidad
## Entidades
- `quality.inspections` - Inspecciones
- `quality.fragmentation_tests` - Pruebas de fragmentación
- `quality.non_conformities` - No conformidades
- `quality.certifications` - Certificaciones
- `quality.defect_types` - Tipos de defectos
## Criterios de Aceptación
- Fragmentación: mín 40 partículas en 5×5cm
- Defectos visuales: según tolerancias
- Dimensiones: ±2mm

View File

@ -0,0 +1,31 @@
# VT-008: Despacho
**Módulo:** Despacho y Logística
**Herencia:** 50% del ERP Core (inventory, sales)
**Estado:** PLANIFICADO
## Descripción
Gestión de entregas de producto terminado, incluyendo rutas, confirmaciones y evidencia.
## Funcionalidades
- Programación de entregas
- Asignación de rutas
- Confirmación de recepción
- Evidencia fotográfica
- Instalación (opcional)
- Actas de entrega
## Entidades
- `delivery.deliveries` - Entregas
- `delivery.delivery_lines` - Líneas de entrega
- `delivery.routes` - Rutas
- `delivery.confirmations` - Confirmaciones
- `delivery.installations` - Instalaciones
## Flujo de Entrega
1. Programación de entrega
2. Asignación de ruta y vehículo
3. Carga de producto
4. Entrega en sitio
5. Confirmación con evidencia
6. Cierre de entrega

View File

@ -0,0 +1,65 @@
# Épica: Fundamentos del Sistema
**Código:** EPIC-VT-001
**Módulos:** VT-001 a VT-002
**Estado:** PLANIFICADO
---
## Descripción
Implementación de los módulos fundacionales del ERP Vidrio Templado, incluyendo la configuración inicial del sistema heredado del core y el módulo de cotizaciones.
---
## Objetivos
1. Configurar el ambiente de desarrollo heredando del ERP Core
2. Implementar el cotizador de vidrio con cálculo por m²
3. Establecer el catálogo de tipos de vidrio y acabados
---
## Módulos Incluidos
| Módulo | Descripción | SP Estimados |
|--------|-------------|--------------|
| VT-001 | Fundamentos (hereda core) | 0 |
| VT-002 | Cotizaciones | 34 |
---
## User Stories Principales
1. Como usuario, quiero iniciar sesión en el sistema de vidrio templado
2. Como vendedor, quiero crear una cotización de vidrio por dimensiones
3. Como vendedor, quiero calcular el precio automático por m²
4. Como vendedor, quiero generar un PDF de cotización para el cliente
---
## Criterios de Aceptación
- [ ] Sistema de autenticación funcional (heredado)
- [ ] CRUD de cotizaciones implementado
- [ ] Cálculo de precios por dimensiones correcto
- [ ] Generación de PDF funcional
- [ ] Catálogo de tipos de vidrio completo
---
## Dependencias
- ERP Core instalado y funcional
- Base de datos configurada con schemas heredados
---
## Story Points Totales
**34 SP** (excluyendo fundamentos heredados)
---
**Épica fundacional**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,175 @@
# Herencia de SPECS del Core - Vidrio Templado
**Fecha:** 2025-12-08
**Versión:** 1.0
**Vertical:** Vidrio Templado (VT)
**Nivel:** 2B.2
---
## Resumen
| Métrica | Valor |
|---------|-------|
| SPECS Aplicables | 25/30 |
| SPECS Obligatorias | 22 |
| SPECS Opcionales | 3 |
| SPECS No Aplican | 5 |
| Estado Implementación | 0% |
---
## SPECS Obligatorias (Deben Implementarse)
### P0 - Críticas
| SPEC | Gap Original | SP | Estado | Módulos Afectados |
|------|-------------|----:|--------|-------------------|
| SPEC-SISTEMA-SECUENCIAS | ir.sequence | 8 | PENDIENTE | VT-001, VT-002 |
| SPEC-VALORACION-INVENTARIO | FIFO/AVCO | 21 | PENDIENTE | VT-004 |
| SPEC-SEGURIDAD-API-KEYS-PERMISOS | API Keys + ACL | 31 | PENDIENTE | VT-001 |
| SPEC-REPORTES-FINANCIEROS | Balance/P&L SAT | 13 | PENDIENTE | VT-008 |
| SPEC-PORTAL-PROVEEDORES | Portal RFQ | 13 | PENDIENTE | VT-004 |
| SPEC-NOMINA-BASICA | hr_payroll | 21 | PENDIENTE | VT-001 |
| SPEC-GASTOS-EMPLEADOS | hr_expense | 13 | PENDIENTE | VT-001 |
| SPEC-TAREAS-RECURRENTES | project.task.recurrence | 13 | PENDIENTE | VT-003 |
| SPEC-SCHEDULER-REPORTES | ir.cron + mail | 8 | PENDIENTE | VT-008 |
### P1 - Complementarias
| SPEC | Gap Original | SP | Estado | Módulos Afectados |
|------|-------------|----:|--------|-------------------|
| SPEC-CONTABILIDAD-ANALITICA | Centros de costo | 21 | PENDIENTE | VT-008 |
| SPEC-CONCILIACION-BANCARIA | Conciliación | 21 | PENDIENTE | VT-008 |
| SPEC-TWO-FACTOR-AUTHENTICATION | 2FA | 13 | PENDIENTE | VT-001 |
| SPEC-TRAZABILIDAD-LOTES-SERIES | Lotes/Series | 13 | PENDIENTE | VT-004, VT-007 |
| SPEC-PRICING-RULES | Reglas precio | 8 | PENDIENTE | VT-002 |
| SPEC-BLANKET-ORDERS | Órdenes marco | 13 | PENDIENTE | VT-004 |
| SPEC-IMPUESTOS-AVANZADOS | IVA, ISR | 8 | PENDIENTE | VT-008 |
| SPEC-PLANTILLAS-CUENTAS | Plan contable | 8 | PENDIENTE | VT-008 |
| SPEC-TASAS-CAMBIO-AUTOMATICAS | Tipos cambio | 5 | PENDIENTE | VT-008 |
| SPEC-ALERTAS-PRESUPUESTO | Alertas | 8 | PENDIENTE | VT-002, VT-003 |
| SPEC-PRESUPUESTOS-REVISIONES | Aprobación | 8 | PENDIENTE | VT-002 |
| SPEC-RRHH-EVALUACIONES-SKILLS | Evaluaciones | 26 | PENDIENTE | VT-001 |
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | Burndown | 13 | PENDIENTE | VT-003 |
| SPEC-LOCALIZACION-PAISES | Localización | 13 | PENDIENTE | VT-001 |
### Patrones Técnicos
| SPEC | Patrón | SP | Estado | Aplicación |
|------|--------|----:|--------|------------|
| SPEC-MAIL-THREAD-TRACKING | mail.thread | 13 | PENDIENTE | Órdenes producción, Cotizaciones |
| SPEC-WIZARD-TRANSIENT-MODEL | TransientModel | 8 | PENDIENTE | Wizards de corte, templado |
---
## SPECS Opcionales
| SPEC | Descripción | SP | Decisión | Razón |
|------|-------------|----:|----------|-------|
| SPEC-FIRMA-ELECTRONICA-NOM151 | e.firma | 13 | EVALUAR | Para certificados de calidad |
| SPEC-OAUTH2-SOCIAL-LOGIN | OAuth2 | 8 | DIFERIR | No prioritario |
| SPEC-INVENTARIOS-CICLICOS | Conteo cíclico | 13 | EVALUAR | Útil para materia prima |
---
## SPECS No Aplicables
| SPEC | Razón |
|------|-------|
| SPEC-INTEGRACION-CALENDAR | No requiere calendario externo |
| SPEC-CONSOLIDACION-FINANCIERA | Negocio de una sola planta |
---
## Adaptaciones Requeridas
### Mapeo de Conceptos Core → Vidrio
| Concepto Core | Concepto Vidrio |
|---------------|-----------------|
| `sales.sale_orders` | Pedidos de vidrio |
| `inventory.products` | Tipos de vidrio (templado, laminado, etc.) |
| `inventory.lots` | Lotes de producción |
| `projects.projects` | Órdenes de producción |
| `projects.tasks` | Etapas (corte, templado, inspección) |
### Extensiones de Entidad
```sql
-- Tipos de vidrio
glass.glass_types (
product_id → inventory.products,
tipo ENUM('templado', 'laminado', 'insulado', 'curvo'),
espesor_mm DECIMAL,
color VARCHAR,
propiedades JSONB
)
-- Órdenes de producción
production.production_orders (
id UUID,
sale_order_id → sales.sale_orders,
tipo_vidrio_id → glass_types,
dimensiones JSONB,
cantidad INTEGER,
estado ENUM
)
-- Parámetros de horno
production.oven_parameters (
production_order_id → production_orders,
temperatura_c INTEGER,
tiempo_minutos INTEGER,
velocidad_enfriamiento DECIMAL,
fecha_templado TIMESTAMPTZ
)
-- Inspecciones de calidad
quality.inspections (
id UUID,
production_order_id → production_orders,
tipo_inspeccion ENUM,
resultado ENUM('aprobado', 'rechazado', 'condicional'),
observaciones TEXT
)
```
---
## Plan de Implementación
### Fase 1: Fundamentos (SP: 52)
1. SPEC-SISTEMA-SECUENCIAS
2. SPEC-SEGURIDAD-API-KEYS-PERMISOS
3. SPEC-TWO-FACTOR-AUTHENTICATION
### Fase 2: Producción (SP: 55)
4. SPEC-VALORACION-INVENTARIO
5. SPEC-TRAZABILIDAD-LOTES-SERIES
6. SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN
7. SPEC-PRICING-RULES
### Fase 3: Operaciones (SP: 34)
8. SPEC-MAIL-THREAD-TRACKING
9. SPEC-WIZARD-TRANSIENT-MODEL
10. SPEC-TAREAS-RECURRENTES
### Fase 4: Financiero (SP: 65)
11. SPEC-REPORTES-FINANCIEROS
12. SPEC-CONTABILIDAD-ANALITICA
13. SPEC-CONCILIACION-BANCARIA
---
## Referencias
- Documento Core: `erp-core/docs/04-modelado/MAPEO-SPECS-VERTICALES.md`
- SPECS del Core: `erp-core/docs/04-modelado/especificaciones-tecnicas/transversal/`
- Herencia DB: `database/HERENCIA-ERP-CORE.md`
- Directivas: `orchestration/directivas/`
---
**Documento de herencia de SPECS oficial**
**Última actualización:** 2025-12-08

View File

@ -0,0 +1,94 @@
# Inventarios - ERP Vidrio Templado
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Nivel SIMCO:** 2B.2
---
## Descripción
Este directorio contiene los inventarios YAML que sirven como **Single Source of Truth (SSOT)** para el proyecto ERP Vidrio Templado. Estos archivos son la referencia canónica para métricas, trazabilidad y componentes del sistema.
---
## Archivos de Inventario
| Archivo | Descripción | Estado |
|---------|-------------|--------|
| [MASTER_INVENTORY.yml](./MASTER_INVENTORY.yml) | Inventario maestro con métricas globales | Completo |
| [DATABASE_INVENTORY.yml](./DATABASE_INVENTORY.yml) | Inventario de objetos de base de datos | Planificado |
| [BACKEND_INVENTORY.yml](./BACKEND_INVENTORY.yml) | Inventario de componentes backend | Planificado |
| [FRONTEND_INVENTORY.yml](./FRONTEND_INVENTORY.yml) | Inventario de componentes frontend | Planificado |
| [TRACEABILITY_MATRIX.yml](./TRACEABILITY_MATRIX.yml) | Matriz de trazabilidad RF->ET->US | Completo |
| [DEPENDENCY_GRAPH.yml](./DEPENDENCY_GRAPH.yml) | Grafo de dependencias entre módulos | Completo |
---
## Herencia del Core
Este proyecto hereda del **ERP Core** (nivel 2B.1):
| Aspecto | Heredado | Específico |
|---------|----------|------------|
| **Tablas DB** | ~100 | Planificado |
| **Schemas** | 8+ | Planificado |
| **Specs** | 3 | - |
### Specs Heredadas
1. SPEC-VALORACION-INVENTARIO.md
2. SPEC-TRAZABILIDAD-LOTES-SERIES.md
3. SPEC-INVENTARIOS-CICLICOS.md
---
## Resumen Ejecutivo
### Métricas del Proyecto
| Métrica | Valor |
|---------|-------|
| **Módulos** | 5 (VT-001 a VT-005) |
| **Estado** | PLANIFICACION_COMPLETA |
| **Completitud** | 15% |
### Dominio del Negocio
- Producción de vidrio templado
- Control de calidad
- Trazabilidad de lotes
- Gestión de hornos y procesos
---
## Directivas Específicas
1. [DIRECTIVA-PRODUCCION-VIDRIO.md](../directivas/DIRECTIVA-PRODUCCION-VIDRIO.md)
2. [DIRECTIVA-CONTROL-CALIDAD.md](../directivas/DIRECTIVA-CONTROL-CALIDAD.md)
---
## Configuración de Puertos (Planificado)
| Servicio | Puerto |
|----------|--------|
| Backend API | 3300 |
| Frontend Web | 5176 |
---
## Alineación con ERP Core
Estos inventarios siguen la misma estructura que:
- `/erp-core/orchestration/inventarios/` (proyecto padre)
### Referencias
- Suite Master: `orchestration/inventarios/SUITE_MASTER_INVENTORY.yml`
- Core: `apps/erp-core/orchestration/inventarios/`
- Status Global: `orchestration/inventarios/STATUS.yml`
---
**Última actualización:** 2025-12-08

View File

@ -123,39 +123,114 @@ referencias_erp_core:
referencias_verticales:
construccion:
ubicacion_base: apps/verticales/construccion/
documentacion: docs/
nivel: 2B.2
estado: EN_DESARROLLO
completitud: 40%
documentacion: docs/ (449 archivos)
orchestration: orchestration/
total_archivos: 403
vidrio_templado:
ubicacion_base: apps/verticales/vidrio-templado/
documentacion: docs/
orchestration: orchestration/
estado: estructura_base
inventarios:
ubicacion: orchestration/inventarios/
archivos: 6
readme: true
database:
ubicacion: database/
herencia: database/HERENCIA-ERP-CORE.md
ddl_implementado: true
tablas_especificas: 33
backend:
porcentaje: 15%
entities: 12
services: 2
controllers: 2
directivas:
- directivas/DIRECTIVA-CONTROL-OBRA.md
- directivas/DIRECTIVA-ESTIMACIONES.md
- directivas/DIRECTIVA-INTEGRACION-INFONAVIT.md
mecanicas_diesel:
ubicacion_base: apps/verticales/mecanicas-diesel/
nivel: 2B.2
estado: DDL_IMPLEMENTADO
completitud: 20%
documentacion: docs/ (75 archivos)
orchestration: orchestration/
inventarios:
ubicacion: orchestration/inventarios/
archivos: 6
readme: true
database:
ubicacion: database/
herencia: database/HERENCIA-ERP-CORE.md
ddl_implementado: true
lineas_sql: 1561
directivas:
- directivas/DIRECTIVA-ORDENES-TRABAJO.md
- directivas/DIRECTIVA-INVENTARIO-REFACCIONES.md
vidrio_templado:
ubicacion_base: apps/verticales/vidrio-templado/
nivel: 2B.2
estado: PLANIFICACION_COMPLETA
completitud: 15%
documentacion: docs/
orchestration: orchestration/
estado: estructura_base
inventarios:
ubicacion: orchestration/inventarios/
archivos: 6
readme: true
database:
ubicacion: database/
herencia: database/HERENCIA-ERP-CORE.md
ddl_implementado: false
directivas:
- directivas/DIRECTIVA-PRODUCCION-VIDRIO.md
- directivas/DIRECTIVA-CONTROL-CALIDAD.md
retail:
ubicacion_base: apps/verticales/retail/
nivel: 2B.2
estado: PLANIFICACION_COMPLETA
completitud: 15%
documentacion: docs/
orchestration: orchestration/
estado: estructura_base
inventarios:
ubicacion: orchestration/inventarios/
archivos: 6
readme: true
database:
ubicacion: database/
herencia: database/HERENCIA-ERP-CORE.md
ddl_implementado: false
directivas:
- directivas/DIRECTIVA-PUNTO-VENTA.md
- directivas/DIRECTIVA-INVENTARIO-SUCURSALES.md
clinicas:
ubicacion_base: apps/verticales/clinicas/
nivel: 2B.2
estado: PLANIFICACION_COMPLETA
completitud: 15%
documentacion: docs/
orchestration: orchestration/
estado: estructura_base
inventarios:
ubicacion: orchestration/inventarios/
archivos: 6
readme: true
database:
ubicacion: database/
herencia: database/HERENCIA-ERP-CORE.md
ddl_implementado: false
directivas:
- directivas/DIRECTIVA-EXPEDIENTE-CLINICO.md
- directivas/DIRECTIVA-GESTION-CITAS.md
# ============================================================================
# MATRIZ DE HERENCIA (Verticales -> Core)
# ============================================================================
herencia_verticales:
descripcion: "Especificaciones del core que cada vertical debe heredar"
fecha_propagacion: 2025-12-08
estado_propagacion: COMPLETO
construccion:
specs_heredables:
@ -165,32 +240,92 @@ herencia_verticales:
- SPEC-VALORACION-INVENTARIO.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
- SPEC-TAREAS-RECURRENTES.md
documentado: false
vidrio_templado:
specs_heredables:
- SPEC-VALORACION-INVENTARIO.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
- SPEC-INVENTARIOS-CICLICOS.md
documentado: false
documentado: true
documento_herencia: apps/verticales/construccion/database/HERENCIA-ERP-CORE.md
tablas_heredadas: 124
tablas_especificas: 33
mecanicas_diesel:
specs_heredables:
- SPEC-VALORACION-INVENTARIO.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
- SPEC-INVENTARIOS-CICLICOS.md
documentado: false
- SPEC-MAIL-THREAD-TRACKING.md
- SPEC-TAREAS-RECURRENTES.md
documentado: true
documento_herencia: apps/verticales/mecanicas-diesel/database/HERENCIA-ERP-CORE.md
tablas_heredadas: 97
tablas_especificas: 30+
vidrio_templado:
specs_heredables:
- SPEC-VALORACION-INVENTARIO.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
- SPEC-INVENTARIOS-CICLICOS.md
documentado: true
documento_herencia: apps/verticales/vidrio-templado/database/HERENCIA-ERP-CORE.md
tablas_heredadas: ~97
tablas_especificas: ~25 (planificado)
retail:
specs_heredables:
- SPEC-PRICING-RULES.md
- SPEC-INVENTARIOS-CICLICOS.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
documentado: false
documentado: true
documento_herencia: apps/verticales/retail/database/HERENCIA-ERP-CORE.md
tablas_heredadas: ~102
tablas_especificas: ~30 (planificado)
clinicas:
specs_heredables:
- SPEC-RRHH-EVALUACIONES-SKILLS.md
- SPEC-INTEGRACION-CALENDAR.md
- SPEC-MAIL-THREAD-TRACKING.md
documentado: false
documentado: true
documento_herencia: apps/verticales/clinicas/database/HERENCIA-ERP-CORE.md
tablas_heredadas: ~100
tablas_especificas: ~35 (planificado)
# ============================================================================
# VALIDACION DE PROPAGACION SIMCO
# ============================================================================
validacion_propagacion:
fecha: 2025-12-08
sistema: SIMCO v2.2.0
niveles:
suite_master:
nivel: 2B
inventarios: [SUITE_MASTER_INVENTORY.yml, STATUS.yml, REFERENCIAS.yml]
estado: COMPLETO
erp_core:
nivel: 2B.1
inventarios: 6
estado: COMPLETO
carga_limpia: EXITOSA
verticales:
nivel: 2B.2
total: 5
inventarios_por_vertical: 6
readme_por_vertical: 5/5
herencia_documentada: 5/5
directivas_por_vertical: 2-3
checklist:
- item: "SUITE_MASTER_INVENTORY actualizado"
estado: true
- item: "STATUS.yml sincronizado"
estado: true
- item: "REFERENCIAS.yml completo"
estado: true
- item: "README.md en todas las verticales"
estado: true
- item: "HERENCIA-ERP-CORE.md en todas las verticales"
estado: true
- item: "Directivas específicas por vertical"
estado: true
- item: "6 inventarios por proyecto"
estado: true

View File

@ -62,7 +62,7 @@ componentes:
nivel: "2B.2"
ultima_modificacion: "2025-12-08"
estado: "EN_DESARROLLO"
completitud: "35%"
completitud: "40%"
capas:
documentacion:
estado: "AVANZADA"
@ -73,10 +73,13 @@ componentes:
tablas_especificas: 33
schemas_especificos: ["construccion", "hr", "hse"]
backend:
estado: "INICIAL"
archivos_ts: 7
entities: 2
porcentaje: "5%"
estado: "EN_PROGRESO"
archivos_ts: 25
entities: 12
services: 2
controllers: 2
porcentaje: "15%"
modulos_funcionales: ["construction"]
frontend:
estado: "INICIAL"
archivos: 3
@ -93,8 +96,9 @@ componentes:
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
- SPEC-TAREAS-RECURRENTES.md
gap_analisis:
documentacion_vs_codigo: "449 MD vs 10 código"
ratio_implementacion: "2%"
documentacion_vs_codigo: "449 MD vs 25 código"
ratio_implementacion: "5.6%"
nota: "Gap corregido - entities base implementadas"
vidrio_templado:
nivel: "2B.2"
@ -226,8 +230,8 @@ alertas:
fecha: "2025-12-08"
- componente: "construccion"
tipo: "GAP"
mensaje: "Gap significativo: 449 docs vs 10 archivos código (2% implementado)"
tipo: "EN_PROGRESO"
mensaje: "Gap corregido: 12 entities, 2 services, 2 controllers implementados"
fecha: "2025-12-08"
- componente: "mecanicas_diesel"
@ -239,6 +243,16 @@ alertas:
# HISTORIAL DE CAMBIOS RECIENTES
# ========================================
historial:
- fecha: "2025-12-08"
componente: "construccion"
cambio: "IMPLEMENTACION GAP FIX - 12 entities, 2 services, 2 controllers creados"
agente: "System"
detalles:
- "Entities: Proyecto, Fraccionamiento, Employee, Puesto, Incidente, Capacitacion"
- "Services: ProyectoService, FraccionamientoService"
- "Controllers: ProyectoController, FraccionamientoController"
- "Backend 15% implementado (25 archivos TS)"
- fecha: "2025-12-08"
componente: "erp_core"
cambio: "CARGA LIMPIA EXITOSA - 124 tablas, 12 schemas, 6 seeds"

View File

@ -1,157 +1,422 @@
# Suite Master Inventory - ERP Suite
# Ultima actualizacion: 2025-12-08
# SSOT para metricas de toda la suite (core + verticales)
# Sistema: SIMCO v2.2.0
# Nivel: 2B (Suite Master)
suite:
nombre: ERP Suite
tipo: Multi-Vertical Suite
version: 0.5.0
version: 0.6.0
nivel: 2B
estado: En Desarrollo
ultima_modificacion: 2025-12-08
# Inventarios de este nivel
inventarios_suite:
- SUITE_MASTER_INVENTORY.yml # Este archivo
- STATUS.yml # Estado de componentes
- REFERENCIAS.yml # Referencias cruzadas
- BACKEND_CONSOLIDATED.yml # Consolidado backend (referencia)
- FRONTEND_CONSOLIDATED.yml # Consolidado frontend (referencia)
- DEPENDENCY_SUITE.yml # Dependencias inter-proyecto
# ============================================================================
# ERP CORE (Nivel 2B.1)
# ERP CORE (Nivel 2B.1) - PROYECTO PADRE
# ============================================================================
erp_core:
path: apps/erp-core/
estado: Gap Analysis COMPLETO
version: 0.6.0
nivel: 2B.1
estado: DATABASE_COMPLETO
version: 1.1.0
ultima_modificacion: 2025-12-08
# Estado de capas
capas:
documentacion:
estado: COMPLETA
archivos: 680+
especificaciones: 30
workflows: 3
database:
estado: VALIDADO
tablas: 124
schemas: 12
ddl_archivos: 15
carga_limpia: EXITOSA
fecha_validacion: 2025-12-08
backend:
estado: PENDIENTE
endpoints_especificados: 148
services_especificados: 45+
frontend:
estado: PENDIENTE
componentes_especificados: 80+
# Inventarios del core (6 archivos estándar)
inventarios:
- MASTER_INVENTORY.yml
- DATABASE_INVENTORY.yml
- BACKEND_INVENTORY.yml
- FRONTEND_INVENTORY.yml
- DEPENDENCY_GRAPH.yml
- TRACEABILITY_MATRIX.yml
ubicacion: apps/erp-core/orchestration/inventarios/
# Métricas de documentación
metricas:
modulos_totales: 15
modulos_p0: 4
modulos_p1: 6
modulos_p2: 5
gap_analysis_cobertura: "100%"
story_points_cubiertos: 394
documentacion:
total_archivos: 680+
especificaciones_transversales: 30
workflows: 3
requerimientos_funcionales: 46
user_stories: 17
test_plans: 4
gap_analysis:
gaps_p0_documentados: 18
gaps_p1_documentados: 22
patrones_tecnicos: 2
cobertura: "100%"
story_points_cubiertos: 394
especificaciones_transversales:
# Especificaciones que heredan las verticales
especificaciones_heredables:
ubicacion: docs/04-modelado/especificaciones-tecnicas/transversal/
total: 30
referencia: apps/erp-core/orchestration/inventarios/MASTER_INVENTORY.yml
lista_principales:
- SPEC-VALORACION-INVENTARIO.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
- SPEC-INVENTARIOS-CICLICOS.md
- SPEC-MAIL-THREAD-TRACKING.md
- SPEC-TAREAS-RECURRENTES.md
- SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN.md
- SPEC-INTEGRACION-CALENDAR.md
- SPEC-PRICING-RULES.md
- SPEC-RRHH-EVALUACIONES-SKILLS.md
inventario_local: apps/erp-core/orchestration/inventarios/MASTER_INVENTORY.yml
analisis_gaps: apps/erp-core/orchestration/01-analisis/ANALISIS-GAPS-CONSOLIDADO.md
referencias:
inventario_local: apps/erp-core/orchestration/inventarios/MASTER_INVENTORY.yml
analisis_gaps: apps/erp-core/orchestration/01-analisis/ANALISIS-GAPS-CONSOLIDADO.md
database_readme: apps/erp-core/database/README.md
# ============================================================================
# VERTICALES (Nivel 2B.2)
# VERTICALES (Nivel 2B.2) - PROYECTOS HIJOS
# ============================================================================
verticales:
total: 5
en_desarrollo: 1
planificacion: 4
ddl_implementado: 1
planificacion: 3
# Inventarios estándar que debe tener cada vertical
inventarios_requeridos:
- MASTER_INVENTORY.yml
- DATABASE_INVENTORY.yml
- BACKEND_INVENTORY.yml
- FRONTEND_INVENTORY.yml
- DEPENDENCY_GRAPH.yml
- TRACEABILITY_MATRIX.yml
lista:
# -------------------------------------------------------------------------
# CONSTRUCCION - Vertical más avanzada
# -------------------------------------------------------------------------
- nombre: construccion
path: apps/verticales/construccion/
estado: En Desarrollo
completitud: 35%
documentacion: 403+ archivos
modulos: 15 (MAI-001 a MAI-018)
epicas_fase2: 3 (MAE-014 a MAE-016)
ultima_modificacion: 2025-12-05
herencia_core:
- SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN.md
- SPEC-MAIL-THREAD-TRACKING.md
- SPEC-WIZARD-TRANSIENT-MODEL.md
- SPEC-VALORACION-INVENTARIO.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
nivel: 2B.2
estado: EN_DESARROLLO
completitud: 40%
ultima_modificacion: 2025-12-08
- nombre: vidrio-templado
path: apps/verticales/vidrio-templado/
estado: Planificacion
completitud: 0%
documentacion: Estructura base
ultima_modificacion: 2025-12-05
herencia_core:
- SPEC-VALORACION-INVENTARIO.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
- SPEC-INVENTARIOS-CICLICOS.md
capas:
documentacion:
estado: AVANZADA
archivos: 449
database:
estado: DDL_COMPLETO
tablas_heredadas: 124
tablas_especificas: 33
schemas: [construccion, hr, hse]
backend:
estado: EN_PROGRESO
porcentaje: 15%
entities: 12
services: 2
controllers: 2
frontend:
estado: INICIAL
porcentaje: 2%
herencia_core:
specs_heredadas: 6
documento: database/HERENCIA-ERP-CORE.md
lista:
- SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN.md
- SPEC-MAIL-THREAD-TRACKING.md
- SPEC-WIZARD-TRANSIENT-MODEL.md
- SPEC-VALORACION-INVENTARIO.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
- SPEC-TAREAS-RECURRENTES.md
directivas_especificas:
- DIRECTIVA-CONTROL-OBRA.md
- DIRECTIVA-ESTIMACIONES.md
- DIRECTIVA-INTEGRACION-INFONAVIT.md
inventarios_ubicacion: orchestration/inventarios/
# -------------------------------------------------------------------------
# MECANICAS-DIESEL
# -------------------------------------------------------------------------
- nombre: mecanicas-diesel
path: apps/verticales/mecanicas-diesel/
estado: Planificacion
completitud: 0%
documentacion: Estructura base
ultima_modificacion: 2025-12-05
herencia_core:
- SPEC-VALORACION-INVENTARIO.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
- SPEC-INVENTARIOS-CICLICOS.md
nivel: 2B.2
estado: DDL_IMPLEMENTADO
completitud: 20%
ultima_modificacion: 2025-12-08
capas:
documentacion:
estado: COMPLETA
archivos: 75
database:
estado: DDL_DEFINIDO
tablas_heredadas: 97
tablas_especificas: 30+
schemas: [service_management, parts_management, vehicle_management]
lineas_sql: 1561
backend:
estado: PENDIENTE
porcentaje: 0%
frontend:
estado: PENDIENTE
porcentaje: 0%
herencia_core:
specs_heredadas: 5
documento: database/HERENCIA-ERP-CORE.md
lista:
- SPEC-VALORACION-INVENTARIO.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
- SPEC-INVENTARIOS-CICLICOS.md
- SPEC-MAIL-THREAD-TRACKING.md
- SPEC-TAREAS-RECURRENTES.md
directivas_especificas:
- DIRECTIVA-ORDENES-TRABAJO.md
- DIRECTIVA-INVENTARIO-REFACCIONES.md
inventarios_ubicacion: orchestration/inventarios/
# -------------------------------------------------------------------------
# VIDRIO-TEMPLADO
# -------------------------------------------------------------------------
- nombre: vidrio-templado
path: apps/verticales/vidrio-templado/
nivel: 2B.2
estado: PLANIFICACION_COMPLETA
completitud: 15%
ultima_modificacion: 2025-12-08
capas:
documentacion:
estado: ESTRUCTURA_BASE
database:
estado: PLANIFICADO
backend:
estado: PENDIENTE
frontend:
estado: PENDIENTE
herencia_core:
specs_heredadas: 3
lista:
- SPEC-VALORACION-INVENTARIO.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
- SPEC-INVENTARIOS-CICLICOS.md
directivas_especificas:
- DIRECTIVA-PRODUCCION-VIDRIO.md
- DIRECTIVA-CONTROL-CALIDAD.md
inventarios_ubicacion: orchestration/inventarios/
# -------------------------------------------------------------------------
# RETAIL
# -------------------------------------------------------------------------
- nombre: retail
path: apps/verticales/retail/
estado: Planificacion
completitud: 0%
documentacion: Estructura base
ultima_modificacion: 2025-12-05
herencia_core:
- SPEC-PRICING-RULES.md
- SPEC-INVENTARIOS-CICLICOS.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
nivel: 2B.2
estado: PLANIFICACION_COMPLETA
completitud: 15%
ultima_modificacion: 2025-12-08
capas:
documentacion:
estado: ESTRUCTURA_BASE
database:
estado: PLANIFICADO
backend:
estado: PENDIENTE
frontend:
estado: PENDIENTE
herencia_core:
specs_heredadas: 3
lista:
- SPEC-PRICING-RULES.md
- SPEC-INVENTARIOS-CICLICOS.md
- SPEC-TRAZABILIDAD-LOTES-SERIES.md
directivas_especificas:
- DIRECTIVA-PUNTO-VENTA.md
- DIRECTIVA-INVENTARIO-SUCURSALES.md
inventarios_ubicacion: orchestration/inventarios/
# -------------------------------------------------------------------------
# CLINICAS
# -------------------------------------------------------------------------
- nombre: clinicas
path: apps/verticales/clinicas/
estado: Planificacion
completitud: 0%
documentacion: Estructura base
ultima_modificacion: 2025-12-05
nivel: 2B.2
estado: PLANIFICACION_COMPLETA
completitud: 15%
ultima_modificacion: 2025-12-08
capas:
documentacion:
estado: ESTRUCTURA_BASE
database:
estado: PLANIFICADO
backend:
estado: PENDIENTE
frontend:
estado: PENDIENTE
herencia_core:
- SPEC-RRHH-EVALUACIONES-SKILLS.md
- SPEC-INTEGRACION-CALENDAR.md
- SPEC-MAIL-THREAD-TRACKING.md
specs_heredadas: 3
lista:
- SPEC-RRHH-EVALUACIONES-SKILLS.md
- SPEC-INTEGRACION-CALENDAR.md
- SPEC-MAIL-THREAD-TRACKING.md
directivas_especificas:
- DIRECTIVA-EXPEDIENTE-CLINICO.md
- DIRECTIVA-GESTION-CITAS.md
inventarios_ubicacion: orchestration/inventarios/
# ============================================================================
# SHARED LIBS
# SHARED LIBS (Futuro)
# ============================================================================
shared_libs:
path: apps/shared-libs/
estado: Planificado
documentacion: Pendiente
nivel: 2B.3
estado: PLANIFICADO
documentacion: PENDIENTE
proposito: "Componentes compartidos entre verticales"
# ============================================================================
# SAAS LAYER
# SAAS LAYER (Futuro)
# ============================================================================
saas:
path: apps/saas/
estado: Planificado
documentacion: Pendiente
nivel: 2B.4
estado: PLANIFICADO
documentacion: PENDIENTE
proposito: "Capa de servicios multi-tenant cloud"
# ============================================================================
# METRICAS CONSOLIDADAS
# METRICAS CONSOLIDADAS DE LA SUITE
# ============================================================================
metricas_suite:
total_archivos_docs: 1100+
modulos_core: 15
verticales: 5
story_points_documentados: 734
cobertura_gaps_core: 100%
fecha_actualizacion: 2025-12-08
documentacion:
total_archivos: 1200+
core: 680+
verticales: 520+
database:
tablas_core: 124
schemas_core: 12
verticales_con_ddl: 2
tablas_especificas_total: 63+ # construccion(33) + mecanicas(30+)
backend:
core_implementado: 0%
construccion_implementado: 15%
entities_totales: 12
services_totales: 2
controllers_totales: 2
frontend:
core_implementado: 0%
construccion_implementado: 2%
cobertura:
gap_analysis_core: "100%"
story_points_cubiertos: 734
specs_transversales: 30
workflows: 3
# ============================================================================
# PROXIMA ACCION SUITE
# PROPAGACION SIMCO
# ============================================================================
proxima_accion:
prioridad: ALTA
descripcion: Implementar modulos P0 del core
modulos:
- MGN-001 Auth
- MGN-002 Users
- MGN-003 Roles
- MGN-004 Tenants
specs_disponibles: 30
workflows_disponibles: 3
propagacion:
sistema: SIMCO v2.2.0
niveles:
- nivel: 2B
nombre: Suite Master
ubicacion: orchestration/inventarios/
archivos: [SUITE_MASTER_INVENTORY.yml, STATUS.yml, REFERENCIAS.yml]
- nivel: 2B.1
nombre: ERP Core
ubicacion: apps/erp-core/orchestration/inventarios/
archivos: 6 # Inventarios estándar
- nivel: 2B.2
nombre: Verticales
ubicacion: apps/verticales/*/orchestration/inventarios/
archivos: 6 # Inventarios estándar por vertical
verticales: 5
herencia:
direccion: "Core -> Verticales"
documento_base: "HERENCIA-ERP-CORE.md"
specs_heredables: 30
propagacion_completada: true
fecha_propagacion: 2025-12-08
validacion:
inventarios_completos: true
directivas_propagadas: true
herencia_documentada: true
status_sincronizado: true
# ============================================================================
# PROXIMAS ACCIONES SUITE
# ============================================================================
proximas_acciones:
prioridad_1:
descripcion: "Completar backend construcción"
modulos: [MAI-001, MAI-002, MAI-003]
porcentaje_actual: 15%
porcentaje_objetivo: 50%
prioridad_2:
descripcion: "Cargar DDL mecanicas-diesel"
prerequisito: "Validar DDL contra core"
estado: PENDIENTE
prioridad_3:
descripcion: "Iniciar DDL para verticales restantes"
verticales: [vidrio-templado, retail, clinicas]
# ============================================================================
# REFERENCIAS CRUZADAS
# ============================================================================
referencias:
status_global: orchestration/inventarios/STATUS.yml
referencias_herencia: orchestration/inventarios/REFERENCIAS.yml
core_master: apps/erp-core/orchestration/inventarios/MASTER_INVENTORY.yml
core_database: apps/erp-core/database/README.md
guidelines: orchestration/00-guidelines/

View File

@ -0,0 +1,967 @@
# Arquitectura T茅cnica - Platform Marketing Content
**Versi贸n:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Propuesta
---
## 1. Visi贸n de Arquitectura
### 1.1 Principios Arquitect贸nicos
```yaml
Principios:
1. API-First: Todo servicio expuesto via REST/GraphQL
2. Modular: Componentes desacoplados por dominio
3. Multi-tenant Ready: Aislamiento de datos por tenant
4. Open Source First: Priorizar modelos auto-hosteados
5. Escalabilidad Horizontal: Preparado para m煤ltiples GPUs
6. Event-Driven: Comunicaci贸n as铆ncrona entre servicios
```
### 1.2 Diagrama de Arquitectura de Alto Nivel
```
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁? CAPA DE PRESENTACI脫N 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁? 鉁? Web App Admin 鉁? 鉁? Portal Cliente 鉁? 鉁?
鉁? 鉁? (React + Vite) 鉁? 鉁? (Opcional) 鉁? 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁?
鈻?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁? API GATEWAY 鉁?
鉁? (NestJS + JWT Auth) 鉁?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁? 鉁? 鉁? 鉁?
鈻? 鈻? 鈻? 鈻?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁? CAPA DE SERVICIOS 鉁?
鉁? 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁? 鉁? CRM 鉁? 鉁? Projects 鉁? 鉁? Assets 鉁? 鉁? Auth 鉁? 鉁?
鉁? 鉁? Service 鉁? 鉁? Service 鉁? 鉁? Service 鉁? 鉁? Service 鉁? 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁? 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁? 鉁? Generation 鉁? 鉁? Automation 鉁? 鉁?
鉁? 鉁? Service 鉁? 鉁? Service 鉁? 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁? 鉁? 鉁? 鉁?
鈻? 鈻? 鈻? 鈻?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁? CAPA DE INFRAESTRUCTURA 鉁?
鉁? 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁? 鉁? PostgreSQL 鉁? 鉁? Redis 鉁? 鉁? S3/MinIO 鉁? 鉁? ComfyUI 鉁? 鉁?
鉁? 鉁? 15+ 鉁? 鉁? (Cache) 鉁? 鉁? (Storage)鉁? 鉁? (GPU) 鉁? 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁? 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁? 鉁? n8n 鉁? 鉁? Bull/BullMQ 鉁? 鉁?
鉁? 鉁? (Workflows) 鉁? 鉁? (Job Queues) 鉁? 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
```
---
## 2. Stack Tecnol贸gico Detallado
### 2.1 Backend
```yaml
Framework: NestJS 10+
Runtime: Node.js 20 LTS
Lenguaje: TypeScript 5.x
Dependencias Core:
- @nestjs/core
- @nestjs/platform-express
- @nestjs/typeorm
- @nestjs/jwt
- @nestjs/passport
- @nestjs/bull
- class-validator
- class-transformer
ORM/Database:
- TypeORM 0.3+
- pg (PostgreSQL driver)
Queues:
- bull / bullmq
- @nestjs/bull
Caching:
- ioredis
- @nestjs/cache-manager
HTTP Client:
- axios (llamadas a ComfyUI)
File Storage:
- @aws-sdk/client-s3 (o minio compatible)
Logging:
- winston
- @nestjs/winston
```
### 2.2 Frontend
```yaml
Framework: React 18
Build Tool: Vite 5+
Lenguaje: TypeScript 5.x
UI Framework:
- TailwindCSS 3.x
- Shadcn/UI (componentes)
- Radix UI (primitivos)
State Management:
- Zustand (global state)
- React Query / TanStack Query (server state)
Routing:
- React Router 6
Forms:
- React Hook Form
- Zod (validaci贸n)
Charts/Visualizaci贸n:
- Recharts
- Apache ECharts (opcional)
Utilities:
- date-fns
- lodash-es
```
### 2.3 Motor de Generaci贸n IA
```yaml
Orquestador Principal: ComfyUI
- Interfaz de nodos para workflows
- Soporte para m煤ltiples modelos
- Custom nodes extensibles
Exposici贸n API: ComfyDeploy
- Convierte workflows en APIs HTTP
- Manejo de colas integrado
- Webhooks para resultados
Modelos Base:
Text-to-Image:
- Stable Diffusion XL 1.0
- SD 1.5 / 2.1 (fallback para menos VRAM)
Checkpoints Especializados:
- Realistic Vision (rostros)
- Product Photography (e-commerce)
- Juggernaut XL (general alta calidad)
ControlNets:
- OpenPose (control de poses)
- Canny (bordes)
- Depth (profundidad)
- Segmentation (m谩scaras)
Upscalers:
- RealESRGAN x4
- SwinIR
- SDXL refiner
Inpainting:
- SDXL Inpaint model
- SD 1.5 Inpaint (fallback)
Requisitos Hardware:
M铆nimo: NVIDIA GPU 12GB VRAM (RTX 3060 12GB)
Recomendado: NVIDIA GPU 24GB VRAM (RTX 4090, L4, A5000)
脫ptimo: M煤ltiples GPUs para colas paralelas
```
### 2.4 Base de Datos
```yaml
Motor: PostgreSQL 15+
Schemas:
- auth: Usuarios, sesiones, permisos
- crm: Clientes, contactos, marcas, productos
- projects: Proyectos, campa帽as, briefs
- assets: Im谩genes, copys, metadatos
- generation: Jobs, workflows, resultados
- config: Configuraci贸n por tenant
Extensiones:
- uuid-ossp (UUIDs)
- pgcrypto (encriptaci贸n)
- pg_trgm (b煤squeda fuzzy)
Estrategia Multi-tenant:
- Discriminador: tenant_id en todas las tablas
- Row-Level Security (RLS) por tenant
- Contexto de sesi贸n: app.current_tenant_id
```
### 2.5 Automatizaci贸n
```yaml
Orquestador: n8n (self-hosted)
Triggers Soportados:
- Webhooks desde backend
- Cron / schedules
- Eventos de base de datos
Integraciones:
- HTTP/REST (cualquier API)
- PostgreSQL queries
- S3/MinIO operations
- Email (SMTP)
- Slack/Discord (opcional)
```
---
## 3. Estructura de M贸dulos Backend
### 3.1 Organizaci贸n de C贸digo
```
apps/backend/src/
鉁斺攢鉁? modules/
鉁? 鉁斺攢鉁? auth/
鉁? 鉁? 鉁斺攢鉁? controllers/
鉁? 鉁? 鉁斺攢鉁? services/
鉁? 鉁? 鉁斺攢鉁? entities/
鉁? 鉁? 鉁斺攢鉁? dto/
鉁? 鉁? 鉁斺攢鉁? guards/
鉁? 鉁? 鉁斺攢鉁? strategies/
鉁? 鉁? 鉁斺攢鉁? auth.module.ts
鉁? 鉁?
鉁? 鉁斺攢鉁? crm/
鉁? 鉁? 鉁斺攢鉁? controllers/
鉁? 鉁? 鉁? 鉁斺攢鉁? clients.controller.ts
鉁? 鉁? 鉁? 鉁斺攢鉁? brands.controller.ts
鉁? 鉁? 鉁? 鉁斺攢鉁? products.controller.ts
鉁? 鉁? 鉁? 鉁斺攢鉁? contacts.controller.ts
鉁? 鉁? 鉁斺攢鉁? services/
鉁? 鉁? 鉁斺攢鉁? entities/
鉁? 鉁? 鉁斺攢鉁? dto/
鉁? 鉁? 鉁斺攢鉁? crm.module.ts
鉁? 鉁?
鉁? 鉁斺攢鉁? projects/
鉁? 鉁? 鉁斺攢鉁? controllers/
鉁? 鉁? 鉁? 鉁斺攢鉁? projects.controller.ts
鉁? 鉁? 鉁? 鉁斺攢鉁? campaigns.controller.ts
鉁? 鉁? 鉁? 鉁斺攢鉁? briefs.controller.ts
鉁? 鉁? 鉁斺攢鉁? services/
鉁? 鉁? 鉁斺攢鉁? entities/
鉁? 鉁? 鉁斺攢鉁? dto/
鉁? 鉁? 鉁斺攢鉁? projects.module.ts
鉁? 鉁?
鉁? 鉁斺攢鉁? generation/
鉁? 鉁? 鉁斺攢鉁? controllers/
鉁? 鉁? 鉁? 鉁斺攢鉁? generation.controller.ts
鉁? 鉁? 鉁? 鉁斺攢鉁? workflows.controller.ts
鉁? 鉁? 鉁斺攢鉁? services/
鉁? 鉁? 鉁? 鉁斺攢鉁? generation.service.ts
鉁? 鉁? 鉁? 鉁斺攢鉁? comfyui.service.ts
鉁? 鉁? 鉁? 鉁斺攢鉁? llm.service.ts
鉁? 鉁? 鉁斺攢鉁? processors/
鉁? 鉁? 鉁? 鉁斺攢鉁? generation.processor.ts
鉁? 鉁? 鉁斺攢鉁? entities/
鉁? 鉁? 鉁斺攢鉁? dto/
鉁? 鉁? 鉁斺攢鉁? generation.module.ts
鉁? 鉁?
鉁? 鉁斺攢鉁? assets/
鉁? 鉁? 鉁斺攢鉁? controllers/
鉁? 鉁? 鉁斺攢鉁? services/
鉁? 鉁? 鉁斺攢鉁? entities/
鉁? 鉁? 鉁斺攢鉁? dto/
鉁? 鉁? 鉁斺攢鉁? assets.module.ts
鉁? 鉁?
鉁? 鉁斺攢鉁? admin/
鉁? 鉁斺攢鉁? controllers/
鉁? 鉁斺攢鉁? services/
鉁? 鉁斺攢鉁? admin.module.ts
鉁?
鉁斺攢鉁? shared/
鉁? 鉁斺攢鉁? decorators/
鉁? 鉁斺攢鉁? guards/
鉁? 鉁? 鉁斺攢鉁? tenant.guard.ts
鉁? 鉁? 鉁斺攢鉁? roles.guard.ts
鉁? 鉁斺攢鉁? interceptors/
鉁? 鉁? 鉁斺攢鉁? tenant-context.interceptor.ts
鉁? 鉁斺攢鉁? filters/
鉁? 鉁斺攢鉁? pipes/
鉁? 鉁斺攢鉁? utils/
鉁?
鉁斺攢鉁? config/
鉁? 鉁斺攢鉁? database.config.ts
鉁? 鉁斺攢鉁? redis.config.ts
鉁? 鉁斺攢鉁? storage.config.ts
鉁? 鉁斺攢鉁? comfyui.config.ts
鉁?
鉁斺攢鉁? app.module.ts
鉁斺攢鉁? main.ts
```
### 3.2 API Endpoints (Borrador)
```yaml
Auth:
POST /api/v1/auth/login
POST /api/v1/auth/register
POST /api/v1/auth/refresh
POST /api/v1/auth/logout
GET /api/v1/auth/me
CRM - Clients:
GET /api/v1/crm/clients
POST /api/v1/crm/clients
GET /api/v1/crm/clients/:id
PATCH /api/v1/crm/clients/:id
DELETE /api/v1/crm/clients/:id
CRM - Brands:
GET /api/v1/crm/brands
POST /api/v1/crm/brands
GET /api/v1/crm/brands/:id
PATCH /api/v1/crm/brands/:id
GET /api/v1/crm/brands/:id/products
CRM - Products:
GET /api/v1/crm/products
POST /api/v1/crm/products
GET /api/v1/crm/products/:id
PATCH /api/v1/crm/products/:id
Projects:
GET /api/v1/projects
POST /api/v1/projects
GET /api/v1/projects/:id
PATCH /api/v1/projects/:id
GET /api/v1/projects/:id/campaigns
GET /api/v1/projects/:id/assets
Campaigns:
GET /api/v1/campaigns
POST /api/v1/campaigns
GET /api/v1/campaigns/:id
PATCH /api/v1/campaigns/:id
POST /api/v1/campaigns/:id/generate
GET /api/v1/campaigns/:id/assets
Generation:
POST /api/v1/generation/image
POST /api/v1/generation/copy
GET /api/v1/generation/jobs/:id
GET /api/v1/generation/workflows
POST /api/v1/generation/workflows/:id/execute
Assets:
GET /api/v1/assets
GET /api/v1/assets/:id
PATCH /api/v1/assets/:id
DELETE /api/v1/assets/:id
POST /api/v1/assets/:id/approve
GET /api/v1/assets/:id/download
Admin:
GET /api/v1/admin/users
POST /api/v1/admin/users
GET /api/v1/admin/tenants
GET /api/v1/admin/metrics
```
---
## 4. Modelo de Datos (Entidades Principales)
### 4.1 Schema: auth
```sql
-- Tabla: auth.users
CREATE TABLE auth.users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES config.tenants(id),
email VARCHAR(255) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
first_name VARCHAR(100),
last_name VARCHAR(100),
role VARCHAR(50) NOT NULL DEFAULT 'user',
status VARCHAR(20) NOT NULL DEFAULT 'active',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(tenant_id, email)
);
-- Tabla: auth.sessions
CREATE TABLE auth.sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id),
refresh_token VARCHAR(500) NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
ip_address VARCHAR(45),
user_agent TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
### 4.2 Schema: crm
```sql
-- Tabla: crm.clients
CREATE TABLE crm.clients (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES config.tenants(id),
name VARCHAR(255) NOT NULL,
industry VARCHAR(100),
status VARCHAR(20) NOT NULL DEFAULT 'active',
notes TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Tabla: crm.brands
CREATE TABLE crm.brands (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES config.tenants(id),
client_id UUID NOT NULL REFERENCES crm.clients(id),
name VARCHAR(255) NOT NULL,
description TEXT,
tone_of_voice VARCHAR(100),
color_palette JSONB,
restrictions JSONB,
logo_url VARCHAR(500),
lora_model_id UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Tabla: crm.products
CREATE TABLE crm.products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES config.tenants(id),
brand_id UUID NOT NULL REFERENCES crm.brands(id),
name VARCHAR(255) NOT NULL,
description TEXT,
category VARCHAR(100),
reference_images JSONB,
lora_model_id UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Tabla: crm.contacts
CREATE TABLE crm.contacts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES config.tenants(id),
client_id UUID REFERENCES crm.clients(id),
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100),
email VARCHAR(255),
phone VARCHAR(50),
position VARCHAR(100),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
### 4.3 Schema: projects
```sql
-- Tabla: projects.projects
CREATE TABLE projects.projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES config.tenants(id),
client_id UUID REFERENCES crm.clients(id),
name VARCHAR(255) NOT NULL,
description TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'draft',
start_date DATE,
end_date DATE,
created_by UUID REFERENCES auth.users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Tabla: projects.campaigns
CREATE TABLE projects.campaigns (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES config.tenants(id),
project_id UUID NOT NULL REFERENCES projects.projects(id),
brand_id UUID REFERENCES crm.brands(id),
name VARCHAR(255) NOT NULL,
type VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'draft',
channels JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Tabla: projects.briefs
CREATE TABLE projects.briefs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES config.tenants(id),
campaign_id UUID NOT NULL REFERENCES projects.campaigns(id),
objective TEXT NOT NULL,
target_audience TEXT,
tone_of_voice VARCHAR(100),
key_messages JSONB,
restrictions JSONB,
reference_images JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
### 4.4 Schema: assets
```sql
-- Tabla: assets.assets
CREATE TABLE assets.assets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES config.tenants(id),
campaign_id UUID REFERENCES projects.campaigns(id),
type VARCHAR(20) NOT NULL,
name VARCHAR(255),
status VARCHAR(20) NOT NULL DEFAULT 'draft',
file_url VARCHAR(500),
file_size INTEGER,
mime_type VARCHAR(100),
width INTEGER,
height INTEGER,
metadata JSONB,
version INTEGER NOT NULL DEFAULT 1,
parent_id UUID REFERENCES assets.assets(id),
created_by UUID REFERENCES auth.users(id),
approved_by UUID REFERENCES auth.users(id),
approved_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Tabla: assets.tags
CREATE TABLE assets.tags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES config.tenants(id),
name VARCHAR(100) NOT NULL,
UNIQUE(tenant_id, name)
);
-- Tabla: assets.asset_tags
CREATE TABLE assets.asset_tags (
asset_id UUID NOT NULL REFERENCES assets.assets(id),
tag_id UUID NOT NULL REFERENCES assets.tags(id),
PRIMARY KEY (asset_id, tag_id)
);
```
### 4.5 Schema: generation
```sql
-- Tabla: generation.jobs
CREATE TABLE generation.jobs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES config.tenants(id),
campaign_id UUID REFERENCES projects.campaigns(id),
workflow_id VARCHAR(100) NOT NULL,
type VARCHAR(20) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
priority INTEGER NOT NULL DEFAULT 0,
input_params JSONB NOT NULL,
result JSONB,
error_message TEXT,
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
created_by UUID REFERENCES auth.users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Tabla: generation.workflows
CREATE TABLE generation.workflows (
id VARCHAR(100) PRIMARY KEY,
tenant_id UUID REFERENCES config.tenants(id),
name VARCHAR(255) NOT NULL,
description TEXT,
type VARCHAR(50) NOT NULL,
comfyui_workflow JSONB NOT NULL,
input_schema JSONB,
is_system BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Tabla: generation.models
CREATE TABLE generation.models (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID REFERENCES config.tenants(id),
name VARCHAR(255) NOT NULL,
type VARCHAR(50) NOT NULL,
file_path VARCHAR(500),
description TEXT,
training_images JSONB,
is_system BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
---
## 5. Flujo de Generaci贸n de Contenido
### 5.1 Secuencia de Generaci贸n de Imagen
```
1. Usuario solicita generaci贸n
鈻?
Frontend 鈫? POST /api/v1/generation/image
{
"campaign_id": "uuid",
"workflow_id": "product_photography",
"params": {
"prompt": "...",
"product_id": "uuid",
"style": "commercial"
}
}
2. Backend crea Job
鈻?
GenerationService.createJob()
鈫? Valida permisos
鈫? Crea registro en generation.jobs
鈫? Encola job en Bull queue
3. Worker procesa Job
鈻?
GenerationProcessor.process()
鈫? Obtiene workflow de ComfyUI
鈫? Inyecta par谩metros (prompt, LoRA, etc.)
鈫? Env铆a a ComfyUI API
鈫? Espera resultado (webhook o polling)
4. ComfyUI ejecuta workflow
鈻?
鈫? Carga modelo SDXL
鈫? Aplica LoRAs (si hay)
鈫? Ejecuta pipeline de difusi贸n
鈫? Aplica upscaling
鈫? Retorna imagen
5. Worker procesa resultado
鈻?
鈫? Descarga imagen de ComfyUI
鈫? Sube a S3/MinIO
鈫? Crea registro en assets.assets
鈫? Actualiza job como completado
鈫? Emite evento (webhook a n8n)
6. Frontend recibe notificaci贸n
鈻?
WebSocket / Polling 鈫? Job completado
鈫? Muestra imagen al usuario
```
### 5.2 Integraci贸n con ComfyUI
```typescript
// services/comfyui.service.ts
interface ComfyUIWorkflowParams {
prompt: string;
negativePrompt?: string;
width?: number;
height?: number;
steps?: number;
cfg?: number;
seed?: number;
loraModels?: string[];
controlNet?: {
type: string;
image: string;
strength: number;
};
}
@Injectable()
export class ComfyUIService {
constructor(
private httpService: HttpService,
@Inject('COMFYUI_CONFIG') private config: ComfyUIConfig,
) {}
async executeWorkflow(
workflowId: string,
params: ComfyUIWorkflowParams,
): Promise<string> {
// 1. Cargar workflow template
const workflow = await this.getWorkflowTemplate(workflowId);
// 2. Inyectar par谩metros
const modifiedWorkflow = this.injectParams(workflow, params);
// 3. Enviar a ComfyUI
const response = await this.httpService.post(
`${this.config.baseUrl}/prompt`,
{ prompt: modifiedWorkflow },
).toPromise();
return response.data.prompt_id;
}
async getResult(promptId: string): Promise<Buffer> {
// Polling o webhook para obtener resultado
const history = await this.httpService.get(
`${this.config.baseUrl}/history/${promptId}`,
).toPromise();
const outputNode = this.findOutputNode(history.data);
const imageUrl = `${this.config.baseUrl}/view?filename=${outputNode.filename}`;
const imageResponse = await this.httpService.get(imageUrl, {
responseType: 'arraybuffer',
}).toPromise();
return Buffer.from(imageResponse.data);
}
}
```
---
## 6. Seguridad
### 6.1 Autenticaci贸n
```yaml
M茅todo: JWT (JSON Web Tokens)
Access Token:
- Expiraci贸n: 15 minutos
- Payload: userId, tenantId, role
- Almacenamiento: Memory (frontend)
Refresh Token:
- Expiraci贸n: 7 d铆as
- Almacenamiento: HTTP-only cookie
- Rotaci贸n en cada uso
```
### 6.2 Autorizaci贸n (RBAC)
```yaml
Roles:
super_admin:
- Acceso total a todos los tenants
- Configuraci贸n global
- Gesti贸n de modelos del sistema
admin:
- Gesti贸n de usuarios del tenant
- Configuraci贸n del tenant
- Aprobaci贸n de workflows
manager:
- CRUD completo de proyectos/campa帽as
- Aprobaci贸n de assets
- Acceso a anal铆ticas
creative:
- Crear proyectos y campa帽as
- Generar contenido
- Editar assets propios
viewer:
- Solo lectura
- Descargar assets aprobados
```
### 6.3 Multi-tenancy
```sql
-- Pol铆tica RLS ejemplo
CREATE POLICY "crm_clients_tenant_isolation" ON crm.clients
FOR ALL
TO authenticated
USING (tenant_id::text = current_setting('app.current_tenant_id', true))
WITH CHECK (tenant_id::text = current_setting('app.current_tenant_id', true));
```
---
## 7. Despliegue
### 7.1 Docker Compose (Desarrollo/Staging)
```yaml
version: '3.8'
services:
backend:
build: ./apps/backend
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://...
- REDIS_URL=redis://redis:6379
- COMFYUI_URL=http://comfyui:8188
- S3_ENDPOINT=http://minio:9000
depends_on:
- postgres
- redis
- minio
frontend:
build: ./apps/frontend
ports:
- "5173:80"
depends_on:
- backend
postgres:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=pmc
- POSTGRES_USER=pmc
- POSTGRES_PASSWORD=${DB_PASSWORD}
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
minio:
image: minio/minio
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_data:/data
command: server /data --console-address ":9001"
comfyui:
image: comfyui/comfyui:latest
ports:
- "8188:8188"
volumes:
- comfyui_models:/models
- comfyui_output:/output
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
n8n:
image: n8nio/n8n
ports:
- "5678:5678"
volumes:
- n8n_data:/home/node/.n8n
volumes:
postgres_data:
redis_data:
minio_data:
comfyui_models:
comfyui_output:
n8n_data:
```
### 7.2 Requisitos de Hardware (Producci贸n)
```yaml
Backend + Frontend:
CPU: 4 cores
RAM: 8 GB
Storage: 50 GB SSD
PostgreSQL:
CPU: 2 cores
RAM: 8 GB
Storage: 100 GB SSD
ComfyUI (GPU Server):
CPU: 8 cores
RAM: 32 GB
GPU: NVIDIA RTX 4090 (24GB) o similar
Storage: 500 GB NVMe (modelos)
Storage (S3/MinIO):
Storage: 1 TB+ (escalable)
```
---
## 8. Monitoreo y Observabilidad
### 8.1 Logs
```yaml
Backend:
- Winston con formato JSON estructurado
- Niveles: error, warn, info, debug
- Rotaci贸n diaria
Agregaci贸n:
- ELK Stack (Elasticsearch, Logstash, Kibana)
- O alternativa: Loki + Grafana
```
### 8.2 M茅tricas
```yaml
Application Metrics:
- API response times (p50, p95, p99)
- Queue depths y processing times
- Generation success rate
- Error rates por tipo
Business Metrics:
- Assets generados por d铆a
- Tiempo promedio de generaci贸n
- Assets aprobados vs rechazados
- Uso por tenant/usuario
Infrastructure:
- CPU, RAM, disk usage
- GPU utilization
- Network I/O
```
### 8.3 Alertas
```yaml
Cr铆ticas:
- API down > 1 min
- GPU unavailable
- Queue backlog > 100 jobs
- Error rate > 5%
Advertencias:
- Response time p95 > 2s
- Disk usage > 80%
- Queue processing slow
```
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,199 @@
# Glosario - Platform Marketing Content
**Versi贸n:** 1.0.0
**Fecha:** 2025-12-08
---
## T茅rminos del Dominio
### A
**Asset**
: Recurso digital generado o almacenado en la plataforma (imagen, copy, video, etc.).
**Avatar Virtual / Influencer Virtual**
: Personaje generado por IA que mantiene consistencia visual a trav茅s de m煤ltiples im谩genes/videos. Ejemplo: Aitana L贸pez.
### B
**Brief**
: Documento estructurado que define los par谩metros de una campa帽a o generaci贸n: objetivo, p煤blico, tono, canales, restricciones.
### C
**Campa帽a**
: Iniciativa de marketing con objetivo espec铆fico que agrupa m煤ltiples assets generados para diferentes canales.
**Checkpoint**
: Versi贸n guardada de un modelo de IA entrenado. Puede ser especializado (fotograf铆a de producto, rostros, etc.).
**ComfyUI**
: Interfaz de nodos para construir flujos de generaci贸n con Stable Diffusion y otros modelos. Permite encadenar procesos de forma visual.
**ComfyDeploy**
: Herramienta que permite exponer workflows de ComfyUI como APIs HTTP para consumo desde aplicaciones.
**Consistencia de Personaje**
: Capacidad de mantener los mismos rasgos faciales y caracter铆sticas visuales de un personaje a trav茅s de m煤ltiples generaciones.
**ControlNet**
: Adaptador para modelos de difusi贸n que permite controlar la generaci贸n mediante gu铆as visuales (poses, bordes, profundidad, segmentaci贸n).
**Copy**
: Texto publicitario generado para acompa帽ar im谩genes (t铆tulos, descripciones, hashtags, CTAs).
**CRM (Customer Relationship Management)**
: Sistema de gesti贸n de relaciones con clientes. En esta plataforma, integrado con el motor de generaci贸n.
### D
**DAM (Digital Asset Management)**
: Sistema de gesti贸n de activos digitales. Repositorio centralizado para almacenar, organizar y buscar contenido generado.
**Difusi贸n / Diffusion**
: T茅cnica de IA generativa que crea im谩genes partiendo de ruido aleatorio y eliminando ruido gradualmente siguiendo indicaciones de texto.
**DreamBooth**
: T茅cnica de fine-tuning que permite personalizar un modelo con pocas im谩genes de un sujeto espec铆fico.
### F
**Fine-tuning**
: Proceso de re-entrenar un modelo pre-existente con datos espec铆ficos para especializarlo en un dominio o estilo particular.
**Fotograf铆a Sint茅tica**
: Im谩genes generadas por IA que simulan fotograf铆as reales de productos, personas o escenas.
### G
**Generaci贸n de Contenido**
: Proceso de crear im谩genes, textos u otros medios mediante modelos de IA.
### I
**Image Prompt**
: Imagen de referencia utilizada como entrada adicional al texto para guiar la generaci贸n hacia un estilo o sujeto espec铆fico.
**Inpainting**
: T茅cnica de edici贸n que permite regenerar solo una porci贸n seleccionada de una imagen manteniendo el resto intacto.
**IP-Adapter**
: Adaptador que permite inyectar caracter铆sticas de una imagen de referencia en la generaci贸n, logrando consistencia visual.
### L
**LLM (Large Language Model)**
: Modelo de lenguaje de gran escala usado para generar texto (GPT-4, Claude, Llama, etc.).
**LoRA (Low-Rank Adaptation)**
: T茅cnica de fine-tuning eficiente que permite personalizar un modelo con pocos recursos. Crea archivos peque帽os que modifican el comportamiento del modelo base.
### M
**Multi-tenant**
: Arquitectura donde una sola instancia del software sirve a m煤ltiples clientes (tenants) con datos aislados.
### N
**n8n**
: Plataforma de automatizaci贸n de workflows que conecta diferentes servicios y APIs mediante flujos visuales.
### O
**Orquestador**
: Sistema que coordina la ejecuci贸n de m煤ltiples tareas o servicios en un flujo definido.
### P
**Plantilla / Template**
: Workflow predefinido de generaci贸n que puede ser reutilizado para crear contenido con par谩metros espec铆ficos.
**Prompt**
: Texto de instrucci贸n que gu铆a al modelo de IA sobre qu茅 generar. Puede incluir descripci贸n de la imagen, estilo, elementos a incluir/excluir.
### R
**RBAC (Role-Based Access Control)**
: Sistema de permisos basado en roles donde los usuarios heredan permisos seg煤n su rol asignado.
**Rollout**
: Despliegue gradual de una funcionalidad a grupos de usuarios.
### S
**SaaS (Software as a Service)**
: Modelo de distribuci贸n de software donde la aplicaci贸n se aloja en la nube y se accede v铆a web bajo suscripci贸n.
**SDXL (Stable Diffusion XL)**
: Versi贸n avanzada de Stable Diffusion con mayor resoluci贸n y mejor comprensi贸n de prompts.
**Stable Diffusion**
: Modelo de difusi贸n de c贸digo abierto para generaci贸n de im谩genes a partir de texto.
### T
**Tenant**
: Cliente/organizaci贸n en una arquitectura multi-tenant. Cada tenant tiene sus datos aislados.
**Text-to-Image (T2I)**
: Proceso de generar im谩genes a partir de descripciones textuales.
**Textual Inversion**
: T茅cnica que permite ense帽ar a un modelo un nuevo concepto asociado a una palabra clave espec铆fica.
### U
**Upscaling**
: Proceso de aumentar la resoluci贸n de una imagen utilizando t茅cnicas de IA para mantener o mejorar la calidad.
### V
**VRAM (Video RAM)**
: Memoria dedicada de la tarjeta gr谩fica. Los modelos de difusi贸n requieren 8-24GB VRAM para funcionar.
### W
**Workflow**
: Flujo de trabajo que define una secuencia de pasos para completar un proceso (ej: generaci贸n de im谩genes con postprocesado).
---
## Acr贸nimos Comunes
| Acr贸nimo | Significado |
|----------|-------------|
| **API** | Application Programming Interface |
| **CRM** | Customer Relationship Management |
| **DAM** | Digital Asset Management |
| **GPU** | Graphics Processing Unit |
| **JWT** | JSON Web Token |
| **LLM** | Large Language Model |
| **LoRA** | Low-Rank Adaptation |
| **MVP** | Minimum Viable Product |
| **NLP** | Natural Language Processing |
| **RBAC** | Role-Based Access Control |
| **RLS** | Row-Level Security |
| **SaaS** | Software as a Service |
| **SDXL** | Stable Diffusion XL |
| **T2I** | Text-to-Image |
| **VRAM** | Video Random Access Memory |
---
## Modelos de IA Relevantes
| Modelo | Tipo | Descripci贸n |
|--------|------|-------------|
| **Stable Diffusion XL** | Text-to-Image | Modelo base open-source de alta calidad |
| **Fooocus** | Frontend SDXL | Interfaz simplificada para SDXL |
| **Gemini 3 Pro Image** | Text-to-Image | Modelo de Google con texto perfecto en im谩genes |
| **Seedream 4.0** | Multimodal | Modelo de ByteDance con imagen, video y avatares |
| **DeepFloyd IF** | Text-to-Image | Especializado en renderizado de texto |
| **GPT-4** | LLM | Modelo de OpenAI para generaci贸n de texto |
| **Claude** | LLM | Modelo de Anthropic para generaci贸n de texto |
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,327 @@
# MVP Plataforma SaaS de Generación de Contenido y CRM Creativo
*(Basada en la investigación de la plataforma tipo Morfeo Academy y tecnologías open source)*
---
## 1. Objetivo General del MVP
Construir una **plataforma SaaS interna** (modo “SaaS para uso propio”) para una **agencia de publicidad y generación de contenido**, que permita:
- Generar contenido visual y textual (imágenes, piezas publicitarias, posts para redes, etc.) con IA.
- Gestionar campañas, clientes y oportunidades desde un **CRM integrado**.
- Automatizar flujos creativos y procesos operativos (desde brief → contenido → aprobación → publicación/entrega).
- Operar bajo un **admin con uso ilimitado** + roles internos, con base técnica preparada para futura multi-empresa/multi-tenant.
La prioridad del MVP es usar **modelos open source auto-hosteados** y, cuando sea estrictamente necesario, **APIs de terceros** para capacidades avanzadas (texto perfecto en imágenes, video/avatares, etc.).
---
## 2. Alcance del MVP
### 2.1 Incluido
- Núcleo de generación de **imágenes** + **copys** para marketing.
- Gestión básica de **clientes, marcas, productos y campañas**.
- Módulo para **workflows creativos predefinidos** (plantillas de generación).
- **Automatización básica**: triggers desde CRM hacia el motor de generación.
- **Gestión de usuarios internos** (equipo de la agencia) con permisos por rol.
- Panel de **administración SaaS** para el súper admin (configuración global, límites, monitoreo).
### 2.2 Fuera de alcance inmediato (para fases posteriores)
- Generación de video avanzada (SORA, Seedream, etc.) como core.
- Entrenamiento auto-servicio 100% self-service de LoRAs por parte del cliente final (en MVP será gestionado por el equipo técnico).
- Integraciones profundas con múltiples CRMs externos (inicialmente solo 1 CRM interno y 1 integración externa simple).
- Marketplace público multi-tenant con planes comerciales complejos.
---
## 3. Perfiles de Usuario
1. **Súper Admin SaaS (Dueño/CTO)**
- Acceso ilimitado a todos los módulos y recursos (sin restricciones de uso).
- Configura parámetros globales de infraestructura, colas de tareas y modelos.
- Gestiona planes/limitaciones (aunque en MVP sólo se usa el plan interno “ilimitado”).
2. **Admin de Agencia**
- Configura clientes, marcas, equipos y usuarios internos.
- Define plantillas y workflows creativos estándar.
- Aprueba solicitudes de entrenamiento de modelos personalizados (LoRAs, estilos).
3. **Creativo/Media Buyer**
- Crea proyectos y campañas.
- Lanza generaciones de contenido (imágenes y copys) usando plantillas.
- Solicita entrenamiento de modelos personalizados (avatar de marca, producto, estilo).
- Interactúa con el sistema para iterar versiones.
4. **Analista/CRM**
- Administra leads, contactos, oportunidades y campañas en CRM.
- Dispara automatizaciones (por ejemplo, al crear un nuevo producto).
- Consulta métricas de campañas y uso de contenido.
5. **Cliente (externo) Modo opcional MVP**
- Acceso restringido (portal ligero): revisar piezas generadas, aprobar/rechazar, descargar assets.
- No lanza generaciones directamente en MVP (esto puede añadirse en una fase posterior).
---
## 4. Módulos Funcionales del MVP
### 4.1 Módulo de Gestión de Cuentas y Tenants
> Aunque el uso principal es interno, la arquitectura debe soportar **multi-tenant** futuro.
- **Tenant “Agencia Propia”** como tenant principal.
- Estructuras para futuros tenants (clientes/agencias externas) aunque en MVP solo haya uno.
- Configuración por tenant:
- Nombre, branding (logo, colores base).
- Límites de uso (imágenes mensuales, entrenamientos, etc.) aunque el tenant interno tenga límites desactivados.
- Conexiones a CRM interno y/o externo.
### 4.2 Módulo CRM Integrado
- **Entidades básicas**:
- Contactos (personas).
- Empresas / Clientes.
- Productos / Servicios.
- Oportunidades / Deals.
- Campañas de marketing.
- **Funcionalidades**:
- Alta/edición de clientes y contactos.
- Registro de oportunidades asociadas a campañas.
- Segmentación básica de leads (por industria, tamaño, estado, etc.).
- Historial de interacciones (notas, actividades, tareas).
- Campos clave para conectar con generación de contenido (ej. tipo de producto, fotos de referencia, identidad de marca, tono de comunicación).
- **Integración**:
- API interna para conectarse con el motor de generación (ej. “nuevo producto → generar pack de 5 imágenes de catálogo y 3 posts de redes”).
- Integración mínima con 1 CRM externo (ej. webhooks/REST) para sincronizar datos básicos (opcional en MVP).
### 4.3 Módulo de Proyectos y Campañas
- **Proyectos**:
- Agrupan campañas y assets por cliente o iniciativa interna.
- Estados: Borrador, En curso, En revisión, Cerrado.
- **Campañas**:
- Tipos: redes sociales, performance ads, catálogos, landing pages, etc.
- Brief estructurado:
- Objetivo de la campaña.
- Público objetivo.
- Tono de voz.
- Canales (IG, FB, TikTok, Google Ads, etc.).
- Restricciones (palabras prohibidas, colores de marca, etc.).
- **Flujos del MVP**:
- Crear campaña → seleccionar plantillas de generación (ej. pack de 10 imágenes + 10 copys).
- Disparar generación → monitorizar estado → revisión interna → aprobación/entrega.
### 4.4 Módulo de Motor de Generación de Contenido (IA)
#### 4.4.1 Núcleo de Modelos y Runtime
- **Modelos locales (open source, auto-hosteados)**:
- Modelo principal **text-to-image** tipo **Stable Diffusion XL** o similar, optimizado para marketing.
- Checkpoints especializados:
- Fotografía de producto (e-commerce, fondo blanco/contextos comerciales).
- Rostros humanos realistas / lifestyle.
- Estilos ilustrados (diseño publicitario, vintage, cartoon, etc.).
- **ControlNets / Adaptadores**:
- Pose humana (OpenPose) para controlar poses de modelos/influencers.
- Segmentación (fondo/primer plano).
- Otros de utilidad (canny, depth) para composición y ajustes.
- Modelos de **upscaling** para entregar imágenes en alta resolución (2x4x).
- Modelos de **inpainting** para correcciones localizadas (ej. arreglar manos, insertar elementos).
- **Modelos de texto (NLP)**:
- Integración con **modelo de lenguaje** (open source o API external) para:
- Generar copys, títulos, descripciones, hashtags.
- Ajustar tono según el brief.
- El MVP puede usar un modelo de OpenAI vía API para texto, dado su bajo costo relativo.
#### 4.4.2 Workflows de Generación (ComfyUI / Orquestador)
- Uso de **ComfyUI** como base de construcción de flujos de generación y automatización creativa.
- Diseño de varios workflows estándar:
1. **Fotografía sintética de producto**
- Entrada: fotos de referencia (opcional), descripción de producto, estilo o contexto.
- Salida: pack de imágenes listas para catálogo y redes.
2. **Post de redes sociales (imagen + copy)**
- Entrada: brief corto, producto/servicio, canal objetivo.
- Salida: 1N imágenes + textos sugeridos.
3. **Influencer virtual / avatar de marca** (ver más abajo).
4. **Variaciones de anuncio**
- Generación de múltiples variantes (colores, encuadres, fondos) para pruebas A/B.
- Exposición de estos workflows como **APIs internas** (ComfyDeploy u otro mecanismo) para que el backend los consuma.
#### 4.4.3 Personalización de Identidad Visual y Avatares
- **LoRAs / Fine-tuning**:
- Entrenamiento dirigido por el equipo técnico de:
- Estilos de marca.
- Productos específicos (líneas completas de producto).
- Avatares e influencers virtuales (rostros/personajes consistentes).
- Interfaz para:
- Registrar un “Modelo personalizado” (LoRA/Checkpoint).
- Asignarlo a una marca/cliente.
- Seleccionarlo en plantillas de generación.
- **Consistencia de personajes**:
- Uso de image prompts, IP-Adapters y nodos tipo Consistent Characters para mantener la misma apariencia a través de múltiples piezas.
- Flujos de “reciclaje de referencia”: la última imagen aprobada de un avatar se reutiliza como referencia para nuevas generaciones.
#### 4.4.4 Generación con Texto Integrado en la Imagen
- **Modo base (open source)**:
- Mejor esfuerzo con SDXL + técnicas complementarias (inpainting, ControlNet, postprocesado).
- Alternativa híbrida: generar imagen base y superponer texto con capa vectorial (HTML/CSS/Canvas) desde el front.
- **Modo premium vía API (opcional)**:
- Integración con algún modelo propietario tipo **Gemini 3 Pro Image (Nano Banana Pro)** u otro similar para casos en que se requiera texto perfectamente legible integrado (posters densos, infografías).
- Expuesto al usuario como opción “Calidad premium de texto”.
#### 4.4.5 Contenido Animado y Video (MVP reducido)
- MVP:
- Creación de **GIFs/cinemagraphs** a partir de secuencias de imágenes generadas.
- Pipeline básico de “fotogramas clave + interpolación” (usando herramientas IA cuando sea viable).
- Futuro (fuera del MVP, pero preparado en arquitectura):
- Integración con APIs de texto-a-video (Runway, Seedream, SORA, etc.).
- Integración con servicios de avatares parlantes (HeyGen u otros).
---
### 4.5 Módulo de Automatización Creativa y Flujos (Orquestación)
- Integración con un orquestador tipo **n8n** o similar para automatizar tareas entre:
- CRM interno.
- Motor de generación de contenido.
- Sistemas externos (por ejemplo, herramientas de email marketing, redes sociales).
- Flujos MVP:
1. **Nuevo producto en CRM → Generar Kit de Assets**
- Dispara workflow de fotografía de producto + posts base.
2. **Nueva campaña → Generar lote de creatividades**
- Crea assets iniciales para revisión interna.
3. **Cambio de estado de campaña (Aprobada) → Notificación/Entrega**
- Notifica a responsables; genera zip descargable o publica en espacio compartido.
- Sistema de **colas de tareas**:
- Distribución de trabajos en GPU(s).
- Manejo de prioridades (trabajos urgentes vs batch).
---
### 4.6 Módulo de Biblioteca de Activos (DAM)
- Repositorio central de:
- Imágenes generadas.
- Copys, prompts y briefs.
- Modelos personalizados (LoRAs, checkpoints, presets de workflows).
- Funciones:
- Búsqueda avanzada (por cliente, campaña, tags, tipo de contenido, fecha).
- Versionado de assets (iteraciones sobre una misma pieza).
- Estados: borrador, en revisión, aprobado, publicado.
- Descarga individual o en paquetes.
---
### 4.7 Módulo de Administración SaaS y Configuración
- Gestión de:
- Usuarios, roles y permisos.
- Tenants (al menos el tenant “Agencia Propia” en el MVP).
- Parámetros globales:
- Límites de tamaño/formatos de salida.
- Modelos activos/inactivos.
- Política de retención de assets.
- Auditoría básica:
- Log de acciones relevantes (generaciones, entrenamientos, cambios de configuración).
- Métricas de uso por usuario y campaña (número de imágenes generadas, GPU time estimado, etc.).
- Preparación para **planes de suscripción** (aunque en MVP no se comercializa externamente):
- Estructura para planes (Free, Pro, Enterprise, Interno Ilimitado).
- Parámetros por plan: generaciones/mes, entrenamientos, acceso a modo premium de texto, etc.
---
### 4.8 Módulo de Analítica y Reporting
- **Dashboards básicos**:
- Volumen de contenido generado por periodo, cliente, campaña.
- Tiempo promedio de generación y aprobación.
- Uso de modelos personalizados (cuántas campañas usan cada LoRA/estilo).
- Relación entre campañas y assets generados (para análisis posterior de performance).
- **KPI iniciales**:
- Nº de campañas activas.
- Nº de assets por campaña.
- Porcentaje de assets aprobados en primera iteración.
- Exportación:
- Descarga de reportes en CSV/Excel.
- API para integración con herramientas de BI.
---
### 4.9 Módulo de Seguridad, Permisos y Cumplimiento
- **Autenticación y autorización**:
- Login con email + password (más adelante SSO/OAuth).
- Roles y permisos por módulo/acción (RBAC).
- **Aislamiento lógico por tenant** pese a que inicialmente sólo se use uno.
- **Gestión de datos sensibles**:
- Cifrado en tránsito (HTTPS).
- Buenas prácticas de manejo de imágenes de personas (consentimiento, uso de datos, etc.).
---
## 5. Requisitos Técnicos de Alto Nivel
### 5.1 Infraestructura
- Servidor con **GPU dedicada** (idealmente 1224 GB VRAM) para ejecutar los modelos de imagen (SDXL, LoRAs, ControlNets, upscaling).
- Arquitectura con:
- **Backend monolítico** (API REST/GraphQL) para lógica de negocio, colas y orquestación.
- **Servicio(s) de inferencia** (ComfyUI/Deploy, pipelines Diffusers, etc.) comunicados vía HTTP/ gRPC.
- **Base de datos** relacional para CRM, proyectos, campañas, usuarios, configuración.
- Almacenamiento de archivos (local o S3-compatible) para assets e imágenes generadas.
- Sistema de **colado y scheduling de tareas**:
- Gestión eficiente de jobs de generación y entrenamiento.
- Posibilidad de añadir más GPUs/instancias a futuro.
### 5.2 Integraciones Clave
- Orquestador (n8n u otro) para flujos entre módulos.
- APIs externas **opcionales**:
- Modelos de texto avanzados (LLMs).
- Modelos de imágenes con texto perfecto (Gemini/Seedream u otros) sólo para casos premium.
- Servicios de video/avatares (etapa futura).
---
## 6. Roadmap de Evolución (en alto nivel)
> No forma parte estricta del MVP, pero orienta el desglose posterior.
1. **Fase 1 Core MVP**
- Módulos: usuarios/roles, CRM básico, proyectos y campañas, motor de generación de imágenes + copys, DAM básico.
- Workflows estándar de generación de producto y posts.
2. **Fase 2 Personalización Avanzada**
- Entrenamiento recurrente de LoRAs/estilos.
- Avatares/influencers virtuales consistentes.
- Integración más profunda con CRM e orquestador.
3. **Fase 3 Contenido Enriquecido y Multi-tenant Comercial**
- Video, avatares parlantes, modos premium con APIs externas.
- Apertura a clientes externos (modo SaaS completo).
- Planes de suscripción y límites de uso.

View File

@ -0,0 +1,466 @@
# Platform Marketing Content - Plataforma SaaS de Generaci贸n de Contenido y CRM Creativo
**Versi贸n:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Fase de An谩lisis y Documentaci贸n
**Modelo de Negocio:** SaaS Multi-tenant (uso interno inicial)
---
## 1. Resumen Ejecutivo
### 1.1 Visi贸n del Producto
**Platform Marketing Content** es una plataforma SaaS interna para **agencias de publicidad y generaci贸n de contenido** que combina:
1. **Motor de Generaci贸n de Contenido con IA** - Im谩genes, copys, posts para redes sociales
2. **CRM Integrado** - Gesti贸n de clientes, marcas, campa帽as y oportunidades
3. **Automatizaci贸n Creativa** - Flujos desde brief 鈫? contenido 鈫? aprobaci贸n 鈫? entrega
4. **DAM (Digital Asset Management)** - Biblioteca centralizada de activos generados
### 1.2 Propuesta de Valor
| Problema Actual | Soluci贸n Propuesta |
|-----------------|-------------------|
| Creaci贸n manual de contenido visual costosa y lenta | Generaci贸n autom谩tica con IA (Stable Diffusion, ComfyUI) |
| Dependencia de fot贸grafos y sets f铆sicos | Fotograf铆a sint茅tica de productos hiperrealista |
| Inconsistencia de marca entre campa帽as | LoRAs entrenados por marca/producto |
| Gesti贸n fragmentada de clientes y campa帽as | CRM integrado con motor de generaci贸n |
| Costos elevados por APIs comerciales (Midjourney, DALL-E) | Modelos open-source auto-hosteados |
| Flujos manuales de aprobaci贸n | Automatizaci贸n con n8n + workflows predefinidos |
### 1.3 Diferenciadores Clave
```
鉁? Modelos Open Source + Auto-hosting = Costos controlados
鉁? LoRAs personalizados por marca = Consistencia visual
鉁? CRM + Generaci贸n integrados = Flujo end-to-end
鉁? ComfyUI como orquestador = Workflows visuales y escalables
鉁? Arquitectura preparada para multi-tenant = Escalabilidad futura
```
---
## 2. Objetivos del MVP
### 2.1 Objetivos de Negocio
| Objetivo | M茅trica de 脡xito | Plazo |
|----------|------------------|-------|
| Reducir tiempo de creaci贸n de contenido | -70% tiempo vs proceso manual | MVP |
| Eliminar dependencia de servicios externos | 100% generaci贸n local | MVP |
| Centralizar gesti贸n de clientes y campa帽as | 1 plataforma unificada | MVP |
| Habilitar personalizaci贸n por marca | LoRAs entrenados por cliente | Fase 2 |
### 2.2 Objetivos T茅cnicos
| Objetivo | Descripci贸n |
|----------|-------------|
| **Auto-hosting** | Ejecutar modelos en GPU dedicada (12-24GB VRAM) |
| **Arquitectura modular** | Separaci贸n clara: CRM, Motor IA, DAM, Automatizaci贸n |
| **Multi-tenant ready** | Estructura preparada para m煤ltiples tenants |
| **API-first** | Todos los servicios expuestos v铆a REST/GraphQL |
| **Escalabilidad** | Colas de tareas, distribuci贸n en GPUs |
---
## 3. Alcance del MVP
### 3.1 Incluido en MVP (Fase 1)
| M贸dulo | Funcionalidades Core |
|--------|---------------------|
| **CRM Integrado** | Clientes, marcas, productos, campa帽as, oportunidades |
| **Motor de Generaci贸n** | Text-to-image (SDXL), copys con LLM, upscaling |
| **Workflows Creativos** | Fotograf铆a de producto, posts para redes, variaciones |
| **DAM B谩sico** | Repositorio de im谩genes, b煤squeda, estados (borrador/aprobado) |
| **Automatizaci贸n B谩sica** | Triggers CRM 鈫? Generaci贸n |
| **Admin SaaS** | Usuarios, roles, configuraci贸n global |
### 3.2 Fuera de Alcance MVP (Fases Posteriores)
| Feature | Fase Planificada |
|---------|------------------|
| Generaci贸n de video avanzada (SORA, Seedream) | Fase 3 |
| Entrenamiento self-service de LoRAs | Fase 2 |
| Marketplace multi-tenant p煤blico | Fase 4 |
| Integraciones profundas con CRMs externos | Fase 2 |
| Avatares parlantes (HeyGen, etc.) | Fase 3 |
| Texto perfecto en im谩genes (Gemini API) | Fase 2 (opcional) |
---
## 4. Perfiles de Usuario
### 4.1 Usuarios Internos (MVP)
| Perfil | Descripci贸n | Permisos Principales |
|--------|-------------|---------------------|
| **S煤per Admin SaaS** | Due帽o/CTO de la plataforma | Acceso ilimitado, configuraci贸n global, modelos |
| **Admin de Agencia** | Configura clientes, equipos, plantillas | Gesti贸n de usuarios, workflows, aprobaciones |
| **Creativo/Media Buyer** | Lanza generaciones, itera versiones | Crear proyectos, generar contenido, solicitar LoRAs |
| **Analista/CRM** | Gestiona leads, campa帽as, m茅tricas | CRM completo, triggers de automatizaci贸n |
### 4.2 Usuarios Externos (Opcional MVP)
| Perfil | Descripci贸n | Permisos |
|--------|-------------|----------|
| **Cliente (portal ligero)** | Revisa piezas, aprueba/rechaza | Solo lectura + aprobaci贸n |
---
## 5. M贸dulos Funcionales
### 5.1 M贸dulo 1: Gesti贸n de Cuentas y Tenants (PMC-001)
**Objetivo:** Arquitectura multi-tenant preparada para escalar.
```yaml
Funcionalidades:
- Tenant principal "Agencia Propia"
- Estructura para futuros tenants externos
- Configuraci贸n por tenant: branding, l铆mites, conexiones
- L铆mites de uso (im谩genes/mes, entrenamientos)
```
### 5.2 M贸dulo 2: CRM Integrado (PMC-002)
**Objetivo:** Gestionar clientes, marcas y campa帽as con conexi贸n directa al motor de generaci贸n.
```yaml
Entidades:
- Contactos (personas)
- Empresas/Clientes
- Marcas (brand identity, tono, restricciones)
- Productos/Servicios
- Oportunidades/Deals
- Campa帽as de Marketing
Funcionalidades:
- Alta/edici贸n de clientes y contactos
- Segmentaci贸n de leads
- Historial de interacciones
- Campos para generaci贸n: fotos referencia, identidad marca, tono
- API interna 鈫? motor de generaci贸n
```
### 5.3 M贸dulo 3: Proyectos y Campa帽as (PMC-003)
**Objetivo:** Organizar el trabajo creativo por iniciativa.
```yaml
Proyectos:
- Agrupan campa帽as y assets por cliente
- Estados: Borrador, En curso, En revisi贸n, Cerrado
Campa帽as:
- Tipos: redes sociales, performance ads, cat谩logos, landing pages
- Brief estructurado:
- Objetivo de campa帽a
- P煤blico objetivo
- Tono de voz
- Canales (IG, FB, TikTok, Google Ads)
- Restricciones (palabras prohibidas, colores marca)
Flujos:
- Crear campa帽a 鈫? seleccionar plantillas 鈫? disparar generaci贸n 鈫? revisi贸n 鈫? entrega
```
### 5.4 M贸dulo 4: Motor de Generaci贸n de Contenido IA (PMC-004)
**Objetivo:** N煤cleo de generaci贸n de im谩genes y copys.
```yaml
4.1 Modelos Locales (Open Source):
Imagen:
- Stable Diffusion XL (principal)
- Checkpoints especializados:
- Fotograf铆a de producto (e-commerce)
- Rostros humanos realistas
- Estilos ilustrados (publicidad, vintage)
- ControlNets: OpenPose, segmentaci贸n, canny, depth
- Upscaling: 2x-4x
- Inpainting: correcciones localizadas
Texto (NLP):
- LLM para copys, t铆tulos, hashtags
- Ajuste de tono seg煤n brief
- OpenAI API (bajo costo) o modelo local
4.2 Workflows ComfyUI:
Predefinidos:
- Fotograf铆a sint茅tica de producto
- Post redes sociales (imagen + copy)
- Variaciones de anuncio (A/B testing)
- Influencer virtual / avatar de marca
Caracter铆sticas:
- Exposici贸n como APIs internas (ComfyDeploy)
- Workflows personalizables por usuario
4.3 Personalizaci贸n (LoRAs):
- Entrenamiento dirigido por equipo t茅cnico:
- Estilos de marca
- Productos espec铆ficos
- Avatares/influencers virtuales
- Interfaz para registrar y asignar modelos personalizados
- Consistencia de personajes (IP-Adapters, Consistent Characters)
4.4 Texto en Im谩genes:
Modo base:
- SDXL + t茅cnicas complementarias
- Superposici贸n vectorial (HTML/CSS/Canvas)
Modo premium (opcional):
- API Gemini 3 Pro Image para tipograf铆a perfecta
```
### 5.5 M贸dulo 5: Automatizaci贸n Creativa (PMC-005)
**Objetivo:** Conectar CRM con motor de generaci贸n mediante flujos automatizados.
```yaml
Orquestador: n8n (o similar)
Flujos MVP:
1. Nuevo producto en CRM 鈫? Generar Kit de Assets
- Dispara workflow de fotograf铆a + posts base
2. Nueva campa帽a 鈫? Generar lote de creatividades
- Crea assets iniciales para revisi贸n
3. Cambio estado campa帽a (Aprobada) 鈫? Notificaci贸n/Entrega
- Notifica responsables
- Genera zip descargable
Sistema de Colas:
- Distribuci贸n de jobs en GPU(s)
- Manejo de prioridades (urgente vs batch)
```
### 5.6 M贸dulo 6: Biblioteca de Activos DAM (PMC-006)
**Objetivo:** Repositorio central de contenido generado.
```yaml
Contenido:
- Im谩genes generadas
- Copys, prompts, briefs
- Modelos personalizados (LoRAs, checkpoints, presets)
Funciones:
- B煤squeda avanzada (cliente, campa帽a, tags, tipo, fecha)
- Versionado de assets
- Estados: borrador, en revisi贸n, aprobado, publicado
- Descarga individual o en paquetes
```
### 5.7 M贸dulo 7: Administraci贸n SaaS (PMC-007)
**Objetivo:** Panel de control de la plataforma.
```yaml
Gesti贸n:
- Usuarios, roles, permisos (RBAC)
- Tenants (al menos "Agencia Propia" en MVP)
- Par谩metros globales:
- L铆mites de tama帽o/formatos
- Modelos activos/inactivos
- Pol铆tica de retenci贸n de assets
Auditor铆a:
- Log de acciones (generaciones, entrenamientos, cambios)
- M茅tricas de uso por usuario y campa帽a
Preparaci贸n para planes:
- Estructura: Free, Pro, Enterprise, Interno Ilimitado
- Par谩metros por plan: generaciones/mes, entrenamientos, etc.
```
### 5.8 M贸dulo 8: Anal铆tica y Reporting (PMC-008)
**Objetivo:** Visibilidad del rendimiento y uso.
```yaml
Dashboards:
- Volumen generado por periodo/cliente/campa帽a
- Tiempo promedio de generaci贸n y aprobaci贸n
- Uso de modelos personalizados
- Relaci贸n campa帽as-assets
KPIs Iniciales:
- N煤mero de campa帽as activas
- Assets por campa帽a
- % assets aprobados en primera iteraci贸n
Exportaci贸n:
- CSV/Excel
- API para herramientas BI
```
---
## 6. Requisitos T茅cnicos
### 6.1 Stack Tecnol贸gico
```yaml
Backend:
- Node.js 20+ / NestJS + TypeScript
- PostgreSQL 15+ (multi-tenant ready)
- Redis (caching, colas)
- Bull/BullMQ (jobs de generaci贸n)
Frontend:
- React 18 + Vite + TypeScript
- TailwindCSS / Shadcn UI
- React Query (data fetching)
Motor IA:
- ComfyUI (orquestaci贸n de workflows)
- ComfyDeploy (API de inferencia)
- Stable Diffusion XL + checkpoints
- Python 3.10+ / Diffusers (alternativa)
Automatizaci贸n:
- n8n (flujos entre m贸dulos)
Almacenamiento:
- S3 / MinIO (assets e im谩genes)
- PostgreSQL (metadata, CRM)
Infraestructura:
- Docker / Docker Compose
- GPU: NVIDIA 12-24GB VRAM (L4, RTX 3090/4090, A5000)
```
### 6.2 Arquitectura de Alto Nivel
```
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁? Frontend (React + Vite) 鉁?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁?
鈻?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁? Backend API (NestJS) 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁? 鉁? CRM 鉁? 鉁? Projects鉁? 鉁? Assets 鉁? 鉁?
鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁? 鉁? 鉁?
鈻? 鈻? 鈻?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁? PostgreSQL 鉁? 鉁? ComfyUI 鉁? 鉁? n8n 鉁?
鉁? + Redis 鉁? 鉁? (GPU GPU) 鉁? 鉁? (Workflows) 鉁?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁? 鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁?
鈻?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
鉁? S3/MinIO 鉁?
鉁? (Assets) 鉁?
鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁斺攢鉁?
```
### 6.3 Integraciones Clave
| Integraci贸n | Prioridad | Descripci贸n |
|-------------|-----------|-------------|
| ComfyUI / ComfyDeploy | Alta | Motor de workflows de generaci贸n |
| n8n | Alta | Orquestaci贸n de automatizaciones |
| OpenAI API | Media | LLM para generaci贸n de copys |
| Gemini API | Baja | Texto perfecto en im谩genes (opcional) |
| CRM Externo | Baja | Sincronizaci贸n b谩sica via webhooks |
---
## 7. Roadmap
### Fase 1: MVP Core (Semanas 1-8)
```yaml
Entregables:
- Arquitectura base (backend + frontend)
- CRM b谩sico (clientes, marcas, productos)
- Motor de generaci贸n con 2-3 workflows
- DAM b谩sico
- Admin de usuarios y roles
M茅trica de 脡xito:
- Generar 100 im谩genes de producto
- 3 campa帽as completas end-to-end
```
### Fase 2: Personalizaci贸n Avanzada (Semanas 9-14)
```yaml
Entregables:
- Entrenamiento de LoRAs por marca
- Avatares/influencers virtuales consistentes
- Integraci贸n profunda CRM 鈫? Generaci贸n
- Workflows adicionales
M茅trica de 脡xito:
- 5 LoRAs entrenados por marca
- 90% consistencia en personajes
```
### Fase 3: Contenido Enriquecido (Semanas 15-22)
```yaml
Entregables:
- GIFs/cinemagraphs
- Integraci贸n de video b谩sico
- APIs premium opcionales (Gemini para texto)
- Portal cliente externo
M茅trica de 脡xito:
- 50% de campa帽as con contenido animado
- 3 clientes externos usando portal
```
### Fase 4: Multi-tenant Comercial (Semanas 23+)
```yaml
Entregables:
- Apertura a clientes externos (SaaS completo)
- Planes de suscripci贸n y l铆mites
- Marketplace de extensiones
M茅trica de 脡xito:
- 10 tenants activos
- MRR positivo
```
---
## 8. Riesgos y Mitigaciones
| Riesgo | Impacto | Probabilidad | Mitigaci贸n |
|--------|---------|--------------|------------|
| Calidad de generaci贸n insuficiente | Alto | Media | Checkpoints especializados, postprocesado |
| Requisitos de GPU elevados | Medio | Alta | Optimizaci贸n de modelos, colas de prioridad |
| Texto ilegible en im谩genes | Medio | Alta | Modo h铆brido (IA + superposici贸n) |
| Inconsistencia de personajes | Medio | Media | LoRAs + IP-Adapters + image prompts |
| Complejidad de ComfyUI | Medio | Media | Workflows pre-construidos, documentaci贸n |
---
## 9. Referencias
### 9.1 Documentaci贸n del Proyecto
- [MVP Original](./MVP_Plataforma_SaaS_Contenido_CRM.md) - Definici贸n inicial
- [Investigaci贸n Morfeo Academy](./Investigaci贸n%20Profunda_%20Plataforma%20de%20Generaci贸n%20de%20Contenido%20de%20Morfeo%20Academy%20y%20Desarrollo%20de%20una%20.pdf) - An谩lisis de referencia
### 9.2 Tecnolog铆as de Referencia
- [Morfeo Academy](https://www.morfeoacademy.com/) - Referencia de plataforma similar
- [ComfyUI](https://github.com/comfyanonymous/ComfyUI) - Motor de workflows
- [ComfyDeploy](https://www.comfydeploy.com/) - API de inferencia
- [Stable Diffusion XL](https://stability.ai/) - Modelo base de generaci贸n
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08
**Pr贸xima revisi贸n:** Al completar Fase 1

View File

@ -0,0 +1,275 @@
# PMC-001: Módulo de Tenants
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Definición
**Prioridad:** Alta
---
## Descripción General
El módulo de Tenants proporciona la arquitectura multi-tenant que permite aislar datos y configuraciones entre diferentes organizaciones (agencias) que utilicen la plataforma.
---
## Objetivos
1. Aislar completamente los datos entre tenants
2. Permitir configuración personalizada por tenant
3. Soportar branding personalizado (logo, colores)
4. Gestionar límites y cuotas por tenant
5. Preparar arquitectura para comercialización SaaS futura
---
## Entidades del Dominio
### Tenant
```yaml
Entidad: Tenant
Descripción: Organización/agencia que utiliza la plataforma
Atributos:
- id: UUID (PK)
- name: string (nombre de la organización)
- slug: string (identificador URL-friendly, único)
- status: enum [active, suspended, trial, cancelled]
- plan_id: UUID (FK a Plan)
- settings: JSONB (configuración personalizada)
- branding: JSONB (logo_url, primary_color, secondary_color)
- limits: JSONB (cuotas y límites)
- created_at: timestamp
- updated_at: timestamp
- deleted_at: timestamp (soft delete)
Relaciones:
- 1:N con User
- 1:N con Client (CRM)
- 1:N con Project
- 1:N con Asset
- N:1 con Plan
```
### Plan
```yaml
Entidad: Plan
Descripción: Plan de suscripción con límites definidos
Atributos:
- id: UUID (PK)
- name: string (Free, Pro, Enterprise, Internal)
- code: string (identificador único)
- features: JSONB (funcionalidades habilitadas)
- limits: JSONB
- generations_per_month: number
- trainings_per_month: number
- storage_gb: number
- users_max: number
- projects_max: number
- price_monthly: decimal
- price_yearly: decimal
- is_active: boolean
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- 1:N con Tenant
```
### TenantSettings
```yaml
Estructura JSONB: settings
Campos:
- default_language: string (es, en)
- timezone: string
- date_format: string
- currency: string
- notifications:
email_enabled: boolean
slack_webhook: string
- integrations:
crm_external_url: string
n8n_webhook_base: string
- generation:
default_model: string
default_quality: string
watermark_enabled: boolean
```
---
## Funcionalidades
### F-001.1: Gestión de Tenants
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-001.1.1 | Crear tenant | Registro de nueva organización | Alta |
| F-001.1.2 | Editar tenant | Modificar datos y configuración | Alta |
| F-001.1.3 | Suspender tenant | Desactivar acceso temporalmente | Media |
| F-001.1.4 | Eliminar tenant | Soft delete con retención de datos | Baja |
### F-001.2: Configuración por Tenant
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-001.2.1 | Branding | Personalizar logo y colores | Media |
| F-001.2.2 | Límites | Configurar cuotas de uso | Alta |
| F-001.2.3 | Integraciones | URLs de webhooks y APIs externas | Media |
| F-001.2.4 | Preferencias | Idioma, zona horaria, formatos | Baja |
### F-001.3: Aislamiento de Datos
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-001.3.1 | RLS PostgreSQL | Row-Level Security por tenant_id | Alta |
| F-001.3.2 | Middleware | Inyección automática de tenant_id | Alta |
| F-001.3.3 | Storage isolation | Prefijos S3 por tenant | Alta |
---
## Reglas de Negocio
```yaml
RN-001.1:
Descripción: Todo tenant debe tener un plan asignado
Validación: plan_id NOT NULL
Default: Plan "Free" o "Internal"
RN-001.2:
Descripción: El slug del tenant debe ser único y URL-safe
Validación: Regex ^[a-z0-9-]+$, único en BD
RN-001.3:
Descripción: Tenant suspendido no permite login de usuarios
Acción: Validar status en middleware de autenticación
RN-001.4:
Descripción: Límites del plan se heredan al tenant
Override: Tenant puede tener límites personalizados que sobreescriban el plan
RN-001.5:
Descripción: Soft delete conserva datos 90 días
Acción: deleted_at marca fecha, cron job limpia después
```
---
## API Endpoints
```yaml
Base: /api/v1/tenants
Endpoints:
POST /tenants:
Descripción: Crear nuevo tenant (Super Admin)
Body: { name, slug, plan_id, settings? }
Response: 201 Created
GET /tenants:
Descripción: Listar tenants (Super Admin)
Query: ?status=active&page=1&limit=20
Response: 200 OK (paginado)
GET /tenants/:id:
Descripción: Obtener tenant por ID
Response: 200 OK
PUT /tenants/:id:
Descripción: Actualizar tenant
Body: { name?, settings?, branding?, limits? }
Response: 200 OK
PATCH /tenants/:id/status:
Descripción: Cambiar estado del tenant
Body: { status: "suspended" | "active" }
Response: 200 OK
DELETE /tenants/:id:
Descripción: Soft delete tenant
Response: 204 No Content
GET /tenants/current:
Descripción: Obtener tenant del usuario actual
Response: 200 OK
```
---
## Dependencias
```yaml
Dependencias del Catálogo:
- @CATALOG_TENANT: Patrón base multi-tenancy (adaptar)
Dependencias de Módulos:
- PMC-007 Admin: Gestión de planes y configuración global
Servicios Externos:
- Ninguno requerido
```
---
## Consideraciones Técnicas
### Estrategia Multi-Tenant
```yaml
Tipo: Single Database, Shared Schema
Aislamiento: Row-Level Security (RLS) en PostgreSQL
Implementación:
1. Columna tenant_id en todas las tablas principales
2. Políticas RLS que filtran por tenant_id
3. Middleware que extrae tenant del JWT/sesión
4. SET app.current_tenant antes de cada query
```
### Ejemplo RLS PostgreSQL
```sql
-- Política para tabla clients
CREATE POLICY tenant_isolation_policy ON clients
USING (tenant_id = current_setting('app.current_tenant')::uuid);
-- Habilitar RLS
ALTER TABLE clients ENABLE ROW LEVEL SECURITY;
```
### Storage Isolation
```yaml
Estructura S3:
bucket/
├── {tenant_slug}/
│ ├── assets/
│ ├── generated/
│ ├── models/
│ └── temp/
```
---
## Criterios de Aceptación
- [ ] Tenant puede ser creado con datos mínimos (name, slug, plan)
- [ ] RLS impide acceso a datos de otros tenants
- [ ] Branding se refleja en UI del tenant
- [ ] Límites del plan se validan antes de operaciones
- [ ] Soft delete funciona correctamente
- [ ] API responde correctamente a todos los endpoints
---
## Referencias
- [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md)
- [@CATALOG_TENANT](../../../core/catalog/modules/multi-tenancy/)
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,387 @@
# PMC-002: Módulo CRM
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Definición
**Prioridad:** Alta
---
## Descripción General
El módulo CRM (Customer Relationship Management) gestiona clientes, marcas, productos y la relación entre estos elementos. Está diseñado para integrarse directamente con el motor de generación de contenido IA.
---
## Objetivos
1. Gestionar clientes y contactos de la agencia
2. Organizar marcas y productos por cliente
3. Almacenar identidad visual y lineamientos de marca
4. Registrar oportunidades comerciales
5. Conectar datos de CRM con generación de contenido
---
## Entidades del Dominio
### Client
```yaml
Entidad: Client
Descripción: Empresa/organización cliente de la agencia
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- name: string
- legal_name: string
- tax_id: string (RFC/NIF)
- industry: string
- size: enum [micro, small, medium, large, enterprise]
- website: string
- status: enum [prospect, active, inactive, churned]
- notes: text
- metadata: JSONB
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Tenant
- 1:N con Contact
- 1:N con Brand
- 1:N con Opportunity
- 1:N con Project
```
### Contact
```yaml
Entidad: Contact
Descripción: Persona de contacto en un cliente
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- client_id: UUID (FK)
- first_name: string
- last_name: string
- email: string
- phone: string
- position: string
- department: string
- is_primary: boolean
- status: enum [active, inactive]
- metadata: JSONB
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Client
- N:1 con Tenant
```
### Brand
```yaml
Entidad: Brand
Descripción: Marca de un cliente con su identidad visual
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- client_id: UUID (FK)
- name: string
- description: text
- status: enum [active, inactive]
- identity: JSONB
- logo_url: string
- logo_variations: array
- primary_color: string
- secondary_colors: array
- typography: object
- tone_of_voice: string (formal, casual, playful, etc.)
- keywords: array
- forbidden_words: array
- visual_style: string
- lora_models: array[UUID] (referencias a modelos entrenados)
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Client
- N:1 con Tenant
- 1:N con Product
- 1:N con Campaign
- N:N con CustomModel (LoRAs)
```
### Product
```yaml
Entidad: Product
Descripción: Producto o servicio de una marca
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- brand_id: UUID (FK)
- name: string
- sku: string
- description: text
- category: string
- status: enum [active, discontinued, coming_soon]
- attributes: JSONB
- price: decimal
- features: array
- target_audience: string
- use_cases: array
- reference_images: array[string] (URLs)
- lora_model_id: UUID (modelo específico del producto)
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Brand
- N:1 con Tenant
- 1:N con Asset (imágenes generadas)
```
### Opportunity
```yaml
Entidad: Opportunity
Descripción: Oportunidad de negocio/deal
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- client_id: UUID (FK)
- contact_id: UUID (FK, opcional)
- name: string
- description: text
- value: decimal
- currency: string
- stage: enum [lead, qualified, proposal, negotiation, won, lost]
- probability: integer (0-100)
- expected_close_date: date
- actual_close_date: date
- lost_reason: string
- notes: text
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Client
- N:1 con Contact
- N:1 con Tenant
- 1:N con Project (al convertirse)
```
---
## Funcionalidades
### F-002.1: Gestión de Clientes
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-002.1.1 | CRUD Clientes | Alta, edición, listado, eliminación | Alta |
| F-002.1.2 | Búsqueda y filtros | Por nombre, industria, estado | Alta |
| F-002.1.3 | Vista de cliente | Dashboard con marcas, contactos, proyectos | Alta |
| F-002.1.4 | Historial | Timeline de actividades y cambios | Media |
### F-002.2: Gestión de Contactos
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-002.2.1 | CRUD Contactos | Gestión de personas de contacto | Alta |
| F-002.2.2 | Contacto primario | Marcar contacto principal por cliente | Media |
| F-002.2.3 | Directorio | Vista consolidada de todos los contactos | Media |
### F-002.3: Gestión de Marcas
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-002.3.1 | CRUD Marcas | Alta y gestión de marcas | Alta |
| F-002.3.2 | Identidad visual | Definir colores, tipografía, tono | Alta |
| F-002.3.3 | Brand guidelines | Subir y almacenar guías de marca | Media |
| F-002.3.4 | Asociar LoRAs | Vincular modelos entrenados a marca | Alta |
### F-002.4: Gestión de Productos
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-002.4.1 | CRUD Productos | Alta y gestión de productos | Alta |
| F-002.4.2 | Imágenes referencia | Subir fotos del producto real | Alta |
| F-002.4.3 | Catálogo | Vista de productos por marca | Media |
| F-002.4.4 | Trigger generación | Botón "Generar pack de imágenes" | Alta |
### F-002.5: Pipeline de Ventas
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-002.5.1 | CRUD Oportunidades | Gestión de deals | Media |
| F-002.5.2 | Kanban pipeline | Vista drag & drop por etapas | Media |
| F-002.5.3 | Convertir a proyecto | Crear proyecto desde oportunidad ganada | Media |
---
## Reglas de Negocio
```yaml
RN-002.1:
Descripción: Cliente debe tener al menos un contacto
Validación: Warning si no hay contactos, no bloquea
RN-002.2:
Descripción: Marca requiere identidad visual mínima
Validación: Al menos logo_url o primary_color definido
RN-002.3:
Descripción: Producto hereda identidad de su marca
Comportamiento: Si no tiene LoRA propio, usa el de la marca
RN-002.4:
Descripción: Oportunidad ganada puede generar proyecto
Acción: Botón "Crear proyecto" disponible en stage=won
RN-002.5:
Descripción: Datos de marca se inyectan en generación
Comportamiento: Al generar contenido, se cargan automáticamente colores, tono, keywords
```
---
## API Endpoints
```yaml
Base: /api/v1/crm
# Clients
POST /clients
GET /clients
GET /clients/:id
PUT /clients/:id
DELETE /clients/:id
GET /clients/:id/brands
GET /clients/:id/contacts
GET /clients/:id/projects
# Contacts
POST /contacts
GET /contacts
GET /contacts/:id
PUT /contacts/:id
DELETE /contacts/:id
# Brands
POST /brands
GET /brands
GET /brands/:id
PUT /brands/:id
DELETE /brands/:id
GET /brands/:id/products
POST /brands/:id/loras # Asociar LoRA
DELETE /brands/:id/loras/:loraId # Desasociar LoRA
# Products
POST /products
GET /products
GET /products/:id
PUT /products/:id
DELETE /products/:id
POST /products/:id/generate # Trigger generación
# Opportunities
POST /opportunities
GET /opportunities
GET /opportunities/:id
PUT /opportunities/:id
DELETE /opportunities/:id
POST /opportunities/:id/convert # Convertir a proyecto
```
---
## Integraciones
### Con Motor de Generación (PMC-004)
```yaml
Trigger: POST /products/:id/generate
Payload:
product_id: UUID
brand_id: UUID
workflow_template: string
options:
quantity: number
formats: array
Comportamiento:
1. Cargar datos de producto (nombre, descripción, referencias)
2. Cargar identidad de marca (colores, tono, LoRAs)
3. Encolar job de generación con parámetros combinados
```
### Con Proyectos (PMC-003)
```yaml
Trigger: POST /opportunities/:id/convert
Crea:
- Proyecto vinculado al cliente
- Campaña inicial (opcional)
- Copia brief de la oportunidad
```
---
## Dependencias
```yaml
Dependencias de Módulos:
- PMC-001 Tenants: Aislamiento de datos
- PMC-003 Projects: Conversión de oportunidades
- PMC-004 Generation: Trigger de generación
- PMC-006 Assets: Almacenamiento de logos y referencias
Servicios Externos:
- Storage (S3/MinIO): Logos, imágenes de referencia
```
---
## UI/UX Consideraciones
```yaml
Vistas principales:
- Lista de clientes con filtros y búsqueda
- Ficha de cliente con tabs (info, contactos, marcas, proyectos)
- Lista de marcas con preview de identidad
- Ficha de producto con galería de referencias
- Pipeline kanban de oportunidades
Acciones rápidas:
- Desde producto: "Generar contenido"
- Desde cliente: "Nueva marca", "Nuevo proyecto"
- Desde oportunidad: "Convertir a proyecto"
```
---
## Criterios de Aceptación
- [ ] CRUD completo para Clients, Contacts, Brands, Products, Opportunities
- [ ] Identidad de marca se almacena y muestra correctamente
- [ ] Trigger de generación funciona desde producto
- [ ] Pipeline de oportunidades permite drag & drop
- [ ] Conversión de oportunidad crea proyecto
- [ ] Búsqueda y filtros funcionan en todas las listas
- [ ] RLS aísla datos por tenant
---
## Referencias
- [VISION-GENERAL.md](../00-vision-general/VISION-GENERAL.md)
- [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md)
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,381 @@
# PMC-003: Módulo de Projects
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Definición
**Prioridad:** Alta
---
## Descripción General
El módulo de Projects gestiona proyectos y campañas de marketing. Cada proyecto agrupa múltiples campañas, y cada campaña contiene un brief estructurado que guía la generación de contenido.
---
## Objetivos
1. Organizar el trabajo por proyectos y campañas
2. Estructurar briefs creativos para generación de contenido
3. Gestionar estados y flujos de aprobación
4. Vincular proyectos con clientes y marcas del CRM
5. Coordinar entregas de assets generados
---
## Entidades del Dominio
### Project
```yaml
Entidad: Project
Descripción: Contenedor de campañas para un cliente/iniciativa
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- client_id: UUID (FK)
- name: string
- description: text
- code: string (identificador corto, ej: "PRJ-2025-001")
- status: enum [draft, active, on_hold, completed, cancelled]
- start_date: date
- end_date: date
- budget: decimal
- currency: string
- owner_id: UUID (FK a User, responsable)
- team_members: array[UUID] (usuarios asignados)
- settings: JSONB
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Tenant
- N:1 con Client
- N:1 con User (owner)
- 1:N con Campaign
- 1:N con Asset (assets del proyecto)
```
### Campaign
```yaml
Entidad: Campaign
Descripción: Campaña de marketing con brief y assets
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- project_id: UUID (FK)
- brand_id: UUID (FK)
- name: string
- description: text
- type: enum [social_media, performance_ads, catalog, landing, email, other]
- status: enum [draft, briefing, in_production, review, approved, published, archived]
- brief: JSONB (ver estructura abajo)
- channels: array[string] (instagram, facebook, tiktok, google_ads, etc.)
- start_date: date
- end_date: date
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Project
- N:1 con Brand
- N:1 con Tenant
- 1:N con GenerationJob
- 1:N con Asset
```
### Brief (JSONB Structure)
```yaml
Estructura: Campaign.brief
Campos:
objective:
description: string (objetivo de la campaña)
kpis: array[string] (métricas de éxito)
audience:
demographics: string
psychographics: string
pain_points: array[string]
desires: array[string]
messaging:
main_message: string
tone_of_voice: string (override del brand)
call_to_action: string
hashtags: array[string]
visual:
style: string (fotográfico, ilustrado, minimalista, etc.)
mood: string (energético, sereno, profesional, etc.)
color_palette: array[string] (override o adicionales)
references: array[string] (URLs de referencias visuales)
constraints:
forbidden_words: array[string]
forbidden_elements: array[string]
legal_disclaimers: array[string]
brand_guidelines_url: string
deliverables:
formats: array[object]
- type: string (post, story, banner, etc.)
dimensions: string (1080x1080, 1080x1920, etc.)
quantity: number
total_images: number
total_copies: number
variations_per_piece: number
```
### CampaignAsset
```yaml
Entidad: CampaignAsset (tabla pivote)
Descripción: Relación entre campaña y assets generados
Atributos:
- id: UUID (PK)
- campaign_id: UUID (FK)
- asset_id: UUID (FK)
- status: enum [pending, approved, rejected, revision_requested]
- feedback: text
- approved_by: UUID (FK a User)
- approved_at: timestamp
- created_at: timestamp
```
---
## Funcionalidades
### F-003.1: Gestión de Proyectos
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-003.1.1 | CRUD Proyectos | Alta, edición, listado, archivado | Alta |
| F-003.1.2 | Dashboard proyecto | Vista general con campañas y progreso | Alta |
| F-003.1.3 | Asignar equipo | Agregar/quitar miembros al proyecto | Media |
| F-003.1.4 | Timeline | Visualización de fechas y milestones | Media |
### F-003.2: Gestión de Campañas
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-003.2.1 | CRUD Campañas | Alta, edición, listado | Alta |
| F-003.2.2 | Editor de brief | Formulario estructurado | Alta |
| F-003.2.3 | Plantillas de brief | Briefs predefinidos por tipo | Media |
| F-003.2.4 | Duplicar campaña | Clonar con modificaciones | Media |
### F-003.3: Flujo de Trabajo
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-003.3.1 | Cambio de estado | Transiciones controladas | Alta |
| F-003.3.2 | Notificaciones | Alertar cambios de estado | Media |
| F-003.3.3 | Aprobación de assets | Aprobar/rechazar contenido | Alta |
| F-003.3.4 | Solicitar revisión | Pedir cambios con feedback | Alta |
### F-003.4: Generación de Contenido
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-003.4.1 | Lanzar generación | Desde brief, iniciar jobs | Alta |
| F-003.4.2 | Seleccionar plantillas | Elegir workflows de generación | Alta |
| F-003.4.3 | Monitor de progreso | Ver estado de generaciones | Alta |
| F-003.4.4 | Regenerar | Volver a generar con ajustes | Media |
---
## Máquina de Estados
### Project Status
```
draft ─────────────────────► active
│ │
│ ├───► on_hold ───► active
│ │
│ └───► completed
└────────────────────────────────► cancelled
```
### Campaign Status
```
draft ───► briefing ───► in_production ───► review ───► approved ───► published
│ │ │ │ │
│ │ └────────────────┘ │
│ │ (revision_requested) │
│ │ │
└───────────┴────────────────────────────────────────────┴───► archived
```
---
## Reglas de Negocio
```yaml
RN-003.1:
Descripción: Proyecto requiere cliente asignado
Validación: client_id NOT NULL
RN-003.2:
Descripción: Campaña requiere brief mínimo para pasar a producción
Validación: brief.objective y brief.deliverables definidos
RN-003.3:
Descripción: Solo owner o admin puede cambiar estado del proyecto
Autorización: Verificar rol del usuario
RN-003.4:
Descripción: Assets aprobados no pueden eliminarse
Validación: Bloquear DELETE si status=approved
RN-003.5:
Descripción: Brief hereda identidad de la marca
Comportamiento: Cargar colores, tono, etc. de Brand al crear campaña
RN-003.6:
Descripción: Campaña solo puede publicarse si tiene assets aprobados
Validación: Al menos 1 asset con status=approved
```
---
## API Endpoints
```yaml
Base: /api/v1/projects
# Projects
POST /projects
GET /projects
GET /projects/:id
PUT /projects/:id
DELETE /projects/:id
PATCH /projects/:id/status
GET /projects/:id/campaigns
GET /projects/:id/assets
POST /projects/:id/team # Agregar miembro
DELETE /projects/:id/team/:userId
# Campaigns
POST /campaigns
GET /campaigns
GET /campaigns/:id
PUT /campaigns/:id
DELETE /campaigns/:id
PATCH /campaigns/:id/status
PUT /campaigns/:id/brief
GET /campaigns/:id/assets
POST /campaigns/:id/generate # Lanzar generación
# Campaign Assets
POST /campaigns/:id/assets/:assetId/approve
POST /campaigns/:id/assets/:assetId/reject
POST /campaigns/:id/assets/:assetId/request-revision
# Brief Templates
GET /brief-templates
GET /brief-templates/:id
POST /brief-templates # Admin only
PUT /brief-templates/:id
DELETE /brief-templates/:id
```
---
## Integraciones
### Con CRM (PMC-002)
```yaml
Relación: Proyecto vinculado a Cliente y Marca
Datos heredados:
- Identidad visual de Brand
- Información de productos
- Contactos para notificaciones
```
### Con Motor de Generación (PMC-004)
```yaml
Trigger: POST /campaigns/:id/generate
Payload:
campaign_id: UUID
workflow_templates: array[string]
options:
use_brand_lora: boolean
quality: string
Respuesta:
job_ids: array[UUID]
estimated_time: number
```
### Con DAM (PMC-006)
```yaml
Assets generados se almacenan en DAM
Vinculación automática campaign_id → asset
Metadatos del brief copiados al asset
```
### Con Automatización (PMC-005)
```yaml
Eventos disparados:
- campaign.status_changed → Notificaciones
- campaign.approved → Preparar entrega
- assets.all_approved → Trigger siguiente paso
```
---
## UI/UX Consideraciones
```yaml
Vistas principales:
- Lista de proyectos con filtros (cliente, estado, fecha)
- Board kanban de campañas por estado
- Editor de brief con preview
- Galería de assets de campaña con acciones
Acciones rápidas:
- "Nueva campaña" desde proyecto
- "Generar contenido" desde campaña
- "Aprobar todo" en vista de revisión
- "Descargar pack" de assets aprobados
Componentes:
- BriefEditor: Formulario estructurado con secciones colapsables
- AssetReviewer: Galería con zoom, comparación, acciones
- ProgressTracker: Timeline visual de estado de campaña
```
---
## Criterios de Aceptación
- [ ] CRUD completo para Projects y Campaigns
- [ ] Editor de brief funcional con todas las secciones
- [ ] Transiciones de estado respetan reglas
- [ ] Generación se lanza correctamente desde campaña
- [ ] Flujo de aprobación de assets funciona
- [ ] Assets rechazados permiten regeneración
- [ ] Notificaciones se disparan en cambios de estado
- [ ] Plantillas de brief funcionan correctamente
---
## Referencias
- [VISION-GENERAL.md](../00-vision-general/VISION-GENERAL.md)
- [PMC-002-CRM.md](./PMC-002-CRM.md)
- [PMC-004-GENERATION.md](./PMC-004-GENERATION.md)
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,489 @@
# PMC-004: Módulo de Generation
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Definición
**Prioridad:** Alta
---
## Descripción General
El módulo de Generation es el núcleo de la plataforma. Gestiona la generación de contenido mediante IA, incluyendo imágenes con Stable Diffusion/ComfyUI y textos con LLMs. Orquesta workflows, colas de tareas, y la integración con modelos personalizados (LoRAs).
---
## Objetivos
1. Generar imágenes de alta calidad para marketing
2. Generar copys y textos publicitarios
3. Ejecutar workflows predefinidos de ComfyUI
4. Gestionar modelos personalizados (LoRAs, checkpoints)
5. Mantener consistencia de marca en generaciones
6. Procesar tareas en cola con prioridades
---
## Entidades del Dominio
### GenerationJob
```yaml
Entidad: GenerationJob
Descripción: Tarea de generación en cola
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- campaign_id: UUID (FK, opcional)
- product_id: UUID (FK, opcional)
- user_id: UUID (FK, quien solicitó)
- type: enum [image, text, image_batch, mixed]
- status: enum [queued, processing, completed, failed, cancelled]
- priority: integer (1-10, mayor = más urgente)
- workflow_id: UUID (FK a WorkflowTemplate)
- input_params: JSONB (parámetros de entrada)
- output_assets: array[UUID] (assets generados)
- error_message: text
- progress: integer (0-100)
- started_at: timestamp
- completed_at: timestamp
- created_at: timestamp
Relaciones:
- N:1 con Tenant
- N:1 con Campaign
- N:1 con Product
- N:1 con User
- N:1 con WorkflowTemplate
- 1:N con Asset (outputs)
```
### WorkflowTemplate
```yaml
Entidad: WorkflowTemplate
Descripción: Plantilla de workflow de generación
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK, null = global)
- name: string
- description: text
- type: enum [product_photo, social_post, banner, avatar, variation, custom]
- category: string (para organización)
- comfyui_workflow: JSONB (definición del workflow)
- input_schema: JSONB (parámetros esperados)
- output_config: JSONB
- format: string (png, jpg, webp)
- dimensions: array[string]
- quantity_default: number
- models_required: array[string] (checkpoints, LoRAs requeridos)
- estimated_time_seconds: integer
- is_active: boolean
- is_system: boolean (plantilla del sistema vs personalizada)
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Tenant (opcional)
- 1:N con GenerationJob
```
### CustomModel
```yaml
Entidad: CustomModel
Descripción: Modelo personalizado (LoRA, checkpoint, embedding)
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- brand_id: UUID (FK, opcional)
- name: string
- type: enum [lora, checkpoint, embedding, controlnet]
- purpose: string (product, avatar, style, character)
- description: text
- file_path: string (ruta en storage)
- file_size: bigint
- status: enum [training, ready, failed, archived]
- training_params: JSONB
- base_model: string
- steps: number
- learning_rate: number
- training_images: array[string]
- trigger_word: string (palabra para activar en prompt)
- preview_images: array[string]
- metadata: JSONB
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Tenant
- N:1 con Brand (opcional)
- N:N con WorkflowTemplate
```
### TextGeneration
```yaml
Entidad: TextGeneration
Descripción: Generación de texto/copy
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- job_id: UUID (FK a GenerationJob, opcional)
- campaign_id: UUID (FK, opcional)
- type: enum [copy, title, description, hashtags, cta, full_post]
- prompt: text (instrucción al LLM)
- context: JSONB (datos de marca, producto, brief)
- output: text
- variations: array[text] (si se generaron múltiples)
- model_used: string
- tokens_used: integer
- status: enum [pending, completed, failed]
- created_at: timestamp
Relaciones:
- N:1 con Tenant
- N:1 con GenerationJob
- N:1 con Campaign
```
---
## Funcionalidades
### F-004.1: Generación de Imágenes
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-004.1.1 | Text-to-Image | Generar imagen desde prompt | Alta |
| F-004.1.2 | Image-to-Image | Transformar imagen existente | Alta |
| F-004.1.3 | Inpainting | Editar partes de una imagen | Media |
| F-004.1.4 | Upscaling | Aumentar resolución | Alta |
| F-004.1.5 | Batch generation | Generar múltiples variaciones | Alta |
### F-004.2: Generación de Texto
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-004.2.1 | Copy generation | Generar textos publicitarios | Alta |
| F-004.2.2 | Hashtag generation | Sugerir hashtags relevantes | Media |
| F-004.2.3 | Title generation | Crear títulos/headlines | Alta |
| F-004.2.4 | Tone adaptation | Ajustar tono según brief | Alta |
### F-004.3: Workflows
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-004.3.1 | Plantillas predefinidas | Workflows listos para usar | Alta |
| F-004.3.2 | Ejecutar workflow | Correr workflow con parámetros | Alta |
| F-004.3.3 | Crear plantillas | Admin puede crear nuevos workflows | Media |
| F-004.3.4 | Previsualizar | Ver ejemplo antes de ejecutar | Media |
### F-004.4: Modelos Personalizados
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-004.4.1 | Registrar LoRA | Subir modelo entrenado | Alta |
| F-004.4.2 | Entrenar LoRA | Iniciar entrenamiento (básico) | Media |
| F-004.4.3 | Asociar a marca | Vincular modelo con brand | Alta |
| F-004.4.4 | Selector de modelos | Elegir LoRAs en generación | Alta |
### F-004.5: Cola de Tareas
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-004.5.1 | Encolar job | Agregar tarea a la cola | Alta |
| F-004.5.2 | Priorizar | Ajustar prioridad de jobs | Media |
| F-004.5.3 | Monitor | Ver estado de cola | Alta |
| F-004.5.4 | Cancelar | Cancelar job pendiente | Media |
| F-004.5.5 | Reintentar | Re-ejecutar job fallido | Media |
---
## Workflows Predefinidos (MVP)
### WF-001: Fotografía Sintética de Producto
```yaml
Nombre: product_photo_synthetic
Descripción: Genera fotos de producto en contextos comerciales
Inputs:
- product_description: string
- reference_images: array[string] (opcional)
- background: string (white, lifestyle, custom)
- style: string (minimalist, premium, casual)
- brand_colors: array[string]
- lora_id: UUID (opcional)
Outputs:
- 5 variaciones del producto
- Formato: PNG 1024x1024
- Fondo transparente disponible
ComfyUI Nodes:
- SDXL Base
- ControlNet (si hay referencia)
- IP-Adapter (consistencia)
- Background Removal
- Upscaler
```
### WF-002: Post para Redes Sociales
```yaml
Nombre: social_media_post
Descripción: Genera imagen + copy para redes
Inputs:
- brief: object (objetivo, audiencia, tono)
- product_id: UUID (opcional)
- channel: string (instagram, facebook, linkedin)
- format: string (post, story, carousel)
- brand_id: UUID
Outputs:
- 3-5 variaciones de imagen
- Copy sugerido por variación
- Hashtags recomendados
Proceso:
1. Cargar identidad de marca
2. Generar imagen base con SDXL + LoRA
3. Aplicar composición según formato
4. Generar copy con LLM
5. Combinar outputs
```
### WF-003: Variaciones de Anuncio
```yaml
Nombre: ad_variations
Descripción: Genera múltiples versiones para A/B testing
Inputs:
- base_image: string (URL o asset_id)
- variations_count: number
- variation_type: string (color, background, composition)
Outputs:
- N variaciones según configuración
- Metadatos de diferencias
Uso:
- Testing de creatividades
- Adaptaciones por canal
```
### WF-004: Avatar/Influencer Virtual
```yaml
Nombre: virtual_avatar
Descripción: Genera imágenes consistentes de un personaje
Inputs:
- character_lora_id: UUID
- pose: string (standing, sitting, action)
- outfit: string
- background: string
- expression: string
Outputs:
- Imagen del avatar
- Consistencia facial garantizada
Técnicas:
- IP-Adapter para consistencia
- ControlNet OpenPose para poses
- LoRA específico del personaje
```
---
## Arquitectura de Integración ComfyUI
```yaml
Componentes:
Backend (NestJS):
- GenerationService: Lógica de negocio
- QueueService: Gestión de cola (Bull)
- ComfyUIClient: Cliente HTTP para ComfyUI
ComfyUI Server:
- API REST nativa o ComfyDeploy
- Websocket para progreso
- Storage compartido para outputs
Flujo:
1. Backend recibe solicitud de generación
2. Valida permisos y límites del tenant
3. Construye payload del workflow
4. Encola job en Bull/Redis
5. Worker toma job y llama a ComfyUI
6. ComfyUI ejecuta workflow
7. Worker recibe resultado y crea Asset
8. Notifica completion via websocket
```
### Ejemplo de Llamada a ComfyUI
```typescript
interface ComfyUIRequest {
workflow_id: string;
inputs: {
positive_prompt: string;
negative_prompt: string;
seed: number;
steps: number;
cfg_scale: number;
width: number;
height: number;
lora_name?: string;
lora_strength?: number;
controlnet_image?: string;
};
webhook_url: string;
}
// Respuesta
interface ComfyUIResponse {
job_id: string;
status: 'queued' | 'processing' | 'completed' | 'failed';
outputs?: {
images: string[]; // URLs
};
error?: string;
}
```
---
## API Endpoints
```yaml
Base: /api/v1/generation
# Jobs
POST /jobs # Crear job de generación
GET /jobs # Listar jobs del usuario
GET /jobs/:id # Detalle de job
DELETE /jobs/:id # Cancelar job
POST /jobs/:id/retry # Reintentar job fallido
# Quick Generate (shortcuts)
POST /generate/image # Generación rápida de imagen
POST /generate/text # Generación rápida de texto
POST /generate/batch # Batch de imágenes
# Workflows
GET /workflows # Listar plantillas disponibles
GET /workflows/:id # Detalle de plantilla
POST /workflows # Crear plantilla (admin)
PUT /workflows/:id # Editar plantilla
POST /workflows/:id/execute # Ejecutar workflow
# Custom Models
GET /models # Listar modelos del tenant
GET /models/:id # Detalle de modelo
POST /models # Registrar modelo
DELETE /models/:id # Eliminar modelo
POST /models/train # Iniciar entrenamiento
# Queue Management (admin)
GET /queue/status # Estado de la cola
GET /queue/jobs # Jobs en cola
POST /queue/jobs/:id/priority # Cambiar prioridad
```
---
## Reglas de Negocio
```yaml
RN-004.1:
Descripción: Generaciones limitadas por plan del tenant
Validación: Verificar cuota antes de encolar
Acción: Rechazar si límite alcanzado
RN-004.2:
Descripción: LoRA de marca se aplica automáticamente
Comportamiento: Si brand tiene LoRA y no se especifica otro, usar el de marca
RN-004.3:
Descripción: Jobs prioritarios para planes premium
Cálculo: priority_base + plan_bonus
RN-004.4:
Descripción: Outputs se almacenan 30 días mínimo
Política: Assets generados tienen retención mínima
RN-004.5:
Descripción: Entrenamiento requiere mínimo 10 imágenes
Validación: Verificar cantidad antes de iniciar
RN-004.6:
Descripción: Negative prompts por defecto
Comportamiento: Agregar prompts negativos estándar de calidad
```
---
## Dependencias
```yaml
Dependencias de Módulos:
- PMC-001 Tenants: Límites y cuotas
- PMC-002 CRM: Datos de marca y producto
- PMC-003 Projects: Contexto de campaña
- PMC-006 Assets: Almacenamiento de outputs
Servicios Externos:
- ComfyUI: Motor de generación de imágenes
- OpenAI/Claude API: Generación de texto
- Redis: Cola de tareas
- S3/MinIO: Almacenamiento de modelos y outputs
Dependencias del Catálogo:
- @CATALOG_RATELIMIT: Rate limiting por tenant
```
---
## Consideraciones de Performance
```yaml
Optimizaciones:
- Cola con workers paralelos según GPUs disponibles
- Cache de modelos frecuentes en VRAM
- Batch processing cuando sea posible
- Compresión de outputs antes de storage
Monitoreo:
- Tiempo promedio por tipo de workflow
- Utilización de GPU
- Cola depth y wait time
- Tasa de errores
```
---
## Criterios de Aceptación
- [ ] Generación de imagen funciona con SDXL base
- [ ] Workflows predefinidos ejecutan correctamente
- [ ] LoRAs se cargan y aplican correctamente
- [ ] Cola procesa jobs en orden de prioridad
- [ ] Progreso se reporta via websocket
- [ ] Jobs fallidos permiten reintento
- [ ] Límites de tenant se respetan
- [ ] Outputs se almacenan como Assets
- [ ] Generación de texto produce copys coherentes
---
## Referencias
- [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md)
- [ComfyUI Documentation](https://github.com/comfyanonymous/ComfyUI)
- [ComfyDeploy](https://www.comfydeploy.com/)
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,447 @@
# PMC-005: Módulo de Automation
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Definición
**Prioridad:** Media
---
## Descripción General
El módulo de Automation gestiona flujos automatizados entre los diferentes componentes de la plataforma. Utiliza n8n como orquestador principal para conectar CRM, motor de generación, notificaciones y sistemas externos.
---
## Objetivos
1. Automatizar flujos creativos desde brief hasta entrega
2. Integrar CRM con motor de generación
3. Disparar acciones basadas en eventos del sistema
4. Conectar con servicios externos (email, redes, etc.)
5. Reducir tareas manuales repetitivas
---
## Entidades del Dominio
### AutomationFlow
```yaml
Entidad: AutomationFlow
Descripción: Definición de un flujo automatizado
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- name: string
- description: text
- type: enum [trigger_based, scheduled, manual]
- trigger_event: string (evento que dispara el flujo)
- n8n_workflow_id: string (ID del workflow en n8n)
- is_active: boolean
- last_run: timestamp
- run_count: integer
- config: JSONB
- retry_on_failure: boolean
- max_retries: number
- timeout_seconds: number
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Tenant
- 1:N con AutomationRun
```
### AutomationRun
```yaml
Entidad: AutomationRun
Descripción: Ejecución de un flujo automatizado
Atributos:
- id: UUID (PK)
- flow_id: UUID (FK)
- tenant_id: UUID (FK)
- status: enum [running, completed, failed, cancelled]
- trigger_data: JSONB (datos del evento que disparó)
- output_data: JSONB (resultados)
- error_message: text
- started_at: timestamp
- completed_at: timestamp
- duration_ms: integer
Relaciones:
- N:1 con AutomationFlow
- N:1 con Tenant
```
### WebhookEndpoint
```yaml
Entidad: WebhookEndpoint
Descripción: Endpoint para recibir eventos externos
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- name: string
- slug: string (parte de la URL)
- secret_key: string (para validación)
- target_flow_id: UUID (FK)
- is_active: boolean
- last_called: timestamp
- call_count: integer
- created_at: timestamp
Relaciones:
- N:1 con Tenant
- N:1 con AutomationFlow
```
---
## Eventos del Sistema
### Eventos Disponibles para Triggers
```yaml
CRM Events:
- client.created
- client.updated
- brand.created
- brand.updated
- product.created
- product.updated
- opportunity.stage_changed
- opportunity.won
- opportunity.lost
Project Events:
- project.created
- project.status_changed
- campaign.created
- campaign.status_changed
- campaign.brief_completed
- campaign.approved
Generation Events:
- job.completed
- job.failed
- batch.completed
- model.training_completed
Asset Events:
- asset.created
- asset.approved
- asset.rejected
- all_assets.approved (todos los assets de campaña)
User Events:
- user.created
- user.invited
- user.activated
```
---
## Flujos Predefinidos (MVP)
### FLOW-001: Nuevo Producto → Generar Kit de Assets
```yaml
Nombre: product_asset_kit
Trigger: product.created
Descripción: Al crear producto, genera pack de imágenes automáticamente
Pasos:
1. Recibir evento product.created
2. Cargar datos del producto y su marca
3. Verificar si tiene imágenes de referencia
4. Llamar a GenerationService con workflow "product_photo_synthetic"
5. Esperar completación del job
6. Notificar al usuario creador
Configuración:
images_count: 5
auto_approve: false
notify_on_complete: true
```
### FLOW-002: Nueva Campaña → Generar Lote Inicial
```yaml
Nombre: campaign_initial_batch
Trigger: campaign.brief_completed
Descripción: Al completar brief, genera creatividades iniciales
Pasos:
1. Recibir evento campaign.brief_completed
2. Extraer deliverables del brief
3. Para cada formato solicitado:
- Llamar a GenerationService
- Generar imágenes según especificaciones
4. Generar copys con LLM
5. Vincular assets a la campaña
6. Cambiar estado campaña a "in_production"
7. Notificar al equipo asignado
Configuración:
parallel_generations: true
generate_copies: true
auto_link_assets: true
```
### FLOW-003: Campaña Aprobada → Preparar Entrega
```yaml
Nombre: campaign_delivery_prep
Trigger: campaign.approved
Descripción: Prepara paquete de entrega al aprobar campaña
Pasos:
1. Recibir evento campaign.approved
2. Recopilar todos los assets aprobados
3. Generar ZIP con estructura organizada
4. Crear enlace de descarga temporal
5. Si cliente tiene acceso al portal:
- Crear notificación en portal
6. Enviar email al contacto del cliente
7. Registrar entrega en historial
Configuración:
zip_structure: "by_format" | "flat"
include_copies: true
link_expiry_days: 7
notify_client: true
```
### FLOW-004: Job Fallido → Notificar y Reintentar
```yaml
Nombre: job_failure_handler
Trigger: job.failed
Descripción: Maneja fallos de generación
Pasos:
1. Recibir evento job.failed
2. Evaluar tipo de error
3. Si error transitorio (GPU, timeout):
- Reintentar hasta max_retries
4. Si error persistente:
- Notificar al usuario
- Crear ticket/tarea de revisión
5. Registrar en logs de auditoría
Configuración:
max_retries: 3
retry_delay_seconds: 60
notify_on_permanent_failure: true
```
---
## Funcionalidades
### F-005.1: Gestión de Flujos
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-005.1.1 | Listar flujos | Ver flujos disponibles y activos | Alta |
| F-005.1.2 | Activar/Desactivar | Toggle de flujos | Alta |
| F-005.1.3 | Configurar | Ajustar parámetros de flujo | Media |
| F-005.1.4 | Ver historial | Ejecuciones pasadas | Media |
### F-005.2: Ejecución Manual
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-005.2.1 | Ejecutar ahora | Disparar flujo manualmente | Media |
| F-005.2.2 | Test run | Ejecutar en modo prueba | Baja |
| F-005.2.3 | Cancelar | Detener ejecución en curso | Media |
### F-005.3: Webhooks
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-005.3.1 | Crear endpoint | Generar URL de webhook | Media |
| F-005.3.2 | Validar payload | Verificar firma/secret | Media |
| F-005.3.3 | Ver logs | Historial de llamadas | Baja |
### F-005.4: Integraciones Externas
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-005.4.1 | Email | Envío de notificaciones | Alta |
| F-005.4.2 | Slack | Notificaciones a canales | Media |
| F-005.4.3 | CRM externo | Sync bidireccional | Baja |
---
## Arquitectura de Integración n8n
```yaml
Componentes:
Backend (NestJS):
- EventEmitter: Emite eventos del sistema
- WebhookController: Recibe llamadas de n8n
- AutomationService: Gestiona flujos y ejecuciones
n8n Server:
- Workflows definidos
- Credenciales de integración
- Webhooks de entrada/salida
Comunicación:
Backend → n8n: Webhooks HTTP POST
n8n → Backend: API calls con auth token
Flujo:
1. Evento ocurre en Backend (ej: product.created)
2. EventEmitter notifica a AutomationService
3. AutomationService busca flujos suscritos al evento
4. Para cada flujo activo:
- POST a webhook de n8n con datos del evento
5. n8n ejecuta workflow
6. n8n llama a API del Backend para acciones
7. n8n reporta resultado via webhook de completion
```
### Ejemplo de Webhook Payload
```json
{
"event": "product.created",
"timestamp": "2025-12-08T10:30:00Z",
"tenant_id": "uuid-tenant",
"data": {
"product_id": "uuid-product",
"brand_id": "uuid-brand",
"name": "Producto X",
"description": "...",
"reference_images": ["url1", "url2"]
},
"metadata": {
"user_id": "uuid-user",
"source": "api"
}
}
```
---
## API Endpoints
```yaml
Base: /api/v1/automation
# Flows
GET /flows # Listar flujos
GET /flows/:id # Detalle de flujo
POST /flows/:id/activate # Activar flujo
POST /flows/:id/deactivate # Desactivar flujo
PUT /flows/:id/config # Configurar flujo
POST /flows/:id/execute # Ejecutar manualmente
# Runs
GET /runs # Historial de ejecuciones
GET /runs/:id # Detalle de ejecución
POST /runs/:id/cancel # Cancelar ejecución
# Webhooks
GET /webhooks # Listar endpoints
POST /webhooks # Crear endpoint
DELETE /webhooks/:id # Eliminar endpoint
POST /webhooks/:id/regenerate # Regenerar secret
# Incoming webhooks (public, con autenticación por secret)
POST /hooks/:tenant_slug/:webhook_slug
```
---
## Reglas de Negocio
```yaml
RN-005.1:
Descripción: Flujos desactivados no procesan eventos
Comportamiento: Eventos ignorados si flow.is_active = false
RN-005.2:
Descripción: Reintentos con backoff exponencial
Cálculo: delay = retry_delay * (2 ^ attempt_number)
RN-005.3:
Descripción: Ejecuciones fallidas notifican a admins
Acción: Email + notificación in-app a usuarios con rol admin
RN-005.4:
Descripción: Webhooks requieren validación de secret
Validación: HMAC-SHA256 del payload con secret key
RN-005.5:
Descripción: Límite de ejecuciones por hora
Validación: Rate limit según plan del tenant
```
---
## Dependencias
```yaml
Dependencias de Módulos:
- PMC-001 Tenants: Configuración y límites
- PMC-002 CRM: Eventos de clientes/productos
- PMC-003 Projects: Eventos de campañas
- PMC-004 Generation: Llamadas a generación
- PMC-006 Assets: Gestión de outputs
Servicios Externos:
- n8n: Orquestador de workflows
- SMTP/SendGrid: Envío de emails
- Slack API: Notificaciones (opcional)
```
---
## Consideraciones de Seguridad
```yaml
Autenticación:
- Webhooks internos usan JWT del sistema
- Webhooks externos usan HMAC con secret por endpoint
- n8n autenticado con API key exclusiva
Aislamiento:
- Flujos aislados por tenant
- Eventos solo visible para el tenant que los genera
Validación:
- Payload size máximo: 1MB
- Rate limiting por endpoint
- Timeout de ejecución: 5 minutos
```
---
## Criterios de Aceptación
- [ ] Flujos predefinidos funcionan correctamente
- [ ] Eventos del sistema disparan flujos suscritos
- [ ] n8n recibe y procesa webhooks
- [ ] Ejecuciones se registran con estado y resultado
- [ ] Reintentos automáticos funcionan en errores transitorios
- [ ] Webhooks externos validan correctamente el secret
- [ ] Notificaciones se envían según configuración
- [ ] Rate limiting funciona según plan
---
## Referencias
- [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md)
- [n8n Documentation](https://docs.n8n.io/)
- [PMC-004-GENERATION.md](./PMC-004-GENERATION.md)
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,449 @@
# PMC-006: Módulo de Assets (DAM)
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Definición
**Prioridad:** Alta
---
## Descripción General
El módulo de Assets implementa un Digital Asset Management (DAM) simplificado para almacenar, organizar y gestionar todos los recursos digitales generados o subidos a la plataforma: imágenes, copys, videos, modelos IA, y documentos.
---
## Objetivos
1. Centralizar todos los activos digitales del tenant
2. Organizar assets por cliente, campaña, tipo
3. Gestionar versiones y estados de aprobación
4. Facilitar búsqueda y descubrimiento de assets
5. Controlar acceso y permisos por rol
---
## Entidades del Dominio
### Asset
```yaml
Entidad: Asset
Descripción: Recurso digital almacenado en la plataforma
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- name: string
- description: text
- type: enum [image, copy, video, document, model, template]
- mime_type: string
- file_path: string (ruta en storage)
- file_size: bigint (bytes)
- dimensions: JSONB (width, height para imágenes/videos)
- duration: integer (segundos, para video/audio)
- status: enum [draft, pending_review, approved, rejected, archived]
- visibility: enum [private, team, client]
- source: enum [generated, uploaded, imported]
- generation_job_id: UUID (FK, si fue generado)
- metadata: JSONB
- prompt: string (si fue generado)
- model_used: string
- seed: number
- parameters: object
- tags: array[string]
- created_by: UUID (FK a User)
- created_at: timestamp
- updated_at: timestamp
- deleted_at: timestamp (soft delete)
Relaciones:
- N:1 con Tenant
- N:1 con User (creator)
- N:1 con GenerationJob
- N:N con Campaign
- N:N con Collection
- 1:N con AssetVersion
```
### AssetVersion
```yaml
Entidad: AssetVersion
Descripción: Versión histórica de un asset
Atributos:
- id: UUID (PK)
- asset_id: UUID (FK)
- version_number: integer
- file_path: string
- file_size: bigint
- changes_description: text
- created_by: UUID (FK)
- created_at: timestamp
Relaciones:
- N:1 con Asset
- N:1 con User
```
### Collection
```yaml
Entidad: Collection
Descripción: Agrupación lógica de assets
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- name: string
- description: text
- type: enum [manual, smart, campaign, brand]
- smart_filters: JSONB (criterios para smart collections)
- cover_asset_id: UUID (FK, opcional)
- is_public: boolean
- created_by: UUID (FK)
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Tenant
- N:N con Asset
- N:1 con User
```
### AssetComment
```yaml
Entidad: AssetComment
Descripción: Comentario o feedback sobre un asset
Atributos:
- id: UUID (PK)
- asset_id: UUID (FK)
- user_id: UUID (FK)
- content: text
- position: JSONB (x, y para comentarios en imagen)
- is_resolved: boolean
- parent_id: UUID (FK, para respuestas)
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Asset
- N:1 con User
- Self-referencial (parent/children)
```
### Download
```yaml
Entidad: Download
Descripción: Registro de descargas de assets
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- asset_id: UUID (FK, opcional - puede ser colección)
- collection_id: UUID (FK, opcional)
- user_id: UUID (FK)
- download_type: enum [single, batch, collection]
- format: string (original, converted)
- ip_address: string
- user_agent: string
- created_at: timestamp
Relaciones:
- N:1 con Asset
- N:1 con Collection
- N:1 con User
```
---
## Funcionalidades
### F-006.1: Gestión de Assets
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-006.1.1 | Upload | Subir archivos manualmente | Alta |
| F-006.1.2 | Ver asset | Detalle con preview y metadata | Alta |
| F-006.1.3 | Editar metadata | Nombre, descripción, tags | Alta |
| F-006.1.4 | Eliminar | Soft delete con papelera | Alta |
| F-006.1.5 | Restaurar | Recuperar de papelera | Media |
### F-006.2: Organización
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-006.2.1 | Colecciones | Agrupar assets manualmente | Alta |
| F-006.2.2 | Smart collections | Colecciones automáticas por criterios | Media |
| F-006.2.3 | Tags | Etiquetar assets | Alta |
| F-006.2.4 | Filtros | Filtrar por tipo, estado, fecha, etc. | Alta |
### F-006.3: Búsqueda
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-006.3.1 | Búsqueda texto | Por nombre, descripción, tags | Alta |
| F-006.3.2 | Filtros avanzados | Combinar múltiples criterios | Alta |
| F-006.3.3 | Búsqueda por similar | Encontrar imágenes similares | Baja |
### F-006.4: Versiones y Aprobación
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-006.4.1 | Versionar | Subir nueva versión de asset | Media |
| F-006.4.2 | Comparar versiones | Ver diferencias | Baja |
| F-006.4.3 | Aprobar/Rechazar | Cambiar estado de revisión | Alta |
| F-006.4.4 | Comentarios | Feedback sobre assets | Alta |
### F-006.5: Descargas y Exportación
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-006.5.1 | Descargar individual | Bajar un asset | Alta |
| F-006.5.2 | Descargar batch | Bajar múltiples como ZIP | Alta |
| F-006.5.3 | Convertir formato | Descargar en formato diferente | Media |
| F-006.5.4 | Enlace temporal | URL con expiración | Media |
---
## Tipos de Assets Soportados
```yaml
Imágenes:
Formatos: PNG, JPG, JPEG, WebP, GIF, SVG
Max size: 50MB
Procesamiento:
- Generación de thumbnails
- Extracción de dimensiones
- Optimización automática
Copys/Textos:
Tipos: Copy publicitario, título, descripción, hashtags
Almacenamiento: En BD + archivo .txt opcional
Metadata: Tone, language, character count
Videos:
Formatos: MP4, MOV, WebM
Max size: 500MB
Procesamiento:
- Generación de thumbnail
- Extracción de duración
- Preview de baja resolución
Documentos:
Formatos: PDF, DOC, DOCX
Max size: 100MB
Uso: Brand guidelines, briefs, contratos
Modelos IA:
Tipos: LoRA (.safetensors), Checkpoint, Embedding
Max size: 10GB
Metadata: Base model, trigger word, training params
Templates:
Tipos: Workflow ComfyUI, plantillas de brief
Formato: JSON
```
---
## Storage Structure
```yaml
S3/MinIO Bucket Structure:
{bucket}/
├── {tenant_slug}/
│ ├── assets/
│ │ ├── images/
│ │ │ ├── {year}/
│ │ │ │ ├── {month}/
│ │ │ │ │ ├── {asset_id}/
│ │ │ │ │ │ ├── original.{ext}
│ │ │ │ │ │ ├── thumb_200.jpg
│ │ │ │ │ │ ├── thumb_800.jpg
│ │ │ │ │ │ └── versions/
│ │ │ │ │ │ ├── v1.{ext}
│ │ │ │ │ │ └── v2.{ext}
│ │ ├── videos/
│ │ ├── documents/
│ │ └── copies/
│ ├── models/
│ │ ├── loras/
│ │ ├── checkpoints/
│ │ └── embeddings/
│ └── temp/
│ └── (archivos temporales, limpiados periódicamente)
```
---
## API Endpoints
```yaml
Base: /api/v1/assets
# Assets CRUD
POST /assets/upload # Subir archivo(s)
GET /assets # Listar con filtros y paginación
GET /assets/:id # Detalle de asset
PUT /assets/:id # Actualizar metadata
DELETE /assets/:id # Soft delete
# Bulk operations
POST /assets/bulk/move # Mover a colección
POST /assets/bulk/tag # Agregar tags
POST /assets/bulk/delete # Eliminar múltiples
POST /assets/bulk/status # Cambiar estado
# Versions
GET /assets/:id/versions # Listar versiones
POST /assets/:id/versions # Subir nueva versión
GET /assets/:id/versions/:v # Obtener versión específica
# Status & Approval
PATCH /assets/:id/status # Cambiar estado
POST /assets/:id/approve # Aprobar
POST /assets/:id/reject # Rechazar con feedback
# Comments
GET /assets/:id/comments # Listar comentarios
POST /assets/:id/comments # Agregar comentario
PUT /assets/:id/comments/:cid # Editar comentario
DELETE /assets/:id/comments/:cid # Eliminar comentario
# Downloads
GET /assets/:id/download # Descargar asset
POST /assets/download/batch # Descargar múltiples (ZIP)
POST /assets/:id/share # Generar enlace temporal
# Collections
GET /collections # Listar colecciones
POST /collections # Crear colección
GET /collections/:id # Detalle de colección
PUT /collections/:id # Actualizar colección
DELETE /collections/:id # Eliminar colección
POST /collections/:id/assets # Agregar assets
DELETE /collections/:id/assets # Quitar assets
# Search
POST /assets/search # Búsqueda avanzada
GET /assets/tags # Listar tags usados
```
---
## Reglas de Negocio
```yaml
RN-006.1:
Descripción: Assets generados heredan metadata del job
Comportamiento: Copiar prompt, modelo, parámetros automáticamente
RN-006.2:
Descripción: Thumbnails se generan automáticamente
Tamaños: 200px y 800px de ancho, manteniendo aspect ratio
RN-006.3:
Descripción: Soft delete retiene archivos 30 días
Acción: Cron job limpia después del período
RN-006.4:
Descripción: Versionado mantiene historial completo
Límite: Máximo 10 versiones por asset
RN-006.5:
Descripción: Links temporales expiran según configuración
Default: 7 días, máximo 30 días
RN-006.6:
Descripción: Storage cuenta contra cuota del tenant
Validación: Verificar límite antes de upload
```
---
## UI/UX Consideraciones
```yaml
Vistas principales:
- Grid view: Thumbnails en cuadrícula
- List view: Lista con metadata
- Detail view: Asset grande con panel de info
Componentes:
- AssetUploader: Drag & drop, multi-file
- AssetPreview: Lightbox con navegación
- AssetFilters: Panel de filtros colapsable
- CollectionPicker: Modal para agregar a colección
- CommentPanel: Sidebar con comentarios
Acciones rápidas:
- Quick preview (spacebar)
- Quick download (d)
- Quick approve (a)
- Add to collection (c)
```
---
## Dependencias
```yaml
Dependencias de Módulos:
- PMC-001 Tenants: Cuotas de storage
- PMC-003 Projects: Vinculación con campañas
- PMC-004 Generation: Assets generados
Servicios Externos:
- S3/MinIO: Almacenamiento de archivos
- Sharp: Procesamiento de imágenes
- FFmpeg: Procesamiento de video (opcional)
Dependencias del Catálogo:
- (ninguna directa)
```
---
## Consideraciones de Performance
```yaml
Optimizaciones:
- Lazy loading de thumbnails
- Paginación con cursor para grandes volúmenes
- CDN para servir assets estáticos
- Compresión de uploads grandes
- Pre-signed URLs para uploads directos a S3
Índices BD:
- tenant_id + type + status
- tenant_id + created_at
- tenant_id + tags (GIN index)
- Full-text search en name, description
```
---
## Criterios de Aceptación
- [ ] Upload funciona con drag & drop y file picker
- [ ] Thumbnails se generan automáticamente
- [ ] Búsqueda y filtros funcionan correctamente
- [ ] Colecciones permiten organizar assets
- [ ] Versionado mantiene historial
- [ ] Flujo de aprobación funciona
- [ ] Descargas individuales y batch funcionan
- [ ] Links temporales se generan y expiran
- [ ] Comentarios se pueden agregar y resolver
- [ ] Storage se cuenta contra cuota
---
## Referencias
- [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md)
- [GLOSARIO.md](../00-vision-general/GLOSARIO.md) - Definición de DAM
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,495 @@
# PMC-007: Módulo de Admin
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Definición
**Prioridad:** Media
---
## Descripción General
El módulo de Admin proporciona las funcionalidades de administración del sistema SaaS: gestión de usuarios, roles, permisos, planes de suscripción, configuración global y herramientas de supervisión.
---
## Objetivos
1. Gestionar usuarios y sus roles dentro del tenant
2. Controlar permisos de acceso por módulo/acción
3. Administrar planes y suscripciones (preparación SaaS)
4. Configurar parámetros globales del sistema
5. Monitorear uso y salud del sistema
---
## Entidades del Dominio
### User
```yaml
Entidad: User
Descripción: Usuario del sistema
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- email: string (único por tenant)
- password_hash: string
- first_name: string
- last_name: string
- avatar_url: string
- status: enum [pending, active, suspended, deactivated]
- role_id: UUID (FK)
- preferences: JSONB
- language: string
- timezone: string
- theme: string
- notifications: object
- last_login_at: timestamp
- email_verified_at: timestamp
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Tenant
- N:1 con Role
- 1:N con Project (como owner)
- 1:N con Asset (como creator)
- 1:N con GenerationJob
```
### Role
```yaml
Entidad: Role
Descripción: Rol con conjunto de permisos
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK, null = rol de sistema)
- name: string
- description: text
- permissions: array[string] (lista de permisos)
- is_system: boolean (no editable si true)
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Tenant (opcional)
- 1:N con User
Roles de Sistema:
- super_admin: Acceso total, sin límites
- tenant_admin: Admin del tenant
- creative: Crear contenido, gestionar campañas
- analyst: CRM, reportes
- viewer: Solo lectura
```
### Permission (definición estática)
```yaml
Permisos del Sistema:
# Tenants
- tenants.view
- tenants.create
- tenants.edit
- tenants.delete
# Users
- users.view
- users.create
- users.edit
- users.delete
- users.invite
# CRM
- clients.view
- clients.create
- clients.edit
- clients.delete
- brands.view
- brands.create
- brands.edit
- brands.delete
- products.view
- products.create
- products.edit
- products.delete
# Projects
- projects.view
- projects.create
- projects.edit
- projects.delete
- campaigns.view
- campaigns.create
- campaigns.edit
- campaigns.delete
- campaigns.approve
# Generation
- generation.execute
- generation.view_queue
- generation.manage_queue
- models.view
- models.create
- models.delete
- models.train
# Assets
- assets.view
- assets.upload
- assets.edit
- assets.delete
- assets.approve
- assets.download
# Automation
- automation.view
- automation.configure
- automation.execute
# Admin
- admin.users
- admin.roles
- admin.settings
- admin.billing
- admin.audit
```
### Invitation
```yaml
Entidad: Invitation
Descripción: Invitación pendiente para unirse al tenant
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- email: string
- role_id: UUID (FK)
- invited_by: UUID (FK a User)
- token: string (único)
- status: enum [pending, accepted, expired, cancelled]
- expires_at: timestamp
- accepted_at: timestamp
- created_at: timestamp
Relaciones:
- N:1 con Tenant
- N:1 con Role
- N:1 con User (inviter)
```
### AuditLog
```yaml
Entidad: AuditLog
Descripción: Registro de acciones importantes
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- user_id: UUID (FK)
- action: string (ej: user.created, asset.deleted)
- entity_type: string
- entity_id: UUID
- old_values: JSONB
- new_values: JSONB
- ip_address: string
- user_agent: string
- created_at: timestamp
Relaciones:
- N:1 con Tenant
- N:1 con User
```
### Setting
```yaml
Entidad: Setting
Descripción: Configuración del sistema/tenant
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK, null = global)
- key: string
- value: JSONB
- type: enum [string, number, boolean, json]
- category: string
- is_secret: boolean
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Tenant (opcional)
```
---
## Funcionalidades
### F-007.1: Gestión de Usuarios
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-007.1.1 | Listar usuarios | Ver todos los usuarios del tenant | Alta |
| F-007.1.2 | Invitar usuario | Enviar invitación por email | Alta |
| F-007.1.3 | Editar usuario | Modificar datos y rol | Alta |
| F-007.1.4 | Suspender usuario | Bloquear acceso temporalmente | Media |
| F-007.1.5 | Eliminar usuario | Desactivar cuenta | Media |
### F-007.2: Gestión de Roles
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-007.2.1 | Listar roles | Ver roles disponibles | Alta |
| F-007.2.2 | Crear rol | Definir rol personalizado | Media |
| F-007.2.3 | Editar permisos | Modificar permisos de rol | Media |
| F-007.2.4 | Eliminar rol | Solo si no tiene usuarios | Baja |
### F-007.3: Configuración
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-007.3.1 | Settings generales | Nombre, branding, etc. | Alta |
| F-007.3.2 | Settings de generación | Modelos por defecto, calidad | Media |
| F-007.3.3 | Integraciones | Configurar n8n, APIs externas | Media |
| F-007.3.4 | Notificaciones | Templates de email, webhooks | Media |
### F-007.4: Auditoría
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-007.4.1 | Ver logs | Historial de acciones | Media |
| F-007.4.2 | Filtrar logs | Por usuario, acción, fecha | Media |
| F-007.4.3 | Exportar logs | Descargar en CSV | Baja |
### F-007.5: Monitoreo (Super Admin)
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-007.5.1 | Dashboard sistema | Métricas globales | Media |
| F-007.5.2 | Estado de servicios | GPU, queue, storage | Alta |
| F-007.5.3 | Uso por tenant | Consumo de recursos | Media |
---
## Roles del Sistema
### Matriz de Permisos por Rol
```yaml
super_admin:
description: "Administrador del sistema completo"
permissions: ["*"] # Todos los permisos
notes: "Solo para el owner/CTO. Sin límites de uso."
tenant_admin:
description: "Administrador del tenant/agencia"
permissions:
- users.*
- roles.view
- clients.*
- brands.*
- products.*
- projects.*
- campaigns.*
- generation.*
- assets.*
- automation.view
- automation.configure
- admin.users
- admin.settings
- admin.audit
creative:
description: "Creativo/Media Buyer"
permissions:
- clients.view
- brands.view
- products.view
- products.create
- projects.view
- projects.create
- projects.edit
- campaigns.*
- generation.execute
- generation.view_queue
- models.view
- assets.view
- assets.upload
- assets.edit
analyst:
description: "Analista/CRM"
permissions:
- clients.*
- brands.view
- products.view
- projects.view
- campaigns.view
- assets.view
- assets.download
- automation.view
viewer:
description: "Solo lectura"
permissions:
- clients.view
- brands.view
- products.view
- projects.view
- campaigns.view
- assets.view
client_portal:
description: "Cliente externo (portal)"
permissions:
- campaigns.view # Solo sus campañas
- assets.view # Solo sus assets
- assets.download
```
---
## API Endpoints
```yaml
Base: /api/v1/admin
# Users
GET /users # Listar usuarios
GET /users/:id # Detalle de usuario
POST /users/invite # Invitar usuario
PUT /users/:id # Actualizar usuario
PATCH /users/:id/status # Cambiar estado
DELETE /users/:id # Desactivar usuario
# Invitations
GET /invitations # Listar invitaciones
POST /invitations/:id/resend # Reenviar
DELETE /invitations/:id # Cancelar
# Roles
GET /roles # Listar roles
GET /roles/:id # Detalle de rol
POST /roles # Crear rol
PUT /roles/:id # Actualizar rol
DELETE /roles/:id # Eliminar rol
# Settings
GET /settings # Listar settings
GET /settings/:key # Obtener setting
PUT /settings/:key # Actualizar setting
DELETE /settings/:key # Eliminar setting (custom)
# Audit
GET /audit # Listar logs
GET /audit/export # Exportar logs
# System (Super Admin only)
GET /system/status # Estado del sistema
GET /system/metrics # Métricas globales
GET /system/tenants # Listar todos los tenants
GET /system/tenants/:id/usage # Uso de un tenant
```
---
## Reglas de Negocio
```yaml
RN-007.1:
Descripción: Email único por tenant
Validación: No puede haber dos usuarios con mismo email en un tenant
RN-007.2:
Descripción: Roles de sistema no editables
Validación: is_system = true bloquea edición
RN-007.3:
Descripción: No eliminar rol con usuarios asignados
Validación: Verificar user_count = 0 antes de DELETE
RN-007.4:
Descripción: Invitación expira en 7 días
Validación: expires_at = created_at + 7 days
RN-007.5:
Descripción: Super admin no puede ser suspendido
Validación: Bloquear cambio de status si role = super_admin
RN-007.6:
Descripción: Audit logs son inmutables
Validación: Solo INSERT, no UPDATE ni DELETE
```
---
## Dependencias
```yaml
Dependencias de Módulos:
- PMC-001 Tenants: Configuración de tenant
- Todos los módulos: Para verificación de permisos
Dependencias del Catálogo:
- @CATALOG_AUTH: Autenticación JWT + OAuth
- @CATALOG_SESSION: Gestión de sesiones
Servicios Externos:
- SMTP: Envío de invitaciones
- (OAuth providers si se implementa SSO)
```
---
## Flujos de Usuario
### Invitar Usuario
```
1. Admin navega a Users → Invite
2. Ingresa email y selecciona rol
3. Sistema genera token único
4. Sistema envía email con link
5. Usuario hace clic en link
6. Usuario completa registro (password, nombre)
7. Usuario queda activo en el tenant
```
### Cambiar Rol de Usuario
```
1. Admin navega a Users → [Usuario]
2. Selecciona nuevo rol
3. Sistema verifica que no sea el único admin
4. Sistema actualiza rol
5. Permisos se aplican inmediatamente
6. Se registra en audit log
```
---
## Criterios de Aceptación
- [ ] CRUD completo de usuarios funciona
- [ ] Sistema de invitaciones por email funciona
- [ ] Roles controlan acceso a módulos correctamente
- [ ] Permisos se verifican en cada endpoint
- [ ] Settings se guardan y cargan correctamente
- [ ] Audit logs registran acciones importantes
- [ ] Dashboard de sistema muestra métricas
- [ ] Super admin tiene acceso a todo
---
## Referencias
- [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md)
- [@CATALOG_AUTH](../../../core/catalog/modules/auth/)
- [PMC-001-TENANTS.md](./PMC-001-TENANTS.md)
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,443 @@
# PMC-008: Módulo de Analytics
**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Definición
**Prioridad:** Baja
---
## Descripción General
El módulo de Analytics proporciona dashboards, reportes y métricas sobre el uso de la plataforma, rendimiento de campañas, y consumo de recursos. Permite tomar decisiones basadas en datos.
---
## Objetivos
1. Visualizar métricas clave de operación
2. Analizar rendimiento de campañas
3. Monitorear uso de recursos (generaciones, storage)
4. Generar reportes exportables
5. Identificar tendencias y oportunidades
---
## Dashboards
### Dashboard Principal (Home)
```yaml
Widgets:
- quick_stats:
- Campañas activas
- Assets generados (mes)
- Tasa de aprobación
- Jobs en cola
- recent_activity:
- Últimos assets generados
- Campañas recién creadas
- Jobs completados
- pending_actions:
- Assets pendientes de revisión
- Campañas esperando aprobación
```
### Dashboard de Producción
```yaml
Widgets:
- generation_volume:
Tipo: Line chart
Datos: Generaciones por día/semana/mes
Filtros: Tipo (imagen/texto), workflow
- queue_status:
Tipo: Real-time gauge
Datos: Jobs en cola, procesando, completados
- model_usage:
Tipo: Pie chart
Datos: Distribución de uso de workflows/LoRAs
- error_rate:
Tipo: Line chart
Datos: % de jobs fallidos por período
- processing_time:
Tipo: Bar chart
Datos: Tiempo promedio por tipo de workflow
```
### Dashboard de Campañas
```yaml
Widgets:
- campaign_funnel:
Tipo: Funnel chart
Datos: Campañas por estado
- approval_metrics:
Tipo: Stats cards
Datos:
- Tasa de aprobación primera iteración
- Promedio de revisiones por campaña
- Tiempo desde brief hasta aprobación
- assets_per_campaign:
Tipo: Bar chart
Datos: Promedio de assets por campaña
- top_clients:
Tipo: Table
Datos: Clientes con más campañas/assets
```
### Dashboard de Recursos
```yaml
Widgets:
- storage_usage:
Tipo: Progress bar + breakdown
Datos: GB usados vs cuota, por tipo de archivo
- generation_quota:
Tipo: Progress bar
Datos: Generaciones usadas vs límite mensual
- gpu_utilization:
Tipo: Real-time gauge (si aplica)
Datos: % de uso de GPU
- cost_estimate:
Tipo: Stats card
Datos: Costo estimado de APIs externas (LLM, etc.)
```
---
## Reportes
### Reporte de Actividad Mensual
```yaml
Nombre: monthly_activity_report
Período: Mes natural
Contenido:
- Resumen ejecutivo
- Campañas creadas/completadas
- Assets generados por tipo
- Clientes más activos
- Uso de recursos
- Comparativa con mes anterior
Formatos: PDF, Excel
Programación: Automático primer día del mes
```
### Reporte de Campaña
```yaml
Nombre: campaign_report
Período: Duración de la campaña
Contenido:
- Datos de la campaña y brief
- Assets generados
- Historial de revisiones
- Tiempo total de producción
- Participantes (usuarios)
Formatos: PDF
Generación: Manual o al cerrar campaña
```
### Reporte de Cliente
```yaml
Nombre: client_report
Período: Configurable
Contenido:
- Proyectos y campañas del cliente
- Assets entregados
- Histórico de actividad
- Métricas de satisfacción (si aplica)
Formatos: PDF, Excel
Generación: Manual
```
### Reporte de Uso (Admin)
```yaml
Nombre: usage_report
Período: Configurable
Contenido:
- Generaciones por usuario
- Storage consumido
- Costo de APIs externas
- Comparativa por período
Formatos: Excel, CSV
Audiencia: Admin/Finance
```
---
## Entidades del Dominio
### Metric
```yaml
Entidad: Metric (tabla de hechos)
Descripción: Registro agregado de métricas
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- metric_type: string (generation_count, storage_used, etc.)
- dimension_1: string (ej: workflow_type)
- dimension_2: string (ej: user_id)
- value: decimal
- period_type: enum [hour, day, week, month]
- period_start: timestamp
- created_at: timestamp
Índices:
- tenant_id + metric_type + period_start
- tenant_id + period_type + period_start
```
### Report
```yaml
Entidad: Report
Descripción: Reporte generado
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- name: string
- type: string (monthly_activity, campaign, client, usage)
- parameters: JSONB (filtros aplicados)
- file_path: string
- file_format: enum [pdf, xlsx, csv]
- generated_by: UUID (FK a User)
- created_at: timestamp
Relaciones:
- N:1 con Tenant
- N:1 con User
```
### SavedView
```yaml
Entidad: SavedView
Descripción: Vista personalizada guardada
Atributos:
- id: UUID (PK)
- tenant_id: UUID (FK)
- user_id: UUID (FK)
- name: string
- dashboard: string (production, campaigns, resources)
- config: JSONB (filtros, widgets visibles, layout)
- is_default: boolean
- created_at: timestamp
- updated_at: timestamp
Relaciones:
- N:1 con Tenant
- N:1 con User
```
---
## Funcionalidades
### F-008.1: Dashboards
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-008.1.1 | Dashboard home | Vista principal con KPIs | Alta |
| F-008.1.2 | Dashboard producción | Métricas de generación | Media |
| F-008.1.3 | Dashboard campañas | Métricas de campañas | Media |
| F-008.1.4 | Dashboard recursos | Uso de recursos | Media |
| F-008.1.5 | Filtros globales | Por fecha, cliente, usuario | Alta |
### F-008.2: Reportes
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-008.2.1 | Generar reporte | Crear reporte bajo demanda | Media |
| F-008.2.2 | Programar reporte | Generación automática | Baja |
| F-008.2.3 | Descargar reporte | PDF, Excel, CSV | Media |
| F-008.2.4 | Historial reportes | Ver reportes generados | Baja |
### F-008.3: Personalización
| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-008.3.1 | Guardar vista | Guardar configuración de dashboard | Baja |
| F-008.3.2 | Vista por defecto | Establecer vista inicial | Baja |
---
## KPIs Principales
```yaml
Operación:
- Generaciones totales (día/semana/mes)
- Tiempo promedio de generación
- Tasa de éxito de jobs (%)
- Cola promedio (tiempo de espera)
Campañas:
- Campañas activas
- Tiempo promedio brief → aprobación
- Tasa de aprobación primera iteración (%)
- Assets por campaña (promedio)
Recursos:
- Storage utilizado vs cuota (%)
- Generaciones usadas vs límite (%)
- Costo estimado de APIs externas
Usuarios:
- Usuarios activos (día/semana/mes)
- Generaciones por usuario
- Acciones por usuario
```
---
## API Endpoints
```yaml
Base: /api/v1/analytics
# Dashboards
GET /dashboards/:name # Datos de dashboard
GET /dashboards/:name/widgets/:widget # Datos de widget específico
# Metrics
GET /metrics # Query de métricas
POST /metrics/aggregate # Agregación personalizada
# Reports
GET /reports # Listar reportes generados
POST /reports # Generar nuevo reporte
GET /reports/:id # Detalle de reporte
GET /reports/:id/download # Descargar archivo
DELETE /reports/:id # Eliminar reporte
# Saved Views
GET /views # Listar vistas guardadas
POST /views # Crear vista
PUT /views/:id # Actualizar vista
DELETE /views/:id # Eliminar vista
PATCH /views/:id/default # Establecer como default
# Quick stats (para widgets)
GET /stats/overview # Resumen general
GET /stats/generations # Stats de generación
GET /stats/campaigns # Stats de campañas
GET /stats/storage # Stats de storage
```
---
## Arquitectura de Datos
### Pipeline de Métricas
```yaml
Flujo:
1. Evento ocurre (generación, campaña creada, etc.)
2. EventEmitter emite evento
3. MetricsService captura y procesa
4. Se inserta en tabla metrics (agregado horario)
5. Job nocturno consolida a día/semana/mes
Retención:
- Métricas horarias: 7 días
- Métricas diarias: 90 días
- Métricas semanales: 1 año
- Métricas mensuales: indefinido
```
### Queries Optimizadas
```yaml
Estrategias:
- Tablas de métricas pre-agregadas
- Índices por tenant + período
- Cache en Redis para datos frecuentes
- Refresh periódico de materialized views
```
---
## Dependencias
```yaml
Dependencias de Módulos:
- PMC-001 Tenants: Contexto de datos
- PMC-003 Projects: Datos de campañas
- PMC-004 Generation: Datos de generación
- PMC-006 Assets: Datos de almacenamiento
Servicios Externos:
- Redis: Cache de métricas
- (Opcional) Chart library frontend
Dependencias del Catálogo:
- (ninguna directa)
```
---
## UI/UX Consideraciones
```yaml
Componentes:
- DashboardGrid: Layout responsivo de widgets
- ChartWidget: Wrapper para gráficos
- FilterBar: Barra de filtros global
- DateRangePicker: Selector de período
- ExportButton: Descarga de datos/reportes
Interactividad:
- Drill-down en gráficos
- Tooltips con detalles
- Filtros aplicables a toda la página
- Auto-refresh configurable
Responsividad:
- Widgets se reordenan en móvil
- Gráficos adaptan tamaño
- Tablas con scroll horizontal
```
---
## Criterios de Aceptación
- [ ] Dashboard home muestra KPIs correctos
- [ ] Filtros de fecha funcionan globalmente
- [ ] Gráficos cargan datos correctamente
- [ ] Reportes se generan en PDF y Excel
- [ ] Métricas se agregan correctamente
- [ ] Cache mejora tiempos de carga
- [ ] Datos se aíslan por tenant
---
## Referencias
- [VISION-GENERAL.md](../00-vision-general/VISION-GENERAL.md)
- [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md)
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,148 @@
# Índice de Módulos - Platform Marketing Content
**Versión:** 1.0.0
**Fecha:** 2025-12-08
---
## Resumen de Módulos
| ID | Módulo | Descripción | Prioridad | Estado |
|----|--------|-------------|-----------|--------|
| PMC-001 | [Tenants](./PMC-001-TENANTS.md) | Arquitectura multi-tenant, planes, configuración | Alta | Definido |
| PMC-002 | [CRM](./PMC-002-CRM.md) | Clientes, marcas, productos, oportunidades | Alta | Definido |
| PMC-003 | [Projects](./PMC-003-PROJECTS.md) | Proyectos, campañas, briefs, flujos de trabajo | Alta | Definido |
| PMC-004 | [Generation](./PMC-004-GENERATION.md) | Motor de IA, workflows ComfyUI, modelos custom | Alta | Definido |
| PMC-005 | [Automation](./PMC-005-AUTOMATION.md) | Flujos automatizados con n8n, triggers, webhooks | Media | Definido |
| PMC-006 | [Assets](./PMC-006-ASSETS.md) | DAM, biblioteca de activos, versionado | Alta | Definido |
| PMC-007 | [Admin](./PMC-007-ADMIN.md) | Usuarios, roles, permisos, configuración SaaS | Media | Definido |
| PMC-008 | [Analytics](./PMC-008-ANALYTICS.md) | Dashboards, reportes, métricas | Baja | Definido |
---
## Dependencias entre Módulos
```
┌─────────────────────────────────────────────────────────────┐
│ PMC-001 Tenants │
│ (Base de aislamiento) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ PMC-007 │ │ PMC-002 │ │ PMC-006 │
│ Admin │ │ CRM │ │ Assets │
│ (Users/Roles)│ │ (Clientes) │ │ (DAM) │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ ▲
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ PMC-003 │ │
│ │ Projects │──────────────┤
│ │ (Campañas) │ │
│ └───────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ PMC-004 │ │
│ │ Generation │──────────────┘
│ │ (Motor IA) │
│ └───────────────┘
│ │
│ ▼
│ ┌───────────────┐
└───────────►│ PMC-005 │
│ Automation │
│ (n8n) │
└───────────────┘
┌───────────────┐
│ PMC-008 │
│ Analytics │
│ (Métricas) │
└───────────────┘
```
---
## Orden de Implementación Sugerido
### Fase 1 - Core MVP
1. **PMC-001 Tenants** - Base de la arquitectura
2. **PMC-007 Admin** - Usuarios y autenticación
3. **PMC-002 CRM** - Gestión de clientes y marcas
4. **PMC-006 Assets** - Almacenamiento de activos
5. **PMC-003 Projects** - Proyectos y campañas básicos
6. **PMC-004 Generation** - Motor de generación (2-3 workflows)
### Fase 2 - Automatización
7. **PMC-005 Automation** - Flujos automatizados
### Fase 3 - Analytics
8. **PMC-008 Analytics** - Dashboards y reportes
---
## Entidades Compartidas
| Entidad | Módulo Principal | Módulos que Referencian |
|---------|------------------|------------------------|
| Tenant | PMC-001 | Todos |
| User | PMC-007 | Todos |
| Client | PMC-002 | PMC-003, PMC-008 |
| Brand | PMC-002 | PMC-003, PMC-004, PMC-006 |
| Product | PMC-002 | PMC-004, PMC-006 |
| Campaign | PMC-003 | PMC-004, PMC-005, PMC-006, PMC-008 |
| Asset | PMC-006 | PMC-002, PMC-003, PMC-004 |
| GenerationJob | PMC-004 | PMC-003, PMC-006, PMC-008 |
---
## APIs por Módulo
| Módulo | Base Path | Endpoints Principales |
|--------|-----------|----------------------|
| Tenants | `/api/v1/tenants` | CRUD tenants, config |
| CRM | `/api/v1/crm` | clients, contacts, brands, products, opportunities |
| Projects | `/api/v1/projects` | projects, campaigns, briefs |
| Generation | `/api/v1/generation` | jobs, workflows, models |
| Automation | `/api/v1/automation` | flows, runs, webhooks |
| Assets | `/api/v1/assets` | assets, collections, downloads |
| Admin | `/api/v1/admin` | users, roles, settings, audit |
| Analytics | `/api/v1/analytics` | dashboards, metrics, reports |
---
## Conteo de Funcionalidades
| Módulo | Funcionalidades | Prioridad Alta | Prioridad Media | Prioridad Baja |
|--------|-----------------|----------------|-----------------|----------------|
| PMC-001 | 10 | 6 | 3 | 1 |
| PMC-002 | 18 | 12 | 5 | 1 |
| PMC-003 | 16 | 10 | 5 | 1 |
| PMC-004 | 20 | 12 | 6 | 2 |
| PMC-005 | 12 | 4 | 6 | 2 |
| PMC-006 | 18 | 10 | 6 | 2 |
| PMC-007 | 14 | 7 | 5 | 2 |
| PMC-008 | 10 | 3 | 5 | 2 |
| **TOTAL** | **118** | **64** | **41** | **13** |
---
## Referencias
- [VISION-GENERAL.md](../00-vision-general/VISION-GENERAL.md)
- [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md)
- [GLOSARIO.md](../00-vision-general/GLOSARIO.md)
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,512 @@
# Requerimientos Funcionales - PMC-001 Tenants
**Módulo:** Tenants
**Versión:** 1.0.0
**Fecha:** 2025-12-08
---
## RF-PMC-001-001: Crear Tenant
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-001 |
| **Nombre** | Crear Tenant |
| **Prioridad** | P1 |
| **Actor** | Super Admin |
**Descripción:**
El sistema debe permitir crear un nuevo tenant con datos básicos.
**Precondiciones:**
- Usuario autenticado como Super Admin
**Datos de entrada:**
- name: string (requerido, 3-100 caracteres)
- slug: string (requerido, único, formato URL-safe)
- plan_id: UUID (requerido)
- settings: object (opcional)
- branding: object (opcional)
**Flujo principal:**
1. Super Admin accede a gestión de tenants
2. Selecciona "Crear tenant"
3. Completa formulario con datos requeridos
4. Sistema valida unicidad del slug
5. Sistema crea tenant con status "active"
6. Sistema crea usuario admin inicial (opcional)
7. Sistema retorna tenant creado
**Postcondiciones:**
- Tenant existe en base de datos
- Tenant tiene plan asignado
- RLS configurado para nuevo tenant
**Criterios de aceptación:**
- [ ] Validación de slug único funciona
- [ ] Tenant se crea con status "active"
- [ ] Plan se asocia correctamente
---
## RF-PMC-001-002: Editar Tenant
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-002 |
| **Nombre** | Editar Tenant |
| **Prioridad** | P2 |
| **Actor** | Super Admin, Tenant Admin |
**Descripción:**
El sistema debe permitir modificar datos de un tenant existente.
**Precondiciones:**
- Usuario autenticado con permisos de edición
- Tenant existe
**Datos de entrada:**
- name: string (opcional)
- settings: object (opcional)
- branding: object (opcional)
- limits: object (opcional, solo Super Admin)
**Flujo principal:**
1. Usuario accede a configuración del tenant
2. Modifica campos permitidos según rol
3. Sistema valida datos
4. Sistema actualiza tenant
5. Sistema registra cambio en audit log
**Restricciones:**
- Tenant Admin no puede modificar limits ni plan
- Slug no es editable después de creación
**Criterios de aceptación:**
- [ ] Campos se actualizan correctamente
- [ ] Permisos por rol se respetan
- [ ] Audit log registra cambios
---
## RF-PMC-001-003: Suspender Tenant
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-003 |
| **Nombre** | Suspender Tenant |
| **Prioridad** | P2 |
| **Actor** | Super Admin |
**Descripción:**
El sistema debe permitir suspender un tenant, bloqueando acceso de usuarios.
**Precondiciones:**
- Usuario autenticado como Super Admin
- Tenant existe con status "active"
**Flujo principal:**
1. Super Admin selecciona tenant a suspender
2. Sistema solicita confirmación
3. Sistema cambia status a "suspended"
4. Sistema invalida todas las sesiones del tenant
5. Sistema notifica a admins del tenant
**Postcondiciones:**
- Usuarios del tenant no pueden hacer login
- Datos permanecen intactos
- Jobs pendientes se pausan
**Criterios de aceptación:**
- [ ] Status cambia a "suspended"
- [ ] Login bloqueado para usuarios del tenant
- [ ] Sesiones existentes invalidadas
---
## RF-PMC-001-004: Reactivar Tenant
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-004 |
| **Nombre** | Reactivar Tenant |
| **Prioridad** | P2 |
| **Actor** | Super Admin |
**Descripción:**
El sistema debe permitir reactivar un tenant suspendido.
**Precondiciones:**
- Tenant existe con status "suspended"
**Flujo principal:**
1. Super Admin selecciona tenant suspendido
2. Selecciona "Reactivar"
3. Sistema cambia status a "active"
4. Sistema notifica a admins del tenant
**Postcondiciones:**
- Usuarios pueden hacer login
- Jobs pausados se reactivan
**Criterios de aceptación:**
- [ ] Status cambia a "active"
- [ ] Login permitido nuevamente
---
## RF-PMC-001-005: Eliminar Tenant (Soft Delete)
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-005 |
| **Nombre** | Eliminar Tenant |
| **Prioridad** | P3 |
| **Actor** | Super Admin |
**Descripción:**
El sistema debe permitir eliminar un tenant mediante soft delete.
**Precondiciones:**
- Tenant existe
**Flujo principal:**
1. Super Admin selecciona tenant a eliminar
2. Sistema solicita confirmación con texto de verificación
3. Sistema marca tenant como eliminado (deleted_at)
4. Sistema invalida sesiones
5. Sistema programa limpieza de datos (90 días)
**Postcondiciones:**
- Tenant marcado con deleted_at
- Datos retenidos por 90 días
- Acceso completamente bloqueado
**Criterios de aceptación:**
- [ ] Soft delete funciona correctamente
- [ ] Datos no se eliminan inmediatamente
- [ ] Tenant no aparece en listados
---
## RF-PMC-001-006: Listar Tenants
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-006 |
| **Nombre** | Listar Tenants |
| **Prioridad** | P1 |
| **Actor** | Super Admin |
**Descripción:**
El sistema debe permitir listar todos los tenants con filtros y paginación.
**Datos de entrada (query params):**
- status: string (filtro por estado)
- plan_id: UUID (filtro por plan)
- search: string (búsqueda por nombre)
- page: number
- limit: number (max 100)
**Datos de salida:**
- Lista de tenants con datos básicos
- Total de registros
- Información de paginación
**Criterios de aceptación:**
- [ ] Paginación funciona correctamente
- [ ] Filtros se aplican correctamente
- [ ] Búsqueda por nombre funciona
---
## RF-PMC-001-007: Ver Detalle de Tenant
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-007 |
| **Nombre** | Ver Detalle de Tenant |
| **Prioridad** | P1 |
| **Actor** | Super Admin, Tenant Admin |
**Descripción:**
El sistema debe mostrar información detallada de un tenant.
**Datos de salida:**
- Datos básicos del tenant
- Plan asociado con límites
- Configuración (settings)
- Branding
- Estadísticas de uso
- Usuarios activos (count)
**Criterios de aceptación:**
- [ ] Todos los datos se muestran correctamente
- [ ] Tenant Admin solo ve su propio tenant
---
## RF-PMC-001-008: Configurar Branding
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-008 |
| **Nombre** | Configurar Branding |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Descripción:**
El sistema debe permitir personalizar el branding del tenant.
**Datos de entrada:**
- logo_url: string (URL o upload)
- primary_color: string (hex color)
- secondary_color: string (hex color)
- favicon_url: string (opcional)
**Flujo principal:**
1. Admin accede a configuración de branding
2. Sube logo o proporciona URL
3. Selecciona colores
4. Sistema valida formatos
5. Sistema actualiza branding
6. Cambios se reflejan en UI
**Criterios de aceptación:**
- [ ] Logo se almacena/referencia correctamente
- [ ] Colores se aplican en UI
- [ ] Preview disponible antes de guardar
---
## RF-PMC-001-009: Configurar Límites Personalizados
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-009 |
| **Nombre** | Configurar Límites Personalizados |
| **Prioridad** | P2 |
| **Actor** | Super Admin |
**Descripción:**
El sistema debe permitir sobreescribir límites del plan para un tenant específico.
**Datos de entrada:**
- generations_per_month: number (null = usar plan)
- storage_gb: number (null = usar plan)
- users_max: number (null = usar plan)
- custom_limits: object
**Flujo principal:**
1. Super Admin accede a límites del tenant
2. Modifica valores específicos
3. Sistema valida que valores sean >= 0
4. Sistema guarda límites personalizados
5. Límites se aplican sobre los del plan
**Criterios de aceptación:**
- [ ] Límites personalizados sobreescriben plan
- [ ] Valores null usan defaults del plan
- [ ] Cambios se aplican inmediatamente
---
## RF-PMC-001-010: Obtener Tenant Actual
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-010 |
| **Nombre** | Obtener Tenant Actual |
| **Prioridad** | P1 |
| **Actor** | Usuario autenticado |
**Descripción:**
El sistema debe proporcionar los datos del tenant del usuario actual.
**Flujo principal:**
1. Usuario hace request a /tenants/current
2. Sistema extrae tenant_id del JWT
3. Sistema retorna datos del tenant
**Datos de salida:**
- Datos básicos del tenant
- Plan con límites efectivos
- Branding
- Settings relevantes para el usuario
**Criterios de aceptación:**
- [ ] Endpoint retorna tenant correcto
- [ ] Límites efectivos calculados correctamente
---
## RF-PMC-001-011: Validar Cuota de Uso
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-011 |
| **Nombre** | Validar Cuota de Uso |
| **Prioridad** | P1 |
| **Actor** | Sistema |
**Descripción:**
El sistema debe validar cuotas antes de operaciones que consumen recursos.
**Operaciones validadas:**
- Generación de imágenes
- Entrenamiento de modelos
- Subida de archivos (storage)
- Creación de usuarios
**Flujo principal:**
1. Usuario solicita operación
2. Sistema obtiene límites del tenant
3. Sistema obtiene uso actual
4. Sistema compara uso vs límite
5. Si excede: rechaza con error específico
6. Si no excede: permite operación
**Criterios de aceptación:**
- [ ] Validación ocurre antes de cada operación
- [ ] Mensaje de error indica límite y uso actual
- [ ] Operaciones no se ejecutan si exceden límite
---
## RF-PMC-001-012: Aplicar RLS por Tenant
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-012 |
| **Nombre** | Aplicar RLS por Tenant |
| **Prioridad** | P1 |
| **Actor** | Sistema |
**Descripción:**
El sistema debe garantizar aislamiento de datos entre tenants mediante RLS.
**Implementación:**
1. Todas las tablas principales tienen columna tenant_id
2. Políticas RLS filtran por tenant_id
3. Middleware inyecta tenant_id en cada request
4. SET app.current_tenant ejecutado antes de queries
**Tablas afectadas:**
- clients, contacts, brands, products
- projects, campaigns
- assets, collections
- users, roles
- generation_jobs, custom_models
- automation_flows, automation_runs
**Criterios de aceptación:**
- [ ] Queries solo retornan datos del tenant actual
- [ ] INSERT automáticamente incluye tenant_id
- [ ] No es posible acceder a datos de otro tenant
---
## RF-PMC-001-013: Gestionar Planes
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-013 |
| **Nombre** | Gestionar Planes |
| **Prioridad** | P2 |
| **Actor** | Super Admin |
**Descripción:**
El sistema debe permitir crear y gestionar planes de suscripción.
**Datos de entrada:**
- name: string
- code: string (único)
- features: object (funcionalidades habilitadas)
- limits: object (cuotas)
- price_monthly: decimal
- price_yearly: decimal
- is_active: boolean
**Operaciones:**
- Crear plan
- Editar plan
- Activar/desactivar plan
- Ver tenants por plan
**Criterios de aceptación:**
- [ ] CRUD de planes funciona
- [ ] Planes inactivos no asignables a nuevos tenants
- [ ] Cambio de plan en tenant actualiza límites
---
## RF-PMC-001-014: Cambiar Plan de Tenant
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-014 |
| **Nombre** | Cambiar Plan de Tenant |
| **Prioridad** | P2 |
| **Actor** | Super Admin |
**Descripción:**
El sistema debe permitir cambiar el plan de un tenant.
**Flujo principal:**
1. Super Admin selecciona tenant
2. Selecciona nuevo plan
3. Sistema valida compatibilidad
4. Sistema actualiza plan_id
5. Nuevos límites se aplican inmediatamente
6. Sistema notifica a admins del tenant
**Validaciones:**
- Si downgrade: verificar que uso actual no exceda nuevos límites
- Warning si usuarios exceden nuevo límite
**Criterios de aceptación:**
- [ ] Plan se actualiza correctamente
- [ ] Límites se aplican inmediatamente
- [ ] Warnings apropiados en downgrade
---
## RF-PMC-001-015: Ver Uso del Tenant
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-001-015 |
| **Nombre** | Ver Uso del Tenant |
| **Prioridad** | P2 |
| **Actor** | Super Admin, Tenant Admin |
**Descripción:**
El sistema debe mostrar métricas de uso del tenant.
**Datos de salida:**
- Generaciones: usado/límite
- Storage: usado/límite (GB)
- Usuarios: activos/límite
- Entrenamientos: usado/límite
- Período de facturación actual
**Criterios de aceptación:**
- [ ] Métricas se calculan correctamente
- [ ] Porcentajes y gráficos visuales
- [ ] Alertas cuando se acerca al límite (>80%)
---
## Resumen
| Prioridad | Cantidad |
|-----------|----------|
| P1 | 6 |
| P2 | 6 |
| P3 | 3 |
| **Total** | **15** |
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,587 @@
# Requerimientos Funcionales - PMC-002 CRM
**Módulo:** CRM
**Versión:** 1.0.0
**Fecha:** 2025-12-08
---
## Gestión de Clientes
### RF-PMC-002-001: Crear Cliente
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-001 |
| **Nombre** | Crear Cliente |
| **Prioridad** | P1 |
| **Actor** | Creative, Analyst, Tenant Admin |
**Descripción:**
El sistema debe permitir registrar un nuevo cliente de la agencia.
**Datos de entrada:**
- name: string (requerido)
- legal_name: string (opcional)
- tax_id: string (opcional)
- industry: string (opcional)
- size: enum (opcional)
- website: string (opcional)
- notes: text (opcional)
**Criterios de aceptación:**
- [ ] Cliente se crea con status "prospect"
- [ ] tenant_id se asigna automáticamente
- [ ] Validación de datos funciona
---
### RF-PMC-002-002: Editar Cliente
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-002 |
| **Nombre** | Editar Cliente |
| **Prioridad** | P1 |
| **Actor** | Creative, Analyst, Tenant Admin |
**Descripción:**
El sistema debe permitir modificar datos de un cliente existente.
**Criterios de aceptación:**
- [ ] Todos los campos editables se actualizan
- [ ] Cambio de status registra historial
- [ ] Audit log registra modificaciones
---
### RF-PMC-002-003: Listar Clientes
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-003 |
| **Nombre** | Listar Clientes |
| **Prioridad** | P1 |
| **Actor** | Todos los roles |
**Descripción:**
El sistema debe mostrar lista de clientes con filtros y paginación.
**Filtros disponibles:**
- status: prospect, active, inactive, churned
- industry: string
- size: enum
- search: nombre o legal_name
**Criterios de aceptación:**
- [ ] Paginación funciona correctamente
- [ ] Filtros se combinan con AND
- [ ] Ordenamiento por nombre/fecha
---
### RF-PMC-002-004: Ver Ficha de Cliente
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-004 |
| **Nombre** | Ver Ficha de Cliente |
| **Prioridad** | P1 |
| **Actor** | Todos los roles |
**Descripción:**
El sistema debe mostrar vista detallada del cliente con información relacionada.
**Datos mostrados:**
- Información básica
- Contactos asociados
- Marcas del cliente
- Proyectos activos
- Oportunidades abiertas
- Historial de actividad
**Criterios de aceptación:**
- [ ] Tabs organizan información
- [ ] Datos relacionados cargan correctamente
- [ ] Acciones rápidas disponibles
---
### RF-PMC-002-005: Eliminar Cliente
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-005 |
| **Nombre** | Eliminar Cliente |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Descripción:**
El sistema debe permitir eliminar un cliente (soft delete).
**Validaciones:**
- No tiene proyectos activos
- Confirmación requerida
**Criterios de aceptación:**
- [ ] Soft delete funciona
- [ ] Validaciones impiden eliminación si hay dependencias activas
---
## Gestión de Contactos
### RF-PMC-002-006: Crear Contacto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-006 |
| **Nombre** | Crear Contacto |
| **Prioridad** | P1 |
| **Actor** | Creative, Analyst, Tenant Admin |
**Datos de entrada:**
- client_id: UUID (requerido)
- first_name: string (requerido)
- last_name: string (requerido)
- email: string (requerido)
- phone: string (opcional)
- position: string (opcional)
- is_primary: boolean (default false)
**Criterios de aceptación:**
- [ ] Contacto se asocia al cliente
- [ ] Email validado (formato)
- [ ] Solo un contacto primario por cliente
---
### RF-PMC-002-007: Editar Contacto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-007 |
| **Nombre** | Editar Contacto |
| **Prioridad** | P2 |
| **Actor** | Creative, Analyst, Tenant Admin |
**Criterios de aceptación:**
- [ ] Campos se actualizan correctamente
- [ ] Cambio de is_primary actualiza otros contactos
---
### RF-PMC-002-008: Listar Contactos
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-008 |
| **Nombre** | Listar Contactos |
| **Prioridad** | P2 |
| **Actor** | Todos los roles |
**Descripción:**
Listar contactos del tenant o de un cliente específico.
**Criterios de aceptación:**
- [ ] Filtro por cliente funciona
- [ ] Búsqueda por nombre/email
- [ ] Paginación implementada
---
### RF-PMC-002-009: Marcar Contacto Primario
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-009 |
| **Nombre** | Marcar Contacto Primario |
| **Prioridad** | P2 |
| **Actor** | Creative, Analyst, Tenant Admin |
**Descripción:**
Designar un contacto como principal del cliente.
**Criterios de aceptación:**
- [ ] Solo un contacto primario por cliente
- [ ] Al marcar uno, otros se desmarcan automáticamente
---
## Gestión de Marcas
### RF-PMC-002-010: Crear Marca
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-010 |
| **Nombre** | Crear Marca |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Datos de entrada:**
- client_id: UUID (requerido)
- name: string (requerido)
- description: text (opcional)
- identity: object (opcional)
- logo_url
- primary_color
- secondary_colors
- typography
- tone_of_voice
- keywords
- forbidden_words
- visual_style
**Criterios de aceptación:**
- [ ] Marca se asocia al cliente
- [ ] Identity se almacena como JSONB
- [ ] Logo se puede subir o referenciar URL
---
### RF-PMC-002-011: Editar Marca
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-011 |
| **Nombre** | Editar Marca |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Criterios de aceptación:**
- [ ] Todos los campos de identity editables
- [ ] Preview de colores en tiempo real
- [ ] Cambios se propagan a nuevas generaciones
---
### RF-PMC-002-012: Definir Identidad Visual
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-012 |
| **Nombre** | Definir Identidad Visual |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Descripción:**
Configurar todos los elementos de identidad visual de la marca.
**Campos de identidad:**
- Logo (principal y variaciones)
- Paleta de colores
- Tipografías
- Tono de voz
- Keywords positivas
- Palabras prohibidas
- Estilo visual preferido
**Criterios de aceptación:**
- [ ] Formulario estructurado para cada sección
- [ ] Upload de logos funciona
- [ ] Preview visual de paleta de colores
---
### RF-PMC-002-013: Asociar LoRA a Marca
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-013 |
| **Nombre** | Asociar LoRA a Marca |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Descripción:**
Vincular modelos LoRA entrenados con una marca.
**Flujo:**
1. Usuario accede a configuración de marca
2. Selecciona "Modelos IA"
3. Elige LoRA(s) del catálogo del tenant
4. Sistema vincula LoRA con marca
5. LoRA se usa automáticamente en generaciones de la marca
**Criterios de aceptación:**
- [ ] Múltiples LoRAs pueden asociarse
- [ ] LoRA se aplica por defecto en generación
- [ ] Desasociación funciona
---
### RF-PMC-002-014: Listar Marcas
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-014 |
| **Nombre** | Listar Marcas |
| **Prioridad** | P1 |
| **Actor** | Todos los roles |
**Criterios de aceptación:**
- [ ] Filtro por cliente funciona
- [ ] Preview de logo/colores en lista
- [ ] Búsqueda por nombre
---
## Gestión de Productos
### RF-PMC-002-015: Crear Producto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-015 |
| **Nombre** | Crear Producto |
| **Prioridad** | P1 |
| **Actor** | Creative, Analyst |
**Datos de entrada:**
- brand_id: UUID (requerido)
- name: string (requerido)
- sku: string (opcional)
- description: text (opcional)
- category: string (opcional)
- attributes: object (precio, features, etc.)
- reference_images: array[file] (opcional)
**Criterios de aceptación:**
- [ ] Producto se asocia a marca
- [ ] Imágenes de referencia se almacenan
- [ ] Status inicial "active"
---
### RF-PMC-002-016: Editar Producto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-016 |
| **Nombre** | Editar Producto |
| **Prioridad** | P2 |
| **Actor** | Creative, Analyst |
**Criterios de aceptación:**
- [ ] Campos se actualizan correctamente
- [ ] Imágenes de referencia pueden agregarse/quitarse
---
### RF-PMC-002-017: Subir Imágenes de Referencia
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-017 |
| **Nombre** | Subir Imágenes de Referencia |
| **Prioridad** | P1 |
| **Actor** | Creative, Analyst |
**Descripción:**
Agregar fotos reales del producto para usar como referencia en generación.
**Criterios de aceptación:**
- [ ] Upload múltiple funciona
- [ ] Formatos: JPG, PNG, WebP
- [ ] Tamaño máximo: 10MB por imagen
- [ ] Thumbnails generados automáticamente
---
### RF-PMC-002-018: Trigger Generación desde Producto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-018 |
| **Nombre** | Trigger Generación desde Producto |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Descripción:**
Iniciar generación de contenido directamente desde la ficha de producto.
**Flujo:**
1. Usuario ve ficha de producto
2. Selecciona "Generar contenido"
3. Elige workflow template
4. Configura opciones (cantidad, estilo)
5. Sistema crea job de generación
6. Sistema redirige a monitor o muestra progreso
**Criterios de aceptación:**
- [ ] Datos del producto se cargan al job
- [ ] Identidad de marca se incluye
- [ ] LoRAs se aplican automáticamente
---
### RF-PMC-002-019: Listar Productos
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-019 |
| **Nombre** | Listar Productos |
| **Prioridad** | P1 |
| **Actor** | Todos los roles |
**Criterios de aceptación:**
- [ ] Filtro por marca funciona
- [ ] Vista de catálogo con imágenes
- [ ] Búsqueda por nombre/SKU
---
## Gestión de Oportunidades
### RF-PMC-002-020: Crear Oportunidad
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-020 |
| **Nombre** | Crear Oportunidad |
| **Prioridad** | P2 |
| **Actor** | Analyst, Tenant Admin |
**Datos de entrada:**
- client_id: UUID (requerido)
- name: string (requerido)
- description: text (opcional)
- value: decimal (opcional)
- currency: string (default: USD)
- expected_close_date: date (opcional)
**Criterios de aceptación:**
- [ ] Stage inicial "lead"
- [ ] Probability calculada por stage
---
### RF-PMC-002-021: Mover Oportunidad en Pipeline
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-021 |
| **Nombre** | Mover Oportunidad en Pipeline |
| **Prioridad** | P2 |
| **Actor** | Analyst, Tenant Admin |
**Descripción:**
Cambiar stage de una oportunidad mediante drag & drop o acción directa.
**Stages:**
- lead → qualified → proposal → negotiation → won/lost
**Criterios de aceptación:**
- [ ] Drag & drop en vista Kanban
- [ ] Probability se actualiza automáticamente
- [ ] Registro de historial de cambios
---
### RF-PMC-002-022: Cerrar Oportunidad como Ganada
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-022 |
| **Nombre** | Cerrar Oportunidad como Ganada |
| **Prioridad** | P2 |
| **Actor** | Analyst, Tenant Admin |
**Flujo:**
1. Usuario mueve a stage "won"
2. Sistema solicita valor final confirmado
3. Sistema marca como ganada
4. Sistema ofrece crear proyecto
**Criterios de aceptación:**
- [ ] actual_close_date se registra
- [ ] Opción de crear proyecto disponible
---
### RF-PMC-002-023: Cerrar Oportunidad como Perdida
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-023 |
| **Nombre** | Cerrar Oportunidad como Perdida |
| **Prioridad** | P2 |
| **Actor** | Analyst, Tenant Admin |
**Flujo:**
1. Usuario mueve a stage "lost"
2. Sistema solicita motivo de pérdida
3. Sistema registra lost_reason
4. Sistema marca como perdida
**Criterios de aceptación:**
- [ ] lost_reason es requerido
- [ ] Oportunidad no editable después de cerrar
---
### RF-PMC-002-024: Convertir Oportunidad en Proyecto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-024 |
| **Nombre** | Convertir Oportunidad en Proyecto |
| **Prioridad** | P2 |
| **Actor** | Analyst, Tenant Admin |
**Descripción:**
Crear proyecto automáticamente desde oportunidad ganada.
**Flujo:**
1. Usuario selecciona "Convertir a proyecto"
2. Sistema muestra formulario pre-llenado
3. Usuario confirma/modifica datos
4. Sistema crea proyecto vinculado al cliente
5. Sistema vincula oportunidad con proyecto
**Criterios de aceptación:**
- [ ] Datos del cliente se heredan
- [ ] Descripción de oportunidad se copia a proyecto
- [ ] Relación bidireccional establecida
---
### RF-PMC-002-025: Vista Kanban de Oportunidades
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-002-025 |
| **Nombre** | Vista Kanban de Oportunidades |
| **Prioridad** | P2 |
| **Actor** | Analyst, Tenant Admin |
**Descripción:**
Mostrar pipeline de oportunidades en formato Kanban.
**Características:**
- Columnas por stage
- Cards con info resumida
- Drag & drop entre columnas
- Filtros por cliente, fecha, valor
- Totales por columna
**Criterios de aceptación:**
- [ ] Drag & drop funciona
- [ ] Totales se calculan correctamente
- [ ] Filtros se aplican en tiempo real
---
## Resumen
| Prioridad | Cantidad |
|-----------|----------|
| P1 | 14 |
| P2 | 10 |
| P3 | 1 |
| **Total** | **25** |
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,503 @@
# Requerimientos Funcionales - PMC-003 Projects
**Módulo:** Projects
**Versión:** 1.0.0
**Fecha:** 2025-12-08
---
## Gestión de Proyectos
### RF-PMC-003-001: Crear Proyecto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-001 |
| **Nombre** | Crear Proyecto |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Datos de entrada:**
- client_id: UUID (requerido)
- name: string (requerido)
- description: text (opcional)
- start_date: date (opcional)
- end_date: date (opcional)
- budget: decimal (opcional)
**Criterios de aceptación:**
- [ ] Proyecto se crea con status "draft"
- [ ] Código autogenerado (PRJ-YYYY-XXX)
- [ ] Owner asignado al creador
---
### RF-PMC-003-002: Editar Proyecto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-002 |
| **Nombre** | Editar Proyecto |
| **Prioridad** | P1 |
| **Actor** | Project Owner, Tenant Admin |
**Criterios de aceptación:**
- [ ] Campos básicos editables
- [ ] Cambio de cliente requiere confirmación
- [ ] Historial de cambios registrado
---
### RF-PMC-003-003: Cambiar Estado de Proyecto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-003 |
| **Nombre** | Cambiar Estado de Proyecto |
| **Prioridad** | P1 |
| **Actor** | Project Owner, Tenant Admin |
**Estados válidos:**
- draft → active
- active → on_hold, completed
- on_hold → active
- cualquiera → cancelled
**Criterios de aceptación:**
- [ ] Transiciones válidas se permiten
- [ ] Transiciones inválidas se bloquean
- [ ] Notificación al equipo en cambio de estado
---
### RF-PMC-003-004: Asignar Equipo a Proyecto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-004 |
| **Nombre** | Asignar Equipo a Proyecto |
| **Prioridad** | P2 |
| **Actor** | Project Owner, Tenant Admin |
**Descripción:**
Agregar o quitar miembros del equipo de un proyecto.
**Criterios de aceptación:**
- [ ] Usuarios del tenant pueden asignarse
- [ ] Owner siempre está en el equipo
- [ ] Notificación al agregar miembro
---
### RF-PMC-003-005: Listar Proyectos
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-005 |
| **Nombre** | Listar Proyectos |
| **Prioridad** | P1 |
| **Actor** | Todos los roles |
**Filtros:**
- client_id
- status
- owner_id
- date_range
**Criterios de aceptación:**
- [ ] Paginación funciona
- [ ] Filtros combinables
- [ ] Vista lista y tarjetas
---
### RF-PMC-003-006: Ver Dashboard de Proyecto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-006 |
| **Nombre** | Ver Dashboard de Proyecto |
| **Prioridad** | P1 |
| **Actor** | Miembros del proyecto |
**Información mostrada:**
- Resumen del proyecto
- Campañas con estado
- Assets recientes
- Actividad del equipo
- Métricas de progreso
**Criterios de aceptación:**
- [ ] Dashboard carga correctamente
- [ ] Métricas calculadas en tiempo real
- [ ] Acciones rápidas disponibles
---
## Gestión de Campañas
### RF-PMC-003-007: Crear Campaña
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-007 |
| **Nombre** | Crear Campaña |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de entrada:**
- project_id: UUID (requerido)
- brand_id: UUID (requerido)
- name: string (requerido)
- type: enum (requerido)
- channels: array[string]
- start_date: date (opcional)
- end_date: date (opcional)
**Criterios de aceptación:**
- [ ] Campaña se crea con status "draft"
- [ ] Brand se vincula correctamente
- [ ] Brief vacío se inicializa
---
### RF-PMC-003-008: Editar Campaña
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-008 |
| **Nombre** | Editar Campaña |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Criterios de aceptación:**
- [ ] Campos básicos editables en cualquier estado
- [ ] Brief editable hasta "in_production"
---
### RF-PMC-003-009: Completar Brief de Campaña
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-009 |
| **Nombre** | Completar Brief de Campaña |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Secciones del Brief:**
- Objetivo: descripción, KPIs
- Audiencia: demographics, psychographics, pain_points
- Mensajes: main_message, tone, CTA, hashtags
- Visual: style, mood, color_palette, references
- Restricciones: forbidden_words, disclaimers
- Entregables: formatos, cantidades
**Criterios de aceptación:**
- [ ] Formulario con secciones colapsables
- [ ] Validación de campos mínimos
- [ ] Guardado automático (draft)
- [ ] Al completar: estado → "briefing"
---
### RF-PMC-003-010: Usar Plantilla de Brief
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-010 |
| **Nombre** | Usar Plantilla de Brief |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Descripción:**
Cargar brief desde una plantilla predefinida.
**Criterios de aceptación:**
- [ ] Listado de plantillas disponibles
- [ ] Preview de plantilla antes de aplicar
- [ ] Campos se pre-llenan pero son editables
---
### RF-PMC-003-011: Cambiar Estado de Campaña
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-011 |
| **Nombre** | Cambiar Estado de Campaña |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Estados y transiciones:**
```
draft → briefing → in_production → review → approved → published
↑_______________|
(revision_requested)
```
**Validaciones:**
- briefing → in_production: brief mínimo completado
- review → approved: al menos 1 asset aprobado
- approved → published: confirmación requerida
**Criterios de aceptación:**
- [ ] Transiciones válidas funcionan
- [ ] Validaciones se aplican
- [ ] Notificaciones en cada cambio
---
### RF-PMC-003-012: Listar Campañas
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-012 |
| **Nombre** | Listar Campañas |
| **Prioridad** | P1 |
| **Actor** | Todos los roles |
**Vistas:**
- Lista con filtros
- Kanban por estado
- Calendario por fechas
**Criterios de aceptación:**
- [ ] Filtro por proyecto, brand, status, type
- [ ] Vista Kanban con drag & drop
- [ ] Búsqueda por nombre
---
### RF-PMC-003-013: Ver Detalle de Campaña
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-013 |
| **Nombre** | Ver Detalle de Campaña |
| **Prioridad** | P1 |
| **Actor** | Miembros del proyecto |
**Información mostrada:**
- Datos de campaña
- Brief completo
- Assets generados con estados
- Historial de actividad
- Jobs de generación
**Criterios de aceptación:**
- [ ] Todas las secciones visibles
- [ ] Assets organizados por estado
- [ ] Acciones contextuales disponibles
---
## Generación de Contenido
### RF-PMC-003-014: Lanzar Generación desde Campaña
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-014 |
| **Nombre** | Lanzar Generación desde Campaña |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Flujo:**
1. Usuario abre campaña en status "briefing" o superior
2. Selecciona "Generar contenido"
3. Elige workflows según deliverables del brief
4. Configura opciones adicionales
5. Sistema crea jobs de generación
6. Sistema cambia status a "in_production" si no lo está
**Criterios de aceptación:**
- [ ] Brief se usa para configurar generación
- [ ] Múltiples workflows ejecutables
- [ ] Progreso visible en tiempo real
---
### RF-PMC-003-015: Seleccionar Workflows de Generación
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-015 |
| **Nombre** | Seleccionar Workflows de Generación |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Descripción:**
Elegir qué workflows ejecutar basado en deliverables del brief.
**Criterios de aceptación:**
- [ ] Workflows sugeridos según tipo de campaña
- [ ] Preview de cada workflow
- [ ] Configuración de cantidad por workflow
---
### RF-PMC-003-016: Monitorear Progreso de Generación
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-016 |
| **Nombre** | Monitorear Progreso de Generación |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Información mostrada:**
- Jobs en cola
- Jobs procesando (con %)
- Jobs completados
- Jobs fallidos
**Criterios de aceptación:**
- [ ] Actualización en tiempo real (websocket)
- [ ] Posibilidad de cancelar jobs pendientes
- [ ] Reintentar jobs fallidos
---
## Flujo de Aprobación
### RF-PMC-003-017: Aprobar Asset de Campaña
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-017 |
| **Nombre** | Aprobar Asset de Campaña |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Flujo:**
1. Usuario revisa asset en vista de revisión
2. Selecciona "Aprobar"
3. Sistema marca asset como aprobado
4. Sistema registra aprobador y timestamp
**Criterios de aceptación:**
- [ ] Asset cambia a status "approved"
- [ ] Metadata de aprobación registrada
- [ ] No editable después de aprobar
---
### RF-PMC-003-018: Rechazar Asset de Campaña
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-018 |
| **Nombre** | Rechazar Asset de Campaña |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Flujo:**
1. Usuario revisa asset
2. Selecciona "Rechazar"
3. Sistema solicita feedback obligatorio
4. Sistema marca asset como rechazado
**Criterios de aceptación:**
- [ ] Feedback es requerido
- [ ] Asset puede regenerarse
- [ ] Historial de rechazos visible
---
### RF-PMC-003-019: Solicitar Revisión de Asset
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-019 |
| **Nombre** | Solicitar Revisión de Asset |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Descripción:**
Pedir cambios específicos en un asset antes de decidir.
**Criterios de aceptación:**
- [ ] Comentario con posición en imagen
- [ ] Asset queda en "revision_requested"
- [ ] Notificación a equipo
---
### RF-PMC-003-020: Aprobar Todos los Assets
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-020 |
| **Nombre** | Aprobar Todos los Assets |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Descripción:**
Acción bulk para aprobar todos los assets pendientes.
**Criterios de aceptación:**
- [ ] Confirmación requerida
- [ ] Solo assets en "pending_review"
- [ ] Registro individual por asset
---
### RF-PMC-003-021: Regenerar Asset
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-021 |
| **Nombre** | Regenerar Asset |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Descripción:**
Crear nueva versión de un asset rechazado o en revisión.
**Flujo:**
1. Usuario selecciona asset rechazado
2. Selecciona "Regenerar"
3. Opcionalmente ajusta parámetros
4. Sistema crea nuevo job
5. Nuevo asset se vincula como versión
**Criterios de aceptación:**
- [ ] Parámetros originales pre-cargados
- [ ] Historial de versiones mantenido
- [ ] Original no se elimina
---
### RF-PMC-003-022: Descargar Assets Aprobados
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-003-022 |
| **Nombre** | Descargar Assets Aprobados |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Descripción:**
Descargar pack de todos los assets aprobados de la campaña.
**Criterios de aceptación:**
- [ ] ZIP generado con estructura organizada
- [ ] Solo assets aprobados incluidos
- [ ] Copys incluidos como .txt
- [ ] Opción de selección manual
---
## Resumen
| Prioridad | Cantidad |
|-----------|----------|
| P1 | 15 |
| P2 | 7 |
| **Total** | **22** |
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,641 @@
# Requerimientos Funcionales - PMC-004 Generation
**Módulo:** Generation (Motor IA)
**Versión:** 1.0.0
**Fecha:** 2025-12-08
---
## Generación de Imágenes
### RF-PMC-004-001: Generar Imagen Text-to-Image
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-001 |
| **Nombre** | Generar Imagen Text-to-Image |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de entrada:**
- prompt: string (requerido)
- negative_prompt: string (opcional)
- width: number (default: 1024)
- height: number (default: 1024)
- seed: number (opcional, aleatorio si no se especifica)
- steps: number (default: 30)
- cfg_scale: number (default: 7.5)
- lora_id: UUID (opcional)
- brand_id: UUID (opcional, para cargar LoRA automáticamente)
**Criterios de aceptación:**
- [ ] Imagen se genera correctamente
- [ ] Parámetros se aplican
- [ ] LoRA se carga si especificado
- [ ] Asset se crea automáticamente
---
### RF-PMC-004-002: Generar Imagen Image-to-Image
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-002 |
| **Nombre** | Generar Imagen Image-to-Image |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de entrada:**
- input_image: file/URL (requerido)
- prompt: string (requerido)
- strength: number (0.0-1.0, default: 0.75)
- Otros parámetros de T2I
**Criterios de aceptación:**
- [ ] Imagen input se procesa correctamente
- [ ] Strength afecta resultado
- [ ] Formatos soportados: PNG, JPG, WebP
---
### RF-PMC-004-003: Aplicar Inpainting
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-003 |
| **Nombre** | Aplicar Inpainting |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Datos de entrada:**
- input_image: file/URL (requerido)
- mask_image: file (requerido)
- prompt: string (requerido)
- Otros parámetros de generación
**Descripción:**
Regenerar solo la parte enmascarada de una imagen.
**Criterios de aceptación:**
- [ ] Máscara define área a regenerar
- [ ] Resto de imagen preservado
- [ ] Editor de máscara en UI
---
### RF-PMC-004-004: Aplicar Upscaling
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-004 |
| **Nombre** | Aplicar Upscaling |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de entrada:**
- input_image: file/URL (requerido)
- scale: number (2 o 4)
- model: string (default: "RealESRGAN")
**Criterios de aceptación:**
- [ ] Imagen se escala correctamente
- [ ] Calidad mejorada, no solo interpolación
- [ ] Formatos de salida preservados
---
### RF-PMC-004-005: Generar Batch de Imágenes
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-005 |
| **Nombre** | Generar Batch de Imágenes |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de entrada:**
- base_params: object (parámetros comunes)
- count: number (cantidad a generar)
- variation_type: string (seed, prompt_variation)
**Criterios de aceptación:**
- [ ] N imágenes generadas
- [ ] Seeds diferentes por imagen
- [ ] Todas vinculadas al mismo job
---
### RF-PMC-004-006: Remover Fondo
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-006 |
| **Nombre** | Remover Fondo |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Datos de entrada:**
- input_image: file/URL (requerido)
**Criterios de aceptación:**
- [ ] Fondo removido correctamente
- [ ] Salida PNG con transparencia
- [ ] Bordes suaves en objetos
---
## Generación de Texto
### RF-PMC-004-007: Generar Copy Publicitario
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-007 |
| **Nombre** | Generar Copy Publicitario |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de entrada:**
- context: object
- product_name: string
- product_description: string
- brand_tone: string
- target_audience: string
- objective: string
- type: enum (title, description, cta, full_post)
- max_length: number (opcional)
- variations: number (default: 3)
**Criterios de aceptación:**
- [ ] Texto generado coherente
- [ ] Tono respeta brand guidelines
- [ ] Múltiples variaciones disponibles
---
### RF-PMC-004-008: Generar Hashtags
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-008 |
| **Nombre** | Generar Hashtags |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Datos de entrada:**
- context: string (descripción del contenido)
- count: number (default: 10)
- platform: string (instagram, twitter, linkedin)
**Criterios de aceptación:**
- [ ] Hashtags relevantes generados
- [ ] Formato correcto (#hashtag)
- [ ] Sin espacios ni caracteres inválidos
---
### RF-PMC-004-009: Adaptar Tono de Texto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-009 |
| **Nombre** | Adaptar Tono de Texto |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Datos de entrada:**
- input_text: string (requerido)
- target_tone: string (formal, casual, playful, professional)
**Criterios de aceptación:**
- [ ] Mensaje preservado, tono cambiado
- [ ] Opciones de tono claras
---
## Workflows
### RF-PMC-004-010: Listar Workflow Templates
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-010 |
| **Nombre** | Listar Workflow Templates |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de salida:**
- Lista de workflows disponibles
- Por cada uno: nombre, descripción, tipo, inputs requeridos
**Criterios de aceptación:**
- [ ] Workflows de sistema incluidos
- [ ] Workflows custom del tenant incluidos
- [ ] Filtro por tipo/categoría
---
### RF-PMC-004-011: Ver Detalle de Workflow
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-011 |
| **Nombre** | Ver Detalle de Workflow |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de salida:**
- Descripción completa
- Inputs requeridos y opcionales
- Outputs esperados
- Ejemplos de resultado
- Tiempo estimado
**Criterios de aceptación:**
- [ ] Schema de inputs documentado
- [ ] Ejemplos visuales disponibles
---
### RF-PMC-004-012: Ejecutar Workflow
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-012 |
| **Nombre** | Ejecutar Workflow |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de entrada:**
- workflow_id: UUID (requerido)
- inputs: object (según schema del workflow)
- campaign_id: UUID (opcional)
- brand_id: UUID (opcional)
**Flujo:**
1. Sistema valida inputs contra schema
2. Sistema verifica cuotas del tenant
3. Sistema crea GenerationJob
4. Sistema encola job
5. Sistema retorna job_id
**Criterios de aceptación:**
- [ ] Validación de inputs funciona
- [ ] Job se crea correctamente
- [ ] ID retornado para tracking
---
### RF-PMC-004-013: Crear Workflow Template (Admin)
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-013 |
| **Nombre** | Crear Workflow Template |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Datos de entrada:**
- name: string
- description: text
- type: enum
- comfyui_workflow: JSON
- input_schema: JSON
- output_config: object
**Criterios de aceptación:**
- [ ] Workflow se almacena correctamente
- [ ] Schema de inputs validado
- [ ] Disponible para usuarios del tenant
---
## Modelos Personalizados
### RF-PMC-004-014: Listar Modelos Custom
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-014 |
| **Nombre** | Listar Modelos Custom |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de salida:**
- Lista de LoRAs, checkpoints disponibles
- Status de cada uno
- Brand asociada (si aplica)
**Criterios de aceptación:**
- [ ] Solo modelos del tenant
- [ ] Filtro por tipo y brand
- [ ] Preview images mostradas
---
### RF-PMC-004-015: Registrar Modelo Custom
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-015 |
| **Nombre** | Registrar Modelo Custom |
| **Prioridad** | P1 |
| **Actor** | Tenant Admin |
**Datos de entrada:**
- name: string (requerido)
- type: enum (lora, checkpoint, embedding)
- file: upload (requerido)
- purpose: string
- trigger_word: string (para LoRAs)
- brand_id: UUID (opcional)
- preview_images: array[file]
**Criterios de aceptación:**
- [ ] Archivo subido a storage
- [ ] Registro en BD
- [ ] Disponible para generación
---
### RF-PMC-004-016: Eliminar Modelo Custom
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-016 |
| **Nombre** | Eliminar Modelo Custom |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Criterios de aceptación:**
- [ ] Confirmación requerida
- [ ] Archivo eliminado de storage
- [ ] Registro eliminado de BD
---
### RF-PMC-004-017: Iniciar Entrenamiento de LoRA
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-017 |
| **Nombre** | Iniciar Entrenamiento de LoRA |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Datos de entrada:**
- name: string
- training_images: array[file] (mínimo 10)
- base_model: string
- steps: number (default: 1000)
- learning_rate: number (default: 0.0001)
- trigger_word: string (requerido)
**Criterios de aceptación:**
- [ ] Mínimo 10 imágenes validado
- [ ] Job de entrenamiento creado
- [ ] Status actualizado durante entrenamiento
- [ ] Modelo disponible al completar
---
## Cola de Tareas
### RF-PMC-004-018: Ver Estado de Cola
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-018 |
| **Nombre** | Ver Estado de Cola |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Datos de salida:**
- Jobs en cola (pending)
- Jobs procesando
- Jobs completados (recientes)
- Jobs fallidos (recientes)
**Criterios de aceptación:**
- [ ] Actualización en tiempo real
- [ ] Filtro por usuario/campaña
- [ ] Progreso visible para jobs activos
---
### RF-PMC-004-019: Ver Detalle de Job
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-019 |
| **Nombre** | Ver Detalle de Job |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de salida:**
- Parámetros de entrada
- Status y progreso
- Timestamps
- Outputs generados
- Error (si falló)
**Criterios de aceptación:**
- [ ] Toda la información visible
- [ ] Links a assets generados
---
### RF-PMC-004-020: Cancelar Job
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-020 |
| **Nombre** | Cancelar Job |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Precondiciones:**
- Job en status "queued"
**Criterios de aceptación:**
- [ ] Job cambia a "cancelled"
- [ ] No se ejecuta
- [ ] Cuota no consumida
---
### RF-PMC-004-021: Reintentar Job Fallido
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-021 |
| **Nombre** | Reintentar Job Fallido |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Precondiciones:**
- Job en status "failed"
**Criterios de aceptación:**
- [ ] Nuevo job creado con mismos parámetros
- [ ] Original marcado como "retried"
- [ ] Hasta 3 reintentos permitidos
---
### RF-PMC-004-022: Cambiar Prioridad de Job (Admin)
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-022 |
| **Nombre** | Cambiar Prioridad de Job |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Precondiciones:**
- Job en status "queued"
**Criterios de aceptación:**
- [ ] Prioridad actualizada
- [ ] Posición en cola recalculada
---
## Integración ComfyUI
### RF-PMC-004-023: Enviar Workflow a ComfyUI
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-023 |
| **Nombre** | Enviar Workflow a ComfyUI |
| **Prioridad** | P1 |
| **Actor** | Sistema |
**Descripción:**
El sistema debe enviar workflows a ComfyUI para ejecución.
**Criterios de aceptación:**
- [ ] Payload correcto enviado
- [ ] Respuesta de ComfyUI procesada
- [ ] Errores manejados correctamente
---
### RF-PMC-004-024: Recibir Resultados de ComfyUI
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-024 |
| **Nombre** | Recibir Resultados de ComfyUI |
| **Prioridad** | P1 |
| **Actor** | Sistema |
**Descripción:**
El sistema debe procesar callbacks/webhooks de ComfyUI con resultados.
**Criterios de aceptación:**
- [ ] Imágenes descargadas y almacenadas
- [ ] Assets creados automáticamente
- [ ] Job actualizado a "completed"
---
### RF-PMC-004-025: Monitorear Progreso en ComfyUI
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-025 |
| **Nombre** | Monitorear Progreso en ComfyUI |
| **Prioridad** | P2 |
| **Actor** | Sistema |
**Descripción:**
Tracking de progreso durante ejecución del workflow.
**Criterios de aceptación:**
- [ ] Websocket conectado a ComfyUI
- [ ] Progreso actualizado en tiempo real
- [ ] Propagado a frontend
---
## Validaciones
### RF-PMC-004-026: Validar Cuota Antes de Generación
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-026 |
| **Nombre** | Validar Cuota Antes de Generación |
| **Prioridad** | P1 |
| **Actor** | Sistema |
**Descripción:**
Verificar límites del tenant antes de aceptar job.
**Criterios de aceptación:**
- [ ] Generaciones mensuales verificadas
- [ ] Storage verificado para outputs
- [ ] Error claro si excede límite
---
### RF-PMC-004-027: Agregar Negative Prompts Automáticos
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-027 |
| **Nombre** | Agregar Negative Prompts Automáticos |
| **Prioridad** | P2 |
| **Actor** | Sistema |
**Descripción:**
Añadir negative prompts de calidad estándar.
**Negative prompts por defecto:**
- "blurry, low quality, watermark, signature, bad anatomy, deformed..."
**Criterios de aceptación:**
- [ ] Negativos agregados automáticamente
- [ ] Usuario puede sobreescribir
- [ ] Configurables por tenant
---
### RF-PMC-004-028: Cargar Identidad de Marca Automáticamente
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-004-028 |
| **Nombre** | Cargar Identidad de Marca Automáticamente |
| **Prioridad** | P1 |
| **Actor** | Sistema |
**Descripción:**
Al generar para una campaña/marca, cargar automáticamente:
- LoRAs asociados
- Colores de marca (para prompts)
- Forbidden words (para negative prompts)
- Tono de voz (para texto)
**Criterios de aceptación:**
- [ ] LoRA se inyecta en workflow
- [ ] Colores incluidos en prompt si aplica
- [ ] Forbidden words en negative prompt
---
## Resumen
| Prioridad | Cantidad |
|-----------|----------|
| P1 | 17 |
| P2 | 9 |
| P3 | 2 |
| **Total** | **28** |
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,414 @@
# Requerimientos Funcionales - PMC-005 Automation
**Módulo:** Automation
**Versión:** 1.0.0
**Fecha:** 2025-12-08
---
## Gestión de Flujos
### RF-PMC-005-001: Listar Flujos de Automatización
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-001 |
| **Nombre** | Listar Flujos de Automatización |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Datos de salida:**
- Lista de flujos disponibles
- Estado (activo/inactivo)
- Tipo (trigger_based, scheduled, manual)
- Última ejecución
- Conteo de ejecuciones
**Criterios de aceptación:**
- [ ] Flujos de sistema y custom listados
- [ ] Filtro por tipo y estado
- [ ] Ordenamiento por nombre/última ejecución
---
### RF-PMC-005-002: Ver Detalle de Flujo
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-002 |
| **Nombre** | Ver Detalle de Flujo |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Datos de salida:**
- Descripción completa
- Evento trigger
- Configuración
- Historial de ejecuciones recientes
**Criterios de aceptación:**
- [ ] Toda la información visible
- [ ] Link a workflow en n8n (admin)
---
### RF-PMC-005-003: Activar Flujo
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-003 |
| **Nombre** | Activar Flujo |
| **Prioridad** | P1 |
| **Actor** | Tenant Admin |
**Descripción:**
Habilitar un flujo para que procese eventos.
**Criterios de aceptación:**
- [ ] is_active cambia a true
- [ ] Flujo comienza a procesar eventos
- [ ] Registro en audit log
---
### RF-PMC-005-004: Desactivar Flujo
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-004 |
| **Nombre** | Desactivar Flujo |
| **Prioridad** | P1 |
| **Actor** | Tenant Admin |
**Descripción:**
Deshabilitar un flujo sin eliminarlo.
**Criterios de aceptación:**
- [ ] is_active cambia a false
- [ ] Eventos son ignorados
- [ ] Configuración preservada
---
### RF-PMC-005-005: Configurar Flujo
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-005 |
| **Nombre** | Configurar Flujo |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Datos de entrada:**
- config: object
- retry_on_failure: boolean
- max_retries: number
- timeout_seconds: number
- custom_params: object
**Criterios de aceptación:**
- [ ] Configuración se guarda
- [ ] Valores aplicados en ejecuciones
- [ ] Validación de rangos
---
### RF-PMC-005-006: Ejecutar Flujo Manualmente
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-006 |
| **Nombre** | Ejecutar Flujo Manualmente |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Descripción:**
Disparar ejecución manual de un flujo con datos de prueba.
**Datos de entrada:**
- flow_id: UUID
- test_data: object (opcional)
**Criterios de aceptación:**
- [ ] Ejecución inicia inmediatamente
- [ ] Datos de prueba inyectados
- [ ] Resultado visible al completar
---
## Ejecuciones
### RF-PMC-005-007: Ver Historial de Ejecuciones
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-007 |
| **Nombre** | Ver Historial de Ejecuciones |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Datos de salida:**
- Lista de ejecuciones con:
- Timestamp
- Status
- Duración
- Trigger data (resumen)
**Criterios de aceptación:**
- [ ] Paginación implementada
- [ ] Filtro por status y fecha
- [ ] Click para ver detalle
---
### RF-PMC-005-008: Ver Detalle de Ejecución
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-008 |
| **Nombre** | Ver Detalle de Ejecución |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Datos de salida:**
- Datos del trigger completos
- Output data
- Error message (si falló)
- Duración
- Timestamps
**Criterios de aceptación:**
- [ ] JSON viewer para datos complejos
- [ ] Error stack visible en fallos
---
### RF-PMC-005-009: Cancelar Ejecución en Curso
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-009 |
| **Nombre** | Cancelar Ejecución en Curso |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Precondiciones:**
- Ejecución en status "running"
**Criterios de aceptación:**
- [ ] Ejecución marcada como "cancelled"
- [ ] n8n notificado para cancelar
---
## Webhooks
### RF-PMC-005-010: Crear Endpoint de Webhook
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-010 |
| **Nombre** | Crear Endpoint de Webhook |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Datos de entrada:**
- name: string
- target_flow_id: UUID
**Datos de salida:**
- slug generado
- URL completa
- secret_key generado
**Criterios de aceptación:**
- [ ] URL única generada
- [ ] Secret para validación
- [ ] Endpoint activo inmediatamente
---
### RF-PMC-005-011: Listar Webhooks
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-011 |
| **Nombre** | Listar Webhooks |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Criterios de aceptación:**
- [ ] Lista de endpoints del tenant
- [ ] URL copiable
- [ ] Estado y última llamada visible
---
### RF-PMC-005-012: Eliminar Webhook
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-012 |
| **Nombre** | Eliminar Webhook |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Criterios de aceptación:**
- [ ] Confirmación requerida
- [ ] Endpoint deja de funcionar
- [ ] Registro eliminado
---
### RF-PMC-005-013: Regenerar Secret de Webhook
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-013 |
| **Nombre** | Regenerar Secret de Webhook |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Descripción:**
Generar nuevo secret invalidando el anterior.
**Criterios de aceptación:**
- [ ] Nuevo secret generado
- [ ] Anterior invalidado
- [ ] Integradores deben actualizar
---
### RF-PMC-005-014: Recibir Webhook Externo
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-014 |
| **Nombre** | Recibir Webhook Externo |
| **Prioridad** | P2 |
| **Actor** | Sistema Externo |
**Flujo:**
1. Sistema externo hace POST a /hooks/{tenant_slug}/{webhook_slug}
2. Sistema valida firma HMAC
3. Sistema busca flujo asociado
4. Sistema crea ejecución
5. Sistema responde 202 Accepted
**Criterios de aceptación:**
- [ ] Validación HMAC funciona
- [ ] Payload parseado correctamente
- [ ] Ejecución disparada
---
## Eventos del Sistema
### RF-PMC-005-015: Emitir Eventos Internos
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-015 |
| **Nombre** | Emitir Eventos Internos |
| **Prioridad** | P1 |
| **Actor** | Sistema |
**Descripción:**
El sistema debe emitir eventos cuando ocurren acciones relevantes.
**Eventos a emitir:**
- CRM: client.created, brand.created, product.created
- Projects: campaign.created, campaign.status_changed, campaign.approved
- Generation: job.completed, job.failed
- Assets: asset.approved, all_assets.approved
**Criterios de aceptación:**
- [ ] Eventos emitidos en cada acción
- [ ] Payload incluye datos relevantes
- [ ] Flujos suscritos son notificados
---
### RF-PMC-005-016: Suscribir Flujo a Evento
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-016 |
| **Nombre** | Suscribir Flujo a Evento |
| **Prioridad** | P1 |
| **Actor** | Sistema |
**Descripción:**
Asociar un flujo con un evento específico.
**Criterios de aceptación:**
- [ ] Flujo se activa cuando evento ocurre
- [ ] Datos del evento pasados al flujo
- [ ] Múltiples flujos pueden suscribirse al mismo evento
---
## Notificaciones
### RF-PMC-005-017: Enviar Notificación por Email
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-017 |
| **Nombre** | Enviar Notificación por Email |
| **Prioridad** | P1 |
| **Actor** | Sistema (vía n8n) |
**Descripción:**
Enviar emails como parte de flujos automatizados.
**Datos de entrada:**
- to: string (email)
- subject: string
- body: string (HTML)
- template_id: string (opcional)
**Criterios de aceptación:**
- [ ] Email enviado vía SMTP/SendGrid
- [ ] Templates soportados
- [ ] Variables interpoladas
---
### RF-PMC-005-018: Enviar Notificación a Slack
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-005-018 |
| **Nombre** | Enviar Notificación a Slack |
| **Prioridad** | P3 |
| **Actor** | Sistema (vía n8n) |
**Descripción:**
Enviar mensajes a canales de Slack.
**Datos de entrada:**
- channel: string
- message: string
- blocks: array (opcional, formato Slack)
**Criterios de aceptación:**
- [ ] Mensaje enviado al canal
- [ ] Formato Slack soportado
- [ ] Webhook configurable por tenant
---
## Resumen
| Prioridad | Cantidad |
|-----------|----------|
| P1 | 7 |
| P2 | 7 |
| P3 | 4 |
| **Total** | **18** |
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,511 @@
# Requerimientos Funcionales - PMC-006 Assets
**Módulo:** Assets (DAM)
**Versión:** 1.0.0
**Fecha:** 2025-12-08
---
## Gestión de Assets
### RF-PMC-006-001: Subir Asset
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-001 |
| **Nombre** | Subir Asset |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de entrada:**
- file(s): array[file] (requerido)
- name: string (opcional, usa filename si vacío)
- description: text (opcional)
- tags: array[string] (opcional)
- campaign_id: UUID (opcional)
**Criterios de aceptación:**
- [ ] Upload múltiple funciona
- [ ] Drag & drop soportado
- [ ] Thumbnails generados automáticamente
- [ ] Progreso visible durante upload
---
### RF-PMC-006-002: Ver Asset
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-002 |
| **Nombre** | Ver Asset |
| **Prioridad** | P1 |
| **Actor** | Todos los roles |
**Datos de salida:**
- Preview/visualización del asset
- Metadata completa
- Historial de versiones
- Comentarios
- Estado de aprobación
**Criterios de aceptación:**
- [ ] Lightbox para imágenes
- [ ] Player para videos
- [ ] Viewer para documentos
- [ ] Zoom disponible
---
### RF-PMC-006-003: Editar Metadata de Asset
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-003 |
| **Nombre** | Editar Metadata de Asset |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos editables:**
- name
- description
- tags
- visibility
**Criterios de aceptación:**
- [ ] Cambios guardados correctamente
- [ ] Historial de cambios registrado
---
### RF-PMC-006-004: Eliminar Asset
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-004 |
| **Nombre** | Eliminar Asset |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Restricciones:**
- Assets aprobados no pueden eliminarse (solo admin)
- Soft delete con retención 30 días
**Criterios de aceptación:**
- [ ] Soft delete funciona
- [ ] Asset va a papelera
- [ ] Confirmación requerida
---
### RF-PMC-006-005: Restaurar Asset
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-005 |
| **Nombre** | Restaurar Asset |
| **Prioridad** | P2 |
| **Actor** | Creative, Tenant Admin |
**Descripción:**
Recuperar asset de la papelera.
**Criterios de aceptación:**
- [ ] Asset restaurado a su estado anterior
- [ ] deleted_at se limpia
- [ ] Disponible dentro de 30 días
---
### RF-PMC-006-006: Listar Assets
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-006 |
| **Nombre** | Listar Assets |
| **Prioridad** | P1 |
| **Actor** | Todos los roles |
**Vistas:**
- Grid (thumbnails)
- Lista (tabla con metadata)
**Filtros:**
- type (image, video, document, copy, model)
- status (draft, pending_review, approved, rejected)
- campaign_id
- collection_id
- tags
- date_range
- source (generated, uploaded)
**Criterios de aceptación:**
- [ ] Ambas vistas funcionan
- [ ] Filtros combinables
- [ ] Paginación con scroll infinito o páginas
- [ ] Ordenamiento múltiple
---
### RF-PMC-006-007: Búsqueda de Assets
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-007 |
| **Nombre** | Búsqueda de Assets |
| **Prioridad** | P1 |
| **Actor** | Todos los roles |
**Campos buscables:**
- name
- description
- tags
- prompt (para generados)
**Criterios de aceptación:**
- [ ] Búsqueda de texto funciona
- [ ] Resultados relevantes primero
- [ ] Highlighting de matches
---
## Colecciones
### RF-PMC-006-008: Crear Colección
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-008 |
| **Nombre** | Crear Colección |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Datos de entrada:**
- name: string (requerido)
- description: text (opcional)
- type: enum (manual, smart)
- smart_filters: object (si type=smart)
**Criterios de aceptación:**
- [ ] Colección creada correctamente
- [ ] Smart collection ejecuta filtros
---
### RF-PMC-006-009: Editar Colección
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-009 |
| **Nombre** | Editar Colección |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Criterios de aceptación:**
- [ ] Nombre y descripción editables
- [ ] Filtros de smart collection editables
- [ ] Cover image seleccionable
---
### RF-PMC-006-010: Agregar Assets a Colección
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-010 |
| **Nombre** | Agregar Assets a Colección |
| **Prioridad** | P1 |
| **Actor** | Creative |
**Métodos:**
- Desde asset detail → "Add to collection"
- Desde colección → "Add assets"
- Bulk selection → "Add to collection"
**Criterios de aceptación:**
- [ ] Múltiples métodos funcionan
- [ ] Asset puede estar en múltiples colecciones
- [ ] No duplicados en misma colección
---
### RF-PMC-006-011: Quitar Assets de Colección
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-011 |
| **Nombre** | Quitar Assets de Colección |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Criterios de aceptación:**
- [ ] Asset se desvincula de colección
- [ ] Asset NO se elimina
- [ ] Bulk removal soportado
---
### RF-PMC-006-012: Eliminar Colección
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-012 |
| **Nombre** | Eliminar Colección |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Criterios de aceptación:**
- [ ] Colección eliminada
- [ ] Assets NO se eliminan
- [ ] Confirmación requerida
---
## Versiones
### RF-PMC-006-013: Subir Nueva Versión de Asset
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-013 |
| **Nombre** | Subir Nueva Versión de Asset |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Datos de entrada:**
- asset_id: UUID
- new_file: file
- changes_description: text (opcional)
**Criterios de aceptación:**
- [ ] Nueva versión almacenada
- [ ] Version number incrementado
- [ ] Versión anterior preservada
---
### RF-PMC-006-014: Ver Historial de Versiones
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-014 |
| **Nombre** | Ver Historial de Versiones |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Datos de salida:**
- Lista de versiones con:
- Número de versión
- Fecha
- Usuario
- Descripción de cambios
- Preview
**Criterios de aceptación:**
- [ ] Historial completo visible
- [ ] Click para ver versión específica
- [ ] Opción de restaurar versión anterior
---
### RF-PMC-006-015: Restaurar Versión Anterior
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-015 |
| **Nombre** | Restaurar Versión Anterior |
| **Prioridad** | P3 |
| **Actor** | Creative |
**Descripción:**
Hacer que una versión anterior sea la actual.
**Criterios de aceptación:**
- [ ] Versión seleccionada se convierte en actual
- [ ] Nueva versión creada (copia)
- [ ] Historial preservado
---
## Aprobación
### RF-PMC-006-016: Aprobar Asset
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-016 |
| **Nombre** | Aprobar Asset |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Criterios de aceptación:**
- [ ] Status cambia a "approved"
- [ ] approved_by y approved_at registrados
- [ ] Asset no editable después (excepto metadata)
---
### RF-PMC-006-017: Rechazar Asset
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-017 |
| **Nombre** | Rechazar Asset |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Datos de entrada:**
- feedback: text (requerido)
**Criterios de aceptación:**
- [ ] Status cambia a "rejected"
- [ ] Feedback almacenado
- [ ] Asset puede regenerarse/versionarse
---
## Comentarios
### RF-PMC-006-018: Agregar Comentario a Asset
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-018 |
| **Nombre** | Agregar Comentario a Asset |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Datos de entrada:**
- content: text (requerido)
- position: object (x, y) - opcional, para comentarios en imagen
**Criterios de aceptación:**
- [ ] Comentario se crea
- [ ] Posición en imagen soportada
- [ ] Notificación a equipo
---
### RF-PMC-006-019: Responder Comentario
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-019 |
| **Nombre** | Responder Comentario |
| **Prioridad** | P2 |
| **Actor** | Creative, Tenant Admin |
**Criterios de aceptación:**
- [ ] Respuesta vinculada al comentario padre
- [ ] Thread visible
- [ ] Notificación a participantes
---
### RF-PMC-006-020: Resolver Comentario
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-020 |
| **Nombre** | Resolver Comentario |
| **Prioridad** | P2 |
| **Actor** | Creative |
**Criterios de aceptación:**
- [ ] is_resolved cambia a true
- [ ] Comentario colapsado visualmente
- [ ] Puede des-resolverse
---
## Descargas
### RF-PMC-006-021: Descargar Asset Individual
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-021 |
| **Nombre** | Descargar Asset Individual |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Opciones:**
- Original
- Convertido (formato diferente)
- Tamaño reducido
**Criterios de aceptación:**
- [ ] Descarga inicia correctamente
- [ ] Registro de descarga creado
- [ ] Conversión on-the-fly funciona
---
### RF-PMC-006-022: Descargar Múltiples Assets (ZIP)
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-022 |
| **Nombre** | Descargar Múltiples Assets (ZIP) |
| **Prioridad** | P1 |
| **Actor** | Creative, Tenant Admin |
**Datos de entrada:**
- asset_ids: array[UUID]
- include_metadata: boolean (opcional)
**Criterios de aceptación:**
- [ ] ZIP generado con estructura organizada
- [ ] Progreso visible para ZIPs grandes
- [ ] Metadata en JSON opcional
---
### RF-PMC-006-023: Generar Enlace Temporal
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-023 |
| **Nombre** | Generar Enlace Temporal |
| **Prioridad** | P2 |
| **Actor** | Creative, Tenant Admin |
**Datos de entrada:**
- asset_id: UUID
- expiry_days: number (default: 7, max: 30)
**Criterios de aceptación:**
- [ ] URL única generada
- [ ] Expira después del tiempo configurado
- [ ] No requiere autenticación para descargar
---
### RF-PMC-006-024: Descargar Colección Completa
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-006-024 |
| **Nombre** | Descargar Colección Completa |
| **Prioridad** | P2 |
| **Actor** | Creative, Tenant Admin |
**Criterios de aceptación:**
- [ ] Todos los assets de colección en ZIP
- [ ] Estructura de carpetas preservada
- [ ] Opción de incluir solo aprobados
---
## Resumen
| Prioridad | Cantidad |
|-----------|----------|
| P1 | 13 |
| P2 | 9 |
| P3 | 2 |
| **Total** | **24** |
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,438 @@
# Requerimientos Funcionales - PMC-007 Admin
**Módulo:** Admin
**Versión:** 1.0.0
**Fecha:** 2025-12-08
---
## Gestión de Usuarios
### RF-PMC-007-001: Listar Usuarios
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-001 |
| **Nombre** | Listar Usuarios |
| **Prioridad** | P1 |
| **Actor** | Tenant Admin |
**Datos de salida:**
- Lista de usuarios del tenant
- Status, rol, último login
- Filtros por status y rol
**Criterios de aceptación:**
- [ ] Solo usuarios del tenant
- [ ] Paginación funciona
- [ ] Búsqueda por nombre/email
---
### RF-PMC-007-002: Invitar Usuario
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-002 |
| **Nombre** | Invitar Usuario |
| **Prioridad** | P1 |
| **Actor** | Tenant Admin |
**Datos de entrada:**
- email: string (requerido)
- role_id: UUID (requerido)
- message: text (opcional)
**Flujo:**
1. Admin ingresa email y selecciona rol
2. Sistema verifica email no existe en tenant
3. Sistema crea invitación con token único
4. Sistema envía email con link
5. Invitación expira en 7 días
**Criterios de aceptación:**
- [ ] Email de invitación enviado
- [ ] Token único generado
- [ ] Invitación listada como pendiente
---
### RF-PMC-007-003: Aceptar Invitación
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-003 |
| **Nombre** | Aceptar Invitación |
| **Prioridad** | P1 |
| **Actor** | Usuario invitado |
**Datos de entrada:**
- token: string (del link)
- first_name: string
- last_name: string
- password: string
**Criterios de aceptación:**
- [ ] Usuario creado con rol asignado
- [ ] Password hasheado
- [ ] Invitación marcada como aceptada
- [ ] Usuario puede hacer login
---
### RF-PMC-007-004: Editar Usuario
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-004 |
| **Nombre** | Editar Usuario |
| **Prioridad** | P1 |
| **Actor** | Tenant Admin |
**Datos editables:**
- first_name, last_name
- role_id
- status
**Criterios de aceptación:**
- [ ] Campos se actualizan
- [ ] Cambio de rol aplica inmediatamente
- [ ] Audit log registra cambios
---
### RF-PMC-007-005: Suspender Usuario
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-005 |
| **Nombre** | Suspender Usuario |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Descripción:**
Bloquear acceso de un usuario temporalmente.
**Criterios de aceptación:**
- [ ] Status cambia a "suspended"
- [ ] Sesiones invalidadas
- [ ] Usuario no puede hacer login
- [ ] Admin no puede suspenderse a sí mismo
---
### RF-PMC-007-006: Reactivar Usuario
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-006 |
| **Nombre** | Reactivar Usuario |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Criterios de aceptación:**
- [ ] Status cambia a "active"
- [ ] Usuario puede hacer login
---
### RF-PMC-007-007: Eliminar Usuario
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-007 |
| **Nombre** | Eliminar Usuario |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Criterios de aceptación:**
- [ ] Status cambia a "deactivated"
- [ ] Soft delete
- [ ] Datos preservados (para auditoría)
- [ ] No puede ser el último admin
---
### RF-PMC-007-008: Reenviar Invitación
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-008 |
| **Nombre** | Reenviar Invitación |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Criterios de aceptación:**
- [ ] Nuevo email enviado
- [ ] Token regenerado
- [ ] Expiry extendido
---
## Gestión de Roles
### RF-PMC-007-009: Listar Roles
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-009 |
| **Nombre** | Listar Roles |
| **Prioridad** | P1 |
| **Actor** | Tenant Admin |
**Datos de salida:**
- Roles de sistema
- Roles custom del tenant
- Usuarios por rol
**Criterios de aceptación:**
- [ ] Todos los roles listados
- [ ] Identificación clara de roles de sistema
---
### RF-PMC-007-010: Ver Permisos de Rol
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-010 |
| **Nombre** | Ver Permisos de Rol |
| **Prioridad** | P1 |
| **Actor** | Tenant Admin |
**Datos de salida:**
- Lista de permisos agrupados por módulo
- Checkbox de cada permiso
**Criterios de aceptación:**
- [ ] Permisos organizados por módulo
- [ ] Visual claro de lo que puede/no puede hacer
---
### RF-PMC-007-011: Crear Rol Custom
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-011 |
| **Nombre** | Crear Rol Custom |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Datos de entrada:**
- name: string
- description: text
- permissions: array[string]
**Criterios de aceptación:**
- [ ] Rol creado con is_system=false
- [ ] Permisos asignados
- [ ] Disponible para asignar a usuarios
---
### RF-PMC-007-012: Editar Rol Custom
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-012 |
| **Nombre** | Editar Rol Custom |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Restricciones:**
- Roles de sistema no editables
**Criterios de aceptación:**
- [ ] Permisos actualizables
- [ ] Cambios aplican inmediatamente a usuarios
---
### RF-PMC-007-013: Eliminar Rol Custom
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-013 |
| **Nombre** | Eliminar Rol Custom |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Restricciones:**
- No puede tener usuarios asignados
- Roles de sistema no eliminables
**Criterios de aceptación:**
- [ ] Validación de usuarios previo a eliminar
- [ ] Error si tiene usuarios
---
## Configuración
### RF-PMC-007-014: Ver Configuración del Tenant
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-014 |
| **Nombre** | Ver Configuración del Tenant |
| **Prioridad** | P1 |
| **Actor** | Tenant Admin |
**Secciones:**
- General (nombre, timezone, idioma)
- Branding
- Generación (defaults)
- Integraciones
- Notificaciones
**Criterios de aceptación:**
- [ ] Todas las secciones accesibles
- [ ] Valores actuales mostrados
---
### RF-PMC-007-015: Editar Configuración General
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-015 |
| **Nombre** | Editar Configuración General |
| **Prioridad** | P1 |
| **Actor** | Tenant Admin |
**Datos editables:**
- Nombre del tenant
- Timezone
- Idioma por defecto
- Formato de fecha
**Criterios de aceptación:**
- [ ] Cambios guardados
- [ ] Aplican a nuevos usuarios
- [ ] Usuarios existentes pueden tener preferencia propia
---
### RF-PMC-007-016: Configurar Integraciones
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-016 |
| **Nombre** | Configurar Integraciones |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Integraciones configurables:**
- n8n webhook URL
- Slack webhook
- SMTP custom (opcional)
- CRM externo URL
**Criterios de aceptación:**
- [ ] URLs validadas
- [ ] Test de conexión disponible
- [ ] Secrets seguros
---
## Auditoría
### RF-PMC-007-017: Ver Logs de Auditoría
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-017 |
| **Nombre** | Ver Logs de Auditoría |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Filtros:**
- Usuario
- Acción
- Entidad
- Fecha
**Criterios de aceptación:**
- [ ] Logs listados con paginación
- [ ] Filtros combinables
- [ ] Detalle expandible
---
### RF-PMC-007-018: Exportar Logs de Auditoría
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-018 |
| **Nombre** | Exportar Logs de Auditoría |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Formatos:**
- CSV
- JSON
**Criterios de aceptación:**
- [ ] Filtros aplicados en export
- [ ] Archivo descargable generado
---
## Sistema (Super Admin)
### RF-PMC-007-019: Ver Estado del Sistema
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-019 |
| **Nombre** | Ver Estado del Sistema |
| **Prioridad** | P2 |
| **Actor** | Super Admin |
**Métricas:**
- Estado de servicios (API, ComfyUI, Redis, DB)
- Uso de GPU
- Cola de generación
- Storage total
**Criterios de aceptación:**
- [ ] Dashboard de salud
- [ ] Alertas en problemas
---
### RF-PMC-007-020: Ver Uso por Tenant
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-007-020 |
| **Nombre** | Ver Uso por Tenant |
| **Prioridad** | P2 |
| **Actor** | Super Admin |
**Métricas por tenant:**
- Generaciones
- Storage
- Usuarios activos
- API calls
**Criterios de aceptación:**
- [ ] Comparativa entre tenants
- [ ] Exportable
---
## Resumen
| Prioridad | Cantidad |
|-----------|----------|
| P1 | 8 |
| P2 | 10 |
| P3 | 2 |
| **Total** | **20** |
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,356 @@
# Requerimientos Funcionales - PMC-008 Analytics
**Módulo:** Analytics
**Versión:** 1.0.0
**Fecha:** 2025-12-08
---
## Dashboards
### RF-PMC-008-001: Ver Dashboard Principal
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-001 |
| **Nombre** | Ver Dashboard Principal |
| **Prioridad** | P1 |
| **Actor** | Todos los roles |
**Widgets:**
- Quick stats (campañas activas, assets mes, tasa aprobación)
- Actividad reciente
- Acciones pendientes
**Criterios de aceptación:**
- [ ] Dashboard carga correctamente
- [ ] Datos actualizados
- [ ] Responsive en diferentes pantallas
---
### RF-PMC-008-002: Ver Dashboard de Producción
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-002 |
| **Nombre** | Ver Dashboard de Producción |
| **Prioridad** | P2 |
| **Actor** | Creative, Tenant Admin |
**Widgets:**
- Volumen de generación (gráfico de líneas)
- Estado de cola (gauge)
- Uso de modelos/workflows (pie chart)
- Tasa de error
- Tiempo promedio de procesamiento
**Criterios de aceptación:**
- [ ] Gráficos interactivos
- [ ] Filtro de período funciona
- [ ] Datos en tiempo real para cola
---
### RF-PMC-008-003: Ver Dashboard de Campañas
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-003 |
| **Nombre** | Ver Dashboard de Campañas |
| **Prioridad** | P2 |
| **Actor** | Creative, Analyst, Tenant Admin |
**Widgets:**
- Funnel de campañas por estado
- Tasa de aprobación primera iteración
- Tiempo promedio brief → aprobación
- Assets por campaña
- Top clientes
**Criterios de aceptación:**
- [ ] Métricas calculadas correctamente
- [ ] Drill-down en gráficos
- [ ] Filtro por cliente/período
---
### RF-PMC-008-004: Ver Dashboard de Recursos
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-004 |
| **Nombre** | Ver Dashboard de Recursos |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Widgets:**
- Storage usado vs cuota
- Generaciones mes vs límite
- Distribución de storage por tipo
- Proyección de uso
**Criterios de aceptación:**
- [ ] Progress bars claras
- [ ] Alertas en >80% uso
- [ ] Breakdown por tipo de asset
---
### RF-PMC-008-005: Aplicar Filtros Globales
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-005 |
| **Nombre** | Aplicar Filtros Globales |
| **Prioridad** | P1 |
| **Actor** | Todos los roles |
**Filtros:**
- Período (hoy, semana, mes, custom)
- Cliente
- Usuario
**Criterios de aceptación:**
- [ ] Filtros aplican a todos los widgets
- [ ] Persistencia durante sesión
- [ ] Reset disponible
---
## Reportes
### RF-PMC-008-006: Generar Reporte de Actividad Mensual
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-006 |
| **Nombre** | Generar Reporte de Actividad Mensual |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Contenido:**
- Resumen ejecutivo
- Campañas del período
- Assets generados
- Uso de recursos
- Comparativa con período anterior
**Formatos:**
- PDF
- Excel
**Criterios de aceptación:**
- [ ] Reporte generado correctamente
- [ ] Datos precisos
- [ ] Formato profesional
---
### RF-PMC-008-007: Generar Reporte de Campaña
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-007 |
| **Nombre** | Generar Reporte de Campaña |
| **Prioridad** | P2 |
| **Actor** | Creative, Tenant Admin |
**Datos de entrada:**
- campaign_id: UUID
**Contenido:**
- Datos de campaña y brief
- Assets generados/aprobados
- Timeline de actividad
- Participantes
**Criterios de aceptación:**
- [ ] Reporte específico de campaña
- [ ] Incluye thumbnails de assets
- [ ] PDF descargable
---
### RF-PMC-008-008: Generar Reporte de Cliente
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-008 |
| **Nombre** | Generar Reporte de Cliente |
| **Prioridad** | P3 |
| **Actor** | Analyst, Tenant Admin |
**Datos de entrada:**
- client_id: UUID
- date_range: object
**Contenido:**
- Proyectos y campañas
- Assets entregados
- Histórico de actividad
**Criterios de aceptación:**
- [ ] Filtro de período funciona
- [ ] Exportable en PDF/Excel
---
### RF-PMC-008-009: Programar Reporte Automático
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-009 |
| **Nombre** | Programar Reporte Automático |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Opciones:**
- Frecuencia (semanal, mensual)
- Destinatarios (emails)
- Tipo de reporte
**Criterios de aceptación:**
- [ ] Programación guardada
- [ ] Email enviado automáticamente
- [ ] PDF adjunto
---
### RF-PMC-008-010: Ver Historial de Reportes
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-010 |
| **Nombre** | Ver Historial de Reportes |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Datos de salida:**
- Lista de reportes generados
- Fecha, tipo, generador
- Link de descarga
**Criterios de aceptación:**
- [ ] Reportes listados
- [ ] Descarga disponible (30 días)
- [ ] Filtro por tipo/fecha
---
## Métricas
### RF-PMC-008-011: Consultar Métricas Raw
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-011 |
| **Nombre** | Consultar Métricas Raw |
| **Prioridad** | P2 |
| **Actor** | Tenant Admin |
**Descripción:**
API para consultar métricas agregadas.
**Parámetros:**
- metric_type
- dimensions
- period
- filters
**Criterios de aceptación:**
- [ ] API retorna datos correctos
- [ ] Agregaciones funcionan
- [ ] Paginación para grandes volúmenes
---
### RF-PMC-008-012: Exportar Datos de Métricas
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-012 |
| **Nombre** | Exportar Datos de Métricas |
| **Prioridad** | P3 |
| **Actor** | Tenant Admin |
**Formatos:**
- CSV
- JSON
**Criterios de aceptación:**
- [ ] Filtros aplicados
- [ ] Formato correcto
- [ ] Útil para BI externo
---
## Personalización
### RF-PMC-008-013: Guardar Vista Personalizada
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-013 |
| **Nombre** | Guardar Vista Personalizada |
| **Prioridad** | P3 |
| **Actor** | Creative, Tenant Admin |
**Datos de entrada:**
- name: string
- dashboard: string
- config: object (filtros, widgets visibles)
**Criterios de aceptación:**
- [ ] Vista guardada
- [ ] Cargable posteriormente
- [ ] Por usuario
---
### RF-PMC-008-014: Establecer Vista por Defecto
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-014 |
| **Nombre** | Establecer Vista por Defecto |
| **Prioridad** | P3 |
| **Actor** | Creative, Tenant Admin |
**Descripción:**
Marcar una vista guardada como la que se carga al abrir dashboard.
**Criterios de aceptación:**
- [ ] Vista se carga automáticamente
- [ ] Una sola vista por defecto por dashboard
---
### RF-PMC-008-015: Eliminar Vista Guardada
| Campo | Valor |
|-------|-------|
| **ID** | RF-PMC-008-015 |
| **Nombre** | Eliminar Vista Guardada |
| **Prioridad** | P3 |
| **Actor** | Creative, Tenant Admin |
**Criterios de aceptación:**
- [ ] Vista eliminada
- [ ] Si era default, se usa vista estándar
---
## Resumen
| Prioridad | Cantidad |
|-----------|----------|
| P1 | 2 |
| P2 | 6 |
| P3 | 7 |
| **Total** | **15** |
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,58 @@
# Índice de Requerimientos Funcionales
**Versión:** 1.0.0
**Fecha:** 2025-12-08
---
## Documentos de Requerimientos
| ID | Módulo | Documento | RFs | Estado |
|----|--------|-----------|-----|--------|
| RF-001 | Tenants | [RF-PMC-001-TENANTS.md](./RF-PMC-001-TENANTS.md) | 15 | Definido |
| RF-002 | CRM | [RF-PMC-002-CRM.md](./RF-PMC-002-CRM.md) | 25 | Definido |
| RF-003 | Projects | [RF-PMC-003-PROJECTS.md](./RF-PMC-003-PROJECTS.md) | 22 | Definido |
| RF-004 | Generation | [RF-PMC-004-GENERATION.md](./RF-PMC-004-GENERATION.md) | 28 | Definido |
| RF-005 | Automation | [RF-PMC-005-AUTOMATION.md](./RF-PMC-005-AUTOMATION.md) | 18 | Definido |
| RF-006 | Assets | [RF-PMC-006-ASSETS.md](./RF-PMC-006-ASSETS.md) | 24 | Definido |
| RF-007 | Admin | [RF-PMC-007-ADMIN.md](./RF-PMC-007-ADMIN.md) | 20 | Definido |
| RF-008 | Analytics | [RF-PMC-008-ANALYTICS.md](./RF-PMC-008-ANALYTICS.md) | 15 | Definido |
**Total de Requerimientos Funcionales:** 167
---
## Nomenclatura
```
RF-PMC-XXX-YYY
Donde:
- RF: Requerimiento Funcional
- PMC: Platform Marketing Content
- XXX: ID del módulo (001-008)
- YYY: Número secuencial del requerimiento
```
---
## Prioridades
| Prioridad | Descripción | Criterio |
|-----------|-------------|----------|
| **P1** | Crítico | Bloquea el MVP, sin esto no funciona |
| **P2** | Alto | Necesario para MVP, funcionalidad core |
| **P3** | Medio | Deseable para MVP, mejora UX |
| **P4** | Bajo | Post-MVP, mejoras futuras |
---
## Referencias
- [Definición de Módulos](../02-definicion-modulos/_INDEX.md)
- [Arquitectura Técnica](../00-vision-general/ARQUITECTURA-TECNICA.md)
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,275 @@
# Modelo de Dominio - Platform Marketing Content
**Versión:** 1.0.0
**Fecha:** 2025-12-08
---
## Diagrama de Entidades
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ CORE ENTITIES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Tenant │◄────────│ Plan │ │ User │ │
│ └────┬─────┘ └──────────┘ └────┬─────┘ │
│ │ │ │
│ │ 1:N │ N:1 │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Client │ │ Role │ │
│ └────┬─────┘ └──────────┘ │
│ │ │
│ │ 1:N │
│ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Contact │ │ Brand │◄────────┐ │
│ └──────────┘ └────┬─────┘ │ │
│ │ │ N:N │
│ │ 1:N │ │
│ ▼ │ │
│ ┌──────────┐ ┌────┴─────┐ │
│ │ Product │ │CustomModel│ │
│ └──────────┘ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ PROJECT ENTITIES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ │
│ │ Project │◄──────────────────┐ │
│ └────┬─────┘ │ │
│ │ │ N:1 (Client) │
│ │ 1:N │ │
│ ▼ │ │
│ ┌──────────┐ ┌─────────┴┐ ┌──────────┐ │
│ │ Campaign │─────────│Opportunity│ │ BriefTpl │ │
│ └────┬─────┘ └──────────┘ └──────────┘ │
│ │ │
│ │ 1:N │
│ ▼ │
│ ┌──────────────┐ │
│ │CampaignAsset │──────────┐ │
│ └──────────────┘ │ N:1 │
│ ▼ │
│ ┌──────────┐ │
│ │ Asset │ │
│ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ GENERATION ENTITIES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────┐ ┌────────────────┐ │
│ │GenerationJob │───────────────────│WorkflowTemplate│ │
│ └───────┬────────┘ └────────────────┘ │
│ │ │
│ │ 1:N │
│ ▼ │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ Asset │ │ TextGeneration │ │
│ └───────┬────────┘ └────────────────┘ │
│ │ │
│ │ 1:N │
│ ▼ │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ AssetVersion │ │ AssetComment │ │
│ └────────────────┘ └────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ AUTOMATION ENTITIES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────┐ ┌────────────────┐ │
│ │AutomationFlow │───────────────────│ AutomationRun │ │
│ └───────┬────────┘ └────────────────┘ │
│ │ │
│ │ N:1 │
│ ▼ │
│ ┌────────────────┐ │
│ │WebhookEndpoint │ │
│ └────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ SUPPORT ENTITIES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Collection│ │Invitation│ │ AuditLog │ │ Setting │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Download │ │ Metric │ │ Report │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Entidades por Módulo
### PMC-001: Tenants
| Entidad | Descripción | Relaciones Principales |
|---------|-------------|------------------------|
| **Tenant** | Organización que usa la plataforma | 1:N User, Client, Project, Asset |
| **Plan** | Plan de suscripción con límites | 1:N Tenant |
### PMC-002: CRM
| Entidad | Descripción | Relaciones Principales |
|---------|-------------|------------------------|
| **Client** | Empresa cliente de la agencia | N:1 Tenant, 1:N Contact, Brand, Project |
| **Contact** | Persona de contacto | N:1 Client |
| **Brand** | Marca con identidad visual | N:1 Client, 1:N Product, Campaign |
| **Product** | Producto o servicio | N:1 Brand |
| **Opportunity** | Oportunidad comercial | N:1 Client, Contact |
### PMC-003: Projects
| Entidad | Descripción | Relaciones Principales |
|---------|-------------|------------------------|
| **Project** | Contenedor de campañas | N:1 Client, Tenant, 1:N Campaign |
| **Campaign** | Campaña de marketing con brief | N:1 Project, Brand, 1:N CampaignAsset |
| **CampaignAsset** | Relación campaña-asset con estado | N:1 Campaign, Asset |
### PMC-004: Generation
| Entidad | Descripción | Relaciones Principales |
|---------|-------------|------------------------|
| **GenerationJob** | Tarea de generación | N:1 Tenant, Campaign, WorkflowTemplate |
| **WorkflowTemplate** | Plantilla de workflow ComfyUI | 1:N GenerationJob |
| **CustomModel** | LoRA/Checkpoint personalizado | N:1 Tenant, Brand |
| **TextGeneration** | Generación de texto/copy | N:1 Tenant, GenerationJob |
### PMC-005: Automation
| Entidad | Descripción | Relaciones Principales |
|---------|-------------|------------------------|
| **AutomationFlow** | Definición de flujo automatizado | N:1 Tenant, 1:N AutomationRun |
| **AutomationRun** | Ejecución de un flujo | N:1 AutomationFlow |
| **WebhookEndpoint** | Endpoint para webhooks externos | N:1 Tenant, AutomationFlow |
### PMC-006: Assets
| Entidad | Descripción | Relaciones Principales |
|---------|-------------|------------------------|
| **Asset** | Recurso digital (imagen, video, etc.) | N:1 Tenant, GenerationJob |
| **AssetVersion** | Versión histórica de asset | N:1 Asset |
| **Collection** | Agrupación de assets | N:1 Tenant, N:N Asset |
| **AssetComment** | Comentario sobre asset | N:1 Asset, User |
| **Download** | Registro de descarga | N:1 Asset, User |
### PMC-007: Admin
| Entidad | Descripción | Relaciones Principales |
|---------|-------------|------------------------|
| **User** | Usuario del sistema | N:1 Tenant, Role |
| **Role** | Rol con permisos | 1:N User |
| **Invitation** | Invitación pendiente | N:1 Tenant, Role |
| **AuditLog** | Registro de auditoría | N:1 Tenant, User |
| **Setting** | Configuración del sistema | N:1 Tenant (opcional) |
### PMC-008: Analytics
| Entidad | Descripción | Relaciones Principales |
|---------|-------------|------------------------|
| **Metric** | Dato métrico agregado | N:1 Tenant |
| **Report** | Reporte generado | N:1 Tenant, User |
| **SavedView** | Vista personalizada guardada | N:1 Tenant, User |
---
## Matriz de Relaciones
```
│Ten│Pln│Usr│Rol│Cli│Con│Bra│Pro│Opp│Prj│Cam│CAs│Job│Wfl│Mod│Txt│Flo│Run│Whk│Ast│Ver│Col│Com│Dwn│Inv│Aud│Set│Met│Rep│Viw│
─────────────┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤
Tenant │ │N:1│1:N│ │1:N│ │ │ │ │1:N│ │ │1:N│ │1:N│ │1:N│ │1:N│1:N│ │1:N│ │ │1:N│1:N│1:N│1:N│1:N│1:N│
Plan │1:N│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
User │N:1│ │ │N:1│ │ │ │ │ │1:N│ │ │1:N│ │ │ │ │ │ │1:N│ │ │1:N│1:N│ │1:N│ │ │1:N│1:N│
Role │ │ │1:N│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │1:N│ │ │ │ │ │
Client │N:1│ │ │ │ │1:N│1:N│ │1:N│1:N│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
Contact │ │ │ │ │N:1│ │ │ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
Brand │ │ │ │ │N:1│ │ │1:N│ │ │1:N│ │ │ │N:N│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
Product │ │ │ │ │ │ │N:1│ │ │ │ │ │N:1│ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
Opportunity │ │ │ │ │N:1│N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
Project │N:1│ │N:1│ │N:1│ │ │ │ │ │1:N│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
Campaign │ │ │ │ │ │ │N:1│ │ │N:1│ │1:N│N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
CampaignAsset│ │ │ │ │ │ │ │ │ │ │N:1│ │ │ │ │ │ │ │ │N:1│ │ │ │ │ │ │ │ │ │ │
GenJob │N:1│ │N:1│ │ │ │ │N:1│ │ │N:1│ │ │N:1│ │1:N│ │ │ │1:N│ │ │ │ │ │ │ │ │ │ │
Workflow │ │ │ │ │ │ │ │ │ │ │ │ │1:N│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
CustomModel │N:1│ │ │ │ │ │N:N│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
TextGen │N:1│ │ │ │ │ │ │ │ │ │N:1│ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
AutoFlow │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │1:N│1:N│ │ │ │ │ │ │ │ │ │ │ │
AutoRun │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │
Webhook │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │
Asset │N:1│ │N:1│ │ │ │ │ │ │ │ │N:1│N:1│ │ │ │ │ │ │ │1:N│N:N│1:N│1:N│ │ │ │ │ │ │
AssetVer │ │ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │N:1│ │ │ │ │ │ │ │ │ │ │
Collection │N:1│ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │N:N│ │ │ │ │ │ │ │ │ │ │
AssetComment │ │ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │N:1│ │ │1:N│ │ │ │ │ │ │ │
Download │N:1│ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │N:1│ │N:1│ │ │ │ │ │ │ │ │
Invitation │N:1│ │N:1│N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
AuditLog │N:1│ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
Setting │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
Metric │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
Report │N:1│ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
SavedView │N:1│ │N:1│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
```
---
## Agregados (DDD)
### Aggregate: Tenant
- **Root:** Tenant
- **Entities:** Plan (referencia)
- **Value Objects:** Settings, Branding, Limits
### Aggregate: Client
- **Root:** Client
- **Entities:** Contact, Brand, Product
- **Value Objects:** Identity (en Brand)
### Aggregate: Project
- **Root:** Project
- **Entities:** Campaign, CampaignAsset
- **Value Objects:** Brief (en Campaign)
### Aggregate: Asset
- **Root:** Asset
- **Entities:** AssetVersion, AssetComment
- **Value Objects:** Metadata, Dimensions
### Aggregate: GenerationJob
- **Root:** GenerationJob
- **Entities:** (outputs referenciados)
- **Value Objects:** InputParams, Progress
### Aggregate: User
- **Root:** User
- **Entities:** (Role como referencia)
- **Value Objects:** Preferences
---
## Referencias
- [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md)
- [Definición de Módulos](../02-definicion-modulos/_INDEX.md)
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,188 @@
# Contexto de Proyecto: Platform Marketing Content
**Versi贸n:** 1.0.0
**Fecha:** 2025-12-08
**Nivel SIMCO:** NIVEL_2B (Proyecto independiente)
---
## Identificaci贸n del Proyecto
```yaml
Proyecto: platform_marketing_content
Alias: PMC
Tipo: SaaS Platform
Dominio: Marketing Digital / Generaci贸n de Contenido IA
Estado: An谩lisis y Documentaci贸n
```
---
## Descripci贸n
**Platform Marketing Content (PMC)** es una plataforma SaaS para agencias de publicidad que combina:
1. **Motor de Generaci贸n de Contenido con IA** - Im谩genes y copys autom谩ticos
2. **CRM Integrado** - Gesti贸n de clientes, marcas y campa帽as
3. **Automatizaci贸n Creativa** - Flujos desde brief hasta entrega
4. **DAM** - Biblioteca de activos digitales
---
## Stack Tecnol贸gico
```yaml
Backend:
- NestJS + TypeScript
- PostgreSQL 15+
- Redis
- Bull/BullMQ
Frontend:
- React 18 + Vite
- TailwindCSS
- Shadcn/UI
Motor IA:
- ComfyUI
- Stable Diffusion XL
- ComfyDeploy
Automatizaci贸n:
- n8n
Almacenamiento:
- S3/MinIO
```
---
## Estructura de Documentaci贸n
```
projects/platform_marketing_content/
鉁斺攢鉁? docs/
鉁? 鉁斺攢鉁? 00-vision-general/ # Visi贸n, arquitectura, glosario
鉁? 鉁斺攢鉁? 01-analisis-referencias/ # Investigaci贸n, benchmarks
鉁? 鉁斺攢鉁? 02-definicion-modulos/ # Especificaciones por m贸dulo
鉁? 鉁斺攢鉁? 03-requerimientos/ # Requerimientos funcionales
鉁? 鉁斺攢鉁? 04-modelado/ # Modelos de dominio, DB design
鉁? 鉁斺攢鉁? 05-user-stories/ # Historias de usuario
鉁? 鉁斺攢鉁? 95-guias-desarrollo/ # Gu铆as y convenciones
鉁? 鉁斺攢鉁? 97-adr/ # Decisiones arquitect贸nicas
鉁?
鉁斺攢鉁? orchestration/
鉁? 鉁斺攢鉁? 00-guidelines/ # Contexto, herencias
鉁? 鉁斺攢鉁? inventarios/ # Inventarios de implementaci贸n
鉁? 鉁斺攢鉁? trazas/ # Trazas de tareas
鉁?
鉁斺攢鉁? apps/
鉁斺攢鉁? backend/ # C贸digo NestJS
鉁斺攢鉁? frontend/ # C贸digo React
鉁斺攢鉁? comfyui/ # Workflows ComfyUI
```
---
## M贸dulos Funcionales
| ID | M贸dulo | Descripci贸n | Prioridad |
|----|--------|-------------|-----------|
| PMC-001 | Tenants | Arquitectura multi-tenant | Alta |
| PMC-002 | CRM | Clientes, marcas, productos | Alta |
| PMC-003 | Projects | Proyectos y campa帽as | Alta |
| PMC-004 | Generation | Motor de generaci贸n IA | Alta |
| PMC-005 | Automation | Flujos automatizados | Media |
| PMC-006 | Assets | DAM - biblioteca de activos | Alta |
| PMC-007 | Admin | Administraci贸n SaaS | Media |
| PMC-008 | Analytics | Reportes y dashboards | Baja |
---
## Aliases del Proyecto
```yaml
# Documentaci贸n
@PMC_DOCS: projects/platform_marketing_content/docs/
@PMC_VISION: projects/platform_marketing_content/docs/00-vision-general/
@PMC_MODULES: projects/platform_marketing_content/docs/02-definicion-modulos/
@PMC_REQS: projects/platform_marketing_content/docs/03-requerimientos/
@PMC_ADR: projects/platform_marketing_content/docs/97-adr/
# Orchestration
@PMC_ORCH: projects/platform_marketing_content/orchestration/
@PMC_INVENTORY: projects/platform_marketing_content/orchestration/inventarios/
@PMC_TRAZA: projects/platform_marketing_content/orchestration/trazas/
# C贸digo
@PMC_BACKEND: projects/platform_marketing_content/apps/backend/
@PMC_FRONTEND: projects/platform_marketing_content/apps/frontend/
```
---
## Dependencias del Cat谩logo Core
Funcionalidades reutilizables del cat谩logo:
```yaml
Requeridas:
- @CATALOG_AUTH: Autenticaci贸n JWT + OAuth
- @CATALOG_SESSION: Gesti贸n de sesiones
- @CATALOG_TENANT: Multi-tenancy (adaptar)
- @CATALOG_NOTIFY: Notificaciones
Opcionales:
- @CATALOG_RATELIMIT: Rate limiting
- @CATALOG_FLAGS: Feature flags
```
---
## Roadmap de Fases
```yaml
Fase 1 - MVP Core (Semanas 1-8):
- Arquitectura base
- CRM b谩sico
- Motor de generaci贸n (2-3 workflows)
- DAM b谩sico
- Admin usuarios
Fase 2 - Personalizaci贸n (Semanas 9-14):
- LoRAs por marca
- Avatares consistentes
- Integraci贸n CRM鈫扜eneraci贸n
Fase 3 - Contenido Enriquecido (Semanas 15-22):
- GIFs/cinemagraphs
- Video b谩sico
- Portal cliente
Fase 4 - Multi-tenant Comercial (Semanas 23+):
- SaaS p煤blico
- Planes de suscripci贸n
```
---
## Contactos y Responsables
```yaml
Product Owner: [Por definir]
Tech Lead: [Por definir]
Requirements Analyst: Agente IA
```
---
## Referencias
- [VISION-GENERAL.md](../../docs/00-vision-general/VISION-GENERAL.md)
- [ARQUITECTURA-TECNICA.md](../../docs/00-vision-general/ARQUITECTURA-TECNICA.md)
- [GLOSARIO.md](../../docs/00-vision-general/GLOSARIO.md)
---
**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,161 @@
# Próxima Acción - Platform Marketing Content
**Fecha:** 2025-12-08
**Estado Actual:** Documentación Completada
---
## Resumen de Progreso
### Completado
- [x] Visión general del proyecto consolidada
- [x] Glosario de términos del dominio
- [x] Arquitectura técnica detallada
- [x] Definición de 8 módulos funcionales
- [x] Contexto de proyecto para orchestration
- [x] Inventario maestro (MASTER_INVENTORY.yml)
### Estructura de Documentación Creada
```
projects/platform_marketing_content/
├── docs/
│ ├── 00-vision-general/
│ │ ├── VISION-GENERAL.md ✅
│ │ ├── ARQUITECTURA-TECNICA.md ✅
│ │ ├── GLOSARIO.md ✅
│ │ └── MVP_Plataforma_SaaS...md (original)
│ ├── 01-analisis-referencias/ (vacío)
│ ├── 02-definicion-modulos/
│ │ ├── _INDEX.md ✅
│ │ ├── PMC-001-TENANTS.md ✅
│ │ ├── PMC-002-CRM.md ✅
│ │ ├── PMC-003-PROJECTS.md ✅
│ │ ├── PMC-004-GENERATION.md ✅
│ │ ├── PMC-005-AUTOMATION.md ✅
│ │ ├── PMC-006-ASSETS.md ✅
│ │ ├── PMC-007-ADMIN.md ✅
│ │ └── PMC-008-ANALYTICS.md ✅
│ ├── 03-requerimientos/ (pendiente)
│ ├── 04-modelado/ (pendiente)
│ ├── 05-user-stories/ (pendiente)
│ ├── 95-guias-desarrollo/ (pendiente)
│ └── 97-adr/ (pendiente)
└── orchestration/
├── 00-guidelines/
│ └── CONTEXTO-PROYECTO.md ✅
├── inventarios/
│ └── MASTER_INVENTORY.yml ✅
└── trazas/ (vacío)
```
---
## Próximas Acciones Sugeridas
### Opción A: Continuar Documentación (Requirements-Analyst)
```yaml
Prioridad: Alta
Agente: Requirements-Analyst
Tareas:
1. Crear requerimientos funcionales detallados:
- docs/03-requerimientos/RF-PMC-001-TENANTS.md
- docs/03-requerimientos/RF-PMC-002-CRM.md
- ... (por cada módulo)
2. Crear modelo de datos consolidado:
- docs/04-modelado/MODELO-DOMINIO.md
- docs/04-modelado/ESQUEMA-BD.md
3. Crear user stories para MVP:
- docs/05-user-stories/EPIC-001-SETUP.md
- docs/05-user-stories/EPIC-002-CRM.md
- ...
Estimación: 4-6 horas de trabajo de agente
```
### Opción B: Iniciar Implementación (Feature-Developer)
```yaml
Prioridad: Alta
Agente: Feature-Developer
Prerequisitos:
- Documentación actual es suficiente para MVP
- Usar docs/02-definicion-modulos/* como referencia
Tareas:
1. Setup del proyecto backend:
- Crear proyecto NestJS
- Configurar TypeORM + PostgreSQL
- Implementar estructura de módulos
2. Implementar PMC-001-TENANTS:
- Entidades: Tenant, Plan
- RLS para multi-tenancy
- Endpoints básicos
3. Implementar PMC-007-ADMIN (parcial):
- User, Role, permisos
- Autenticación JWT
Estimación: 16-24 horas para setup + 2 módulos base
```
### Opción C: Revisar y Validar (Documentation-Validator)
```yaml
Prioridad: Media
Agente: Documentation-Validator
Tareas:
1. Validar consistencia entre documentos
2. Verificar que todas las entidades estén definidas
3. Revisar endpoints duplicados o faltantes
4. Generar checklist de validación
Estimación: 2-3 horas
```
---
## Recomendación
Para avanzar de manera eficiente, se sugiere:
1. **Si se quiere documentación exhaustiva**: Continuar con Opción A
2. **Si se quiere iniciar desarrollo pronto**: Ir con Opción B usando la documentación actual
3. **Enfoque híbrido**: Ejecutar Opción B mientras otro agente avanza en Opción A
La documentación actual (8 módulos definidos con entidades, funcionalidades, APIs) es suficiente para comenzar implementación del MVP.
---
## Comandos de Activación
```bash
# Para continuar documentación
@Requirements-Analyst continuar con requerimientos funcionales para PMC
# Para iniciar implementación
@Feature-Developer setup proyecto backend PMC con módulos Tenants y Admin
# Para validación
@Documentation-Validator validar documentación de PMC
```
---
## Notas Adicionales
- El proyecto usa stack NestJS + React + ComfyUI
- Priorizar módulos: PMC-001, PMC-007, PMC-002, PMC-006 (en ese orden)
- La integración con ComfyUI (PMC-004) requiere servidor GPU configurado
- n8n (PMC-005) puede ejecutarse en contenedor Docker local
---
**Generado por:** Requirements-Analyst
**Fecha:** 2025-12-08

View File

@ -0,0 +1,339 @@
# MASTER_INVENTORY.yml - Platform Marketing Content
# Inventario maestro de implementación
# Versión: 1.0.0
# Fecha: 2025-12-08
project:
name: Platform Marketing Content
alias: PMC
nivel_simco: NIVEL_2B
status: documentacion
# =============================================================================
# MÓDULOS
# =============================================================================
modules:
PMC-001-TENANTS:
name: Tenants
priority: alta
status: definido
doc_path: docs/02-definicion-modulos/PMC-001-TENANTS.md
dependencies:
catalog:
- "@CATALOG_TENANT"
entities:
- Tenant
- Plan
endpoints_count: 8
features_count: 10
PMC-002-CRM:
name: CRM
priority: alta
status: definido
doc_path: docs/02-definicion-modulos/PMC-002-CRM.md
dependencies:
modules:
- PMC-001-TENANTS
- PMC-003-PROJECTS
- PMC-004-GENERATION
- PMC-006-ASSETS
entities:
- Client
- Contact
- Brand
- Product
- Opportunity
endpoints_count: 25
features_count: 18
PMC-003-PROJECTS:
name: Projects
priority: alta
status: definido
doc_path: docs/02-definicion-modulos/PMC-003-PROJECTS.md
dependencies:
modules:
- PMC-001-TENANTS
- PMC-002-CRM
- PMC-004-GENERATION
- PMC-006-ASSETS
entities:
- Project
- Campaign
- CampaignAsset
endpoints_count: 20
features_count: 16
PMC-004-GENERATION:
name: Generation (Motor IA)
priority: alta
status: definido
doc_path: docs/02-definicion-modulos/PMC-004-GENERATION.md
dependencies:
modules:
- PMC-001-TENANTS
- PMC-002-CRM
- PMC-003-PROJECTS
- PMC-006-ASSETS
catalog:
- "@CATALOG_RATELIMIT"
external:
- ComfyUI
- OpenAI/Claude API
- Redis
- S3/MinIO
entities:
- GenerationJob
- WorkflowTemplate
- CustomModel
- TextGeneration
endpoints_count: 18
features_count: 20
workflows_predefinidos:
- product_photo_synthetic
- social_media_post
- ad_variations
- virtual_avatar
PMC-005-AUTOMATION:
name: Automation
priority: media
status: definido
doc_path: docs/02-definicion-modulos/PMC-005-AUTOMATION.md
dependencies:
modules:
- PMC-001-TENANTS
- PMC-002-CRM
- PMC-003-PROJECTS
- PMC-004-GENERATION
- PMC-006-ASSETS
external:
- n8n
- SMTP/SendGrid
entities:
- AutomationFlow
- AutomationRun
- WebhookEndpoint
endpoints_count: 15
features_count: 12
flows_predefinidos:
- product_asset_kit
- campaign_initial_batch
- campaign_delivery_prep
- job_failure_handler
PMC-006-ASSETS:
name: Assets (DAM)
priority: alta
status: definido
doc_path: docs/02-definicion-modulos/PMC-006-ASSETS.md
dependencies:
modules:
- PMC-001-TENANTS
- PMC-003-PROJECTS
- PMC-004-GENERATION
external:
- S3/MinIO
- Sharp
entities:
- Asset
- AssetVersion
- Collection
- AssetComment
- Download
endpoints_count: 25
features_count: 18
PMC-007-ADMIN:
name: Admin
priority: media
status: definido
doc_path: docs/02-definicion-modulos/PMC-007-ADMIN.md
dependencies:
modules:
- PMC-001-TENANTS
catalog:
- "@CATALOG_AUTH"
- "@CATALOG_SESSION"
entities:
- User
- Role
- Invitation
- AuditLog
- Setting
endpoints_count: 20
features_count: 14
roles_sistema:
- super_admin
- tenant_admin
- creative
- analyst
- viewer
- client_portal
PMC-008-ANALYTICS:
name: Analytics
priority: baja
status: definido
doc_path: docs/02-definicion-modulos/PMC-008-ANALYTICS.md
dependencies:
modules:
- PMC-001-TENANTS
- PMC-003-PROJECTS
- PMC-004-GENERATION
- PMC-006-ASSETS
external:
- Redis
entities:
- Metric
- Report
- SavedView
endpoints_count: 12
features_count: 10
dashboards:
- home
- production
- campaigns
- resources
# =============================================================================
# STACK TECNOLÓGICO
# =============================================================================
tech_stack:
backend:
framework: NestJS
language: TypeScript
database: PostgreSQL 15+
cache: Redis
queue: Bull/BullMQ
orm: TypeORM
frontend:
framework: React 18
bundler: Vite
styling: TailwindCSS
components: Shadcn/UI
state: Zustand o React Query
ia_engine:
image_generation: ComfyUI + SDXL
text_generation: OpenAI API / Claude API
deployment: ComfyDeploy
automation:
orchestrator: n8n
storage:
files: S3/MinIO
infrastructure:
containers: Docker + Docker Compose
gpu: NVIDIA (12-24GB VRAM)
# =============================================================================
# CATÁLOGO DE DEPENDENCIAS
# =============================================================================
catalog_dependencies:
required:
- id: "@CATALOG_AUTH"
description: Autenticación JWT + OAuth
module: PMC-007-ADMIN
- id: "@CATALOG_SESSION"
description: Gestión de sesiones
module: PMC-007-ADMIN
- id: "@CATALOG_TENANT"
description: Multi-tenancy (adaptar)
module: PMC-001-TENANTS
optional:
- id: "@CATALOG_RATELIMIT"
description: Rate limiting
module: PMC-004-GENERATION
- id: "@CATALOG_NOTIFY"
description: Notificaciones
module: PMC-005-AUTOMATION
# =============================================================================
# FASES DE IMPLEMENTACIÓN
# =============================================================================
roadmap:
fase_1_mvp_core:
duration: "Semanas 1-8"
modules:
- PMC-001-TENANTS
- PMC-007-ADMIN
- PMC-002-CRM
- PMC-006-ASSETS
- PMC-003-PROJECTS
- PMC-004-GENERATION
deliverables:
- Arquitectura base multi-tenant
- Autenticación y usuarios
- CRM básico (clientes, marcas, productos)
- DAM básico
- Campañas con brief
- Motor de generación (2-3 workflows)
fase_2_personalizacion:
duration: "Semanas 9-14"
modules:
- PMC-004-GENERATION (ampliación)
- PMC-005-AUTOMATION
deliverables:
- Entrenamiento de LoRAs
- Avatares consistentes
- Flujos automatizados CRM → Generation
fase_3_contenido_enriquecido:
duration: "Semanas 15-22"
modules:
- PMC-004-GENERATION (video)
- PMC-008-ANALYTICS
deliverables:
- GIFs/cinemagraphs
- Video básico
- Dashboards y reportes
- Portal cliente
fase_4_saas_comercial:
duration: "Semanas 23+"
modules:
- PMC-001-TENANTS (planes)
- PMC-007-ADMIN (billing)
deliverables:
- SaaS público
- Planes de suscripción
- Onboarding automático
# =============================================================================
# MÉTRICAS
# =============================================================================
metrics:
total_modules: 8
total_entities: 28
total_endpoints: ~143
total_features: 118
features_priority:
alta: 64
media: 41
baja: 13
documentation_status:
vision_general: completado
glosario: completado
arquitectura: completado
modulos: completado
requerimientos: pendiente
user_stories: pendiente
# =============================================================================
# METADATOS
# =============================================================================
metadata:
created_by: Requirements-Analyst
created_at: "2025-12-08"
last_updated: "2025-12-08"
version: "1.0.0"