template-saas/docs/02-especificaciones/ET-SAAS-020-commissions.md
Adrian Flores Cortes 2825b3d5fd
Some checks are pending
CI / Backend CI (push) Waiting to run
CI / Frontend CI (push) Waiting to run
CI / Security Scan (push) Waiting to run
CI / CI Summary (push) Blocked by required conditions
docs: Add ET-SAAS-018 to ET-SAAS-022 technical specifications
- ET-SAAS-018-sales.md: Sales pipeline/CRM module
- ET-SAAS-019-portfolio.md: Product catalog module
- ET-SAAS-020-commissions.md: Commissions system
- ET-SAAS-021-mlm.md: Multi-Level Marketing module
- ET-SAAS-022-goals.md: Goals and objectives system
- Update _MAP.md with all 9 specifications

All specs document:
- Data model (DDL, enums, tables)
- Backend architecture (endpoints, services)
- Security (RLS, RBAC permissions)
- Frontend status and hooks
- Module integrations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 13:14:48 -06:00

376 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: "ET-SAAS-020"
title: "Especificacion Tecnica Commissions"
type: "TechnicalSpec"
status: "Implemented"
priority: "P1"
module: "commissions"
version: "1.0.0"
created_date: "2026-02-03"
updated_date: "2026-02-03"
story_points: 8
---
# ET-SAAS-020: Especificacion Tecnica - Sistema de Comisiones
## Metadata
- **Codigo:** ET-SAAS-020
- **Modulo:** Commissions
- **Version:** 1.0.0
- **Estado:** Implementado
- **Fecha:** 2026-02-03
- **Basado en:** SAAS-020
---
## 1. Resumen Ejecutivo
### 1.1 Estado Actual
Sistema de comisiones completamente implementado.
| Capacidad | Estado | Notas |
|-----------|--------|-------|
| Schemes | SI | Esquemas configurables |
| Assignments | SI | Asignacion usuario-esquema |
| Entries | SI | Entradas de comision |
| Periods | SI | Periodos de pago |
| Dashboard | SI | Metricas y reportes |
| RLS | SI | Row Level Security |
### 1.2 Funcionalidades v1.0
Sistema completo con:
- **4 Entidades**: Schemes, Assignments, Entries, Periods
- **3 Tipos de esquema**: percentage, fixed, tiered
- **Workflow completo**: pending → approved → paid
- **Periodos de pago**: Ciclos configurables
- **Dashboard**: Metricas por usuario, periodo, esquema
---
## 2. Modelo de Datos
### 2.1 Schema: commissions
#### Tabla: schemes
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| id | UUID | PK |
| tenant_id | UUID | FK tenants |
| name | VARCHAR(100) | Nombre del esquema |
| description | TEXT | Descripcion |
| type | ENUM | percentage, fixed, tiered |
| rate | DECIMAL(5,2) | Tasa porcentual (0-100) |
| fixed_amount | DECIMAL(15,2) | Monto fijo |
| tiers | JSONB | [{from, to, rate}] |
| applies_to | ENUM | all, products, categories |
| product_ids | UUID[] | IDs de productos |
| category_ids | UUID[] | IDs de categorias |
| min_amount | DECIMAL(15,2) | Monto minimo para calificar |
| max_amount | DECIMAL(15,2) | Cap maximo por comision |
| is_active | BOOLEAN | Estado |
**Ejemplo tiers:**
```json
[
{"from": 0, "to": 1000, "rate": 5},
{"from": 1001, "to": 5000, "rate": 7},
{"from": 5001, "to": null, "rate": 10}
]
```
#### Tabla: assignments
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| id | UUID | PK |
| tenant_id | UUID | FK tenants |
| user_id | UUID | FK users |
| scheme_id | UUID | FK schemes |
| starts_at | TIMESTAMPTZ | Inicio de vigencia |
| ends_at | TIMESTAMPTZ | Fin de vigencia (null = sin fin) |
| custom_rate | DECIMAL(5,2) | Override de tasa para este usuario |
| is_active | BOOLEAN | Estado |
#### Tabla: entries
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| id | UUID | PK |
| tenant_id | UUID | FK tenants |
| user_id | UUID | FK users (quien gana) |
| scheme_id | UUID | FK schemes |
| assignment_id | UUID | FK assignments |
| reference_type | VARCHAR(50) | sale, opportunity, order |
| reference_id | UUID | ID del registro origen |
| base_amount | DECIMAL(15,2) | Monto base de la venta |
| rate_applied | DECIMAL(5,2) | Tasa aplicada |
| commission_amount | DECIMAL(15,2) | Comision calculada |
| currency | VARCHAR(3) | Moneda |
| status | ENUM | pending, approved, rejected, paid, cancelled |
| period_id | UUID | FK periods |
| paid_at | TIMESTAMPTZ | Fecha de pago |
| payment_reference | VARCHAR(255) | Referencia externa |
| notes | TEXT | Notas |
| metadata | JSONB | Datos adicionales |
| approved_by | UUID | FK users |
| approved_at | TIMESTAMPTZ | Fecha aprobacion |
#### Tabla: periods
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| id | UUID | PK |
| tenant_id | UUID | FK tenants |
| name | VARCHAR(100) | "January 2026" |
| starts_at | TIMESTAMPTZ | Inicio del periodo |
| ends_at | TIMESTAMPTZ | Fin del periodo |
| total_entries | INT | Total de entradas |
| total_amount | DECIMAL(15,2) | Monto total |
| currency | VARCHAR(3) | Moneda |
| status | ENUM | open, closed, processing, paid |
| closed_at | TIMESTAMPTZ | Fecha cierre |
| closed_by | UUID | FK users |
| paid_at | TIMESTAMPTZ | Fecha pago |
| paid_by | UUID | FK users |
| payment_reference | VARCHAR(255) | Referencia |
| payment_notes | TEXT | Notas de pago |
### 2.2 Enums
```sql
commissions.scheme_type: percentage, fixed, tiered
commissions.applies_to: all, products, categories
commissions.entry_status: pending, approved, rejected, paid, cancelled
commissions.period_status: open, closed, processing, paid
```
---
## 3. Arquitectura Backend
### 3.1 Estructura de Archivos
```
backend/src/modules/commissions/
├── commissions.module.ts
├── controllers/
│ ├── schemes.controller.ts
│ ├── assignments.controller.ts
│ ├── entries.controller.ts
│ ├── periods.controller.ts
│ └── dashboard.controller.ts
├── services/
│ ├── schemes.service.ts
│ ├── assignments.service.ts
│ ├── entries.service.ts
│ ├── periods.service.ts
│ ├── calculation.service.ts
│ └── dashboard.service.ts
├── entities/
│ ├── scheme.entity.ts
│ ├── assignment.entity.ts
│ ├── entry.entity.ts
│ └── period.entity.ts
└── dto/
```
### 3.2 Endpoints API
#### Schemes
| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| GET | /commissions/schemes | Listar esquemas |
| GET | /commissions/schemes/active | Solo activos |
| GET | /commissions/schemes/:id | Obtener |
| POST | /commissions/schemes | Crear |
| PUT | /commissions/schemes/:id | Actualizar |
| DELETE | /commissions/schemes/:id | Eliminar |
| POST | /commissions/schemes/:id/duplicate | Duplicar |
| POST | /commissions/schemes/:id/toggle | Activar/desactivar |
#### Assignments
| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| GET | /commissions/assignments | Listar |
| GET | /commissions/assignments/user/:userId | Por usuario |
| GET | /commissions/assignments/user/:userId/active | Esquema activo |
| GET | /commissions/assignments/scheme/:schemeId | Por esquema |
| POST | /commissions/assignments | Asignar |
| PUT | /commissions/assignments/:id | Actualizar |
| DELETE | /commissions/assignments/:id | Remover |
| POST | /commissions/assignments/:id/deactivate | Desactivar |
#### Entries
| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| GET | /commissions/entries | Listar |
| GET | /commissions/entries/pending | Pendientes |
| GET | /commissions/entries/:id | Obtener |
| GET | /commissions/entries/user/:userId | Por usuario |
| GET | /commissions/entries/period/:periodId | Por periodo |
| POST | /commissions/entries/calculate | Calcular comision |
| POST | /commissions/entries/simulate | Simular (sin guardar) |
| PATCH | /commissions/entries/:id/status | Cambiar status |
| POST | /commissions/entries/bulk-approve | Aprobar multiples |
| POST | /commissions/entries/bulk-reject | Rechazar multiples |
#### Periods
| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| GET | /commissions/periods | Listar |
| GET | /commissions/periods/open | Periodo abierto |
| GET | /commissions/periods/:id | Obtener |
| GET | /commissions/periods/:id/summary | Resumen |
| POST | /commissions/periods | Crear |
| POST | /commissions/periods/:id/close | Cerrar |
| POST | /commissions/periods/:id/reopen | Reabrir |
| POST | /commissions/periods/:id/mark-paid | Marcar pagado |
#### Dashboard
| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| GET | /commissions/dashboard | Resumen general |
| GET | /commissions/dashboard/by-user | Por usuario |
| GET | /commissions/dashboard/by-period | Por periodo |
| GET | /commissions/dashboard/top-earners | Top ganadores |
| GET | /commissions/dashboard/me | Mis ganancias |
| GET | /commissions/dashboard/user/:userId | Ganancias de usuario |
| GET | /commissions/dashboard/scheme/:schemeId | Performance de esquema |
---
## 4. Logica de Calculo
### 4.1 Algoritmo de Calculo
```typescript
async calculateCommission(dto: CalculateCommissionDto): Promise<CommissionEntry> {
// 1. Obtener asignacion activa del usuario
const assignment = await this.getActiveAssignment(dto.userId);
// 2. Obtener esquema
const scheme = await this.getScheme(assignment.schemeId);
// 3. Verificar monto minimo
if (dto.amount < scheme.minAmount) {
throw new Error('Amount below minimum threshold');
}
// 4. Calcular tasa segun tipo
let rate: number;
switch (scheme.type) {
case 'percentage':
rate = assignment.customRate ?? scheme.rate;
break;
case 'fixed':
return this.createEntry({
...dto,
rateApplied: 0,
commissionAmount: scheme.fixedAmount
});
case 'tiered':
rate = this.getTieredRate(scheme.tiers, dto.amount);
break;
}
// 5. Calcular comision
let commissionAmount = dto.amount * (rate / 100);
// 6. Aplicar cap si existe
if (scheme.maxAmount && commissionAmount > scheme.maxAmount) {
commissionAmount = scheme.maxAmount;
}
// 7. Crear entrada
return this.createEntry({
...dto,
schemeId: scheme.id,
assignmentId: assignment.id,
rateApplied: rate,
commissionAmount
});
}
```
### 4.2 Ejemplo Tiered
```
Tiers: [{from:0, to:1000, rate:5}, {from:1001, to:5000, rate:7}, {from:5001, to:null, rate:10}]
Venta: $3,500
Calculo:
- Primeros $1,000: $1,000 × 5% = $50
- Siguientes $2,500: $2,500 × 7% = $175
- Total comision: $225
```
---
## 5. Frontend
### 5.1 Paginas
| Ruta | Componente | Estado |
|------|------------|--------|
| /dashboard/commissions | CommissionsDashboard | Pendiente |
| /dashboard/commissions/schemes | SchemesPage | Pendiente |
| /dashboard/commissions/entries | EntriesPage | Pendiente |
| /dashboard/commissions/periods | PeriodsPage | Pendiente |
| /dashboard/commissions/my-earnings | MyEarningsPage | Pendiente |
### 5.2 Hooks
```typescript
// frontend/src/hooks/useCommissions.ts
useSchemes, useScheme, useCreateScheme, useUpdateScheme, useDeleteScheme
useAssignments, useUserAssignments, useCreateAssignment
useEntries, usePendingEntries, useCalculateCommission, useUpdateEntryStatus
usePeriods, useOpenPeriod, useClosePeriod, useMarkPeriodPaid
useCommissionsDashboard, useMyEarnings, useTopEarners
```
---
## 6. Seguridad
### 6.1 RLS
```sql
CREATE POLICY schemes_tenant_isolation ON commissions.schemes
USING (tenant_id = current_setting('app.tenant_id')::uuid);
CREATE POLICY entries_tenant_isolation ON commissions.entries
USING (tenant_id = current_setting('app.tenant_id')::uuid);
```
### 6.2 Permisos RBAC
| Permiso | Descripcion |
|---------|-------------|
| commissions:read | Ver comisiones |
| commissions:write | Crear/editar esquemas |
| commissions:approve | Aprobar comisiones |
| commissions:pay | Marcar como pagadas |
| commissions:manage | Administrar todo |
---
## 7. Integraciones
| Modulo | Integracion |
|--------|-------------|
| sales | Trigger en OpportunityWon |
| goals | Tracking de metas de comision |
| mlm | Comisiones MLM |
| notifications | Alertas de aprobacion/pago |
| audit | Log de cambios |
---
## 8. Referencias
- DDL: `database/ddl/schemas/commissions/`
- Backend: `backend/src/modules/commissions/`
- Frontend: `frontend/src/services/commissions/`
- Hooks: `frontend/src/hooks/useCommissions.ts`