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

11 KiB
Raw Blame History

id title type status priority module version created_date updated_date story_points
ET-SAAS-020 Especificacion Tecnica Commissions TechnicalSpec Implemented P1 commissions 1.0.0 2026-02-03 2026-02-03 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:

[
  {"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

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

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

// 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

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