- 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>
435 lines
12 KiB
Markdown
435 lines
12 KiB
Markdown
---
|
||
id: "ET-SAAS-021"
|
||
title: "Especificacion Tecnica MLM (Multi-Level Marketing)"
|
||
type: "TechnicalSpec"
|
||
status: "Implemented"
|
||
priority: "P1"
|
||
module: "mlm"
|
||
version: "1.0.0"
|
||
created_date: "2026-02-03"
|
||
updated_date: "2026-02-03"
|
||
story_points: 13
|
||
---
|
||
|
||
# ET-SAAS-021: Especificacion Tecnica - MLM (Multi-Level Marketing)
|
||
|
||
## Metadata
|
||
- **Codigo:** ET-SAAS-021
|
||
- **Modulo:** MLM
|
||
- **Version:** 1.0.0
|
||
- **Estado:** Implementado (Backend)
|
||
- **Fecha:** 2026-02-03
|
||
- **Basado en:** SAAS-021
|
||
|
||
---
|
||
|
||
## 1. Resumen Ejecutivo
|
||
|
||
### 1.1 Estado Actual
|
||
|
||
Sistema MLM con backend implementado, UI pendiente.
|
||
|
||
| Capacidad | Estado | Notas |
|
||
|-----------|--------|-------|
|
||
| Structures | SI | Unilevel, Binary, Matrix |
|
||
| Ranks | SI | Rangos con requisitos |
|
||
| Nodes | SI | Arbol de distribuidores |
|
||
| Commissions | SI | Comisiones multinivel |
|
||
| Bonuses | SI | Bonos de rango |
|
||
| LTREE | SI | Consultas eficientes |
|
||
| RLS | SI | Row Level Security |
|
||
| UI | NO | Frontend pendiente |
|
||
|
||
### 1.2 Funcionalidades v1.0
|
||
|
||
Sistema completo con:
|
||
|
||
- **6 Entidades**: Structures, Ranks, Nodes, Commissions, Bonuses, RankHistory
|
||
- **4 Tipos de estructura**: unilevel, binary, matrix, hybrid
|
||
- **5 Tipos de comision**: level, matching, infinity, leadership, pool
|
||
- **4 Tipos de bono**: rank_achievement, rank_maintenance, fast_start, pool_share
|
||
- **LTREE**: Para consultas eficientes de upline/downline
|
||
|
||
---
|
||
|
||
## 2. Modelo de Datos
|
||
|
||
### 2.1 Schema: mlm
|
||
|
||
#### Tabla: structures
|
||
| Campo | Tipo | Descripcion |
|
||
|-------|------|-------------|
|
||
| id | UUID | PK |
|
||
| tenant_id | UUID | FK tenants |
|
||
| name | VARCHAR(100) | Nombre estructura |
|
||
| description | TEXT | Descripcion |
|
||
| type | ENUM | unilevel, binary, matrix, hybrid |
|
||
| config | JSONB | Configuracion especifica |
|
||
| level_rates | JSONB | Tasas por nivel |
|
||
| matching_rates | JSONB | Tasas de matching bonus |
|
||
| is_active | BOOLEAN | Estado |
|
||
|
||
**Configuracion por tipo:**
|
||
```json
|
||
// Unilevel
|
||
{"max_width": null, "max_depth": 10}
|
||
|
||
// Binary
|
||
{"spillover": "left_first" | "weak_leg" | "balanced"}
|
||
|
||
// Matrix
|
||
{"width": 3, "depth": 7}
|
||
```
|
||
|
||
**Level rates:**
|
||
```json
|
||
[
|
||
{"level": 1, "rate": 0.10},
|
||
{"level": 2, "rate": 0.05},
|
||
{"level": 3, "rate": 0.03}
|
||
]
|
||
```
|
||
|
||
#### Tabla: ranks
|
||
| Campo | Tipo | Descripcion |
|
||
|-------|------|-------------|
|
||
| id | UUID | PK |
|
||
| tenant_id | UUID | FK tenants |
|
||
| structure_id | UUID | FK structures |
|
||
| name | VARCHAR(100) | "Silver", "Gold", etc. |
|
||
| level | INT | 1, 2, 3... (orden) |
|
||
| badge_url | VARCHAR(500) | Imagen de badge |
|
||
| color | VARCHAR(7) | Color hex |
|
||
| requirements | JSONB | Requisitos |
|
||
| bonus_rate | DECIMAL(10,4) | Tasa de bono adicional |
|
||
| benefits | JSONB | Beneficios del rango |
|
||
| is_active | BOOLEAN | Estado |
|
||
|
||
**Requisitos ejemplo:**
|
||
```json
|
||
{
|
||
"personal_volume": 1000,
|
||
"group_volume": 10000,
|
||
"direct_referrals": 3,
|
||
"active_legs": 2,
|
||
"rank_in_legs": {"rank_level": 2, "count": 1}
|
||
}
|
||
```
|
||
|
||
#### Tabla: nodes
|
||
| Campo | Tipo | Descripcion |
|
||
|-------|------|-------------|
|
||
| id | UUID | PK |
|
||
| tenant_id | UUID | FK tenants |
|
||
| structure_id | UUID | FK structures |
|
||
| user_id | UUID | FK users |
|
||
| parent_id | UUID | FK nodes (padre directo) |
|
||
| sponsor_id | UUID | FK nodes (patrocinador) |
|
||
| position | INT | 1=left, 2=right (binary) |
|
||
| path | LTREE | Ruta materializada |
|
||
| depth | INT | Profundidad en el arbol |
|
||
| rank_id | UUID | FK ranks (rango actual) |
|
||
| highest_rank_id | UUID | FK ranks (maximo alcanzado) |
|
||
| personal_volume | DECIMAL(15,2) | Volumen personal |
|
||
| group_volume | DECIMAL(15,2) | Volumen de grupo |
|
||
| direct_referrals | INT | Referidos directos |
|
||
| total_downline | INT | Total de linea descendente |
|
||
| total_earnings | DECIMAL(15,2) | Ganancias totales |
|
||
| status | ENUM | pending, active, inactive, suspended |
|
||
| joined_at | TIMESTAMPTZ | Fecha de ingreso |
|
||
| invite_code | VARCHAR(20) | Codigo de invitacion |
|
||
|
||
#### Tabla: commissions
|
||
| Campo | Tipo | Descripcion |
|
||
|-------|------|-------------|
|
||
| id | UUID | PK |
|
||
| tenant_id | UUID | FK tenants |
|
||
| node_id | UUID | FK nodes (receptor) |
|
||
| source_node_id | UUID | FK nodes (origen) |
|
||
| type | ENUM | level, matching, infinity, leadership, pool |
|
||
| level | INT | Nivel de profundidad |
|
||
| source_amount | DECIMAL(15,2) | Monto origen |
|
||
| rate_applied | DECIMAL(10,4) | Tasa aplicada |
|
||
| commission_amount | DECIMAL(15,2) | Comision final |
|
||
| currency | VARCHAR(3) | Moneda |
|
||
| period_id | UUID | FK commissions.periods |
|
||
| source_reference | VARCHAR(200) | Referencia a transaccion |
|
||
| status | ENUM | pending, approved, paid, cancelled |
|
||
| paid_at | TIMESTAMPTZ | Fecha de pago |
|
||
|
||
#### Tabla: bonuses
|
||
| Campo | Tipo | Descripcion |
|
||
|-------|------|-------------|
|
||
| id | UUID | PK |
|
||
| tenant_id | UUID | FK tenants |
|
||
| node_id | UUID | FK nodes |
|
||
| rank_id | UUID | FK ranks |
|
||
| type | ENUM | rank_achievement, rank_maintenance, fast_start, pool_share |
|
||
| amount | DECIMAL(15,2) | Monto del bono |
|
||
| currency | VARCHAR(3) | Moneda |
|
||
| period_id | UUID | FK commissions.periods |
|
||
| status | ENUM | pending, approved, paid, cancelled |
|
||
| paid_at | TIMESTAMPTZ | Fecha de pago |
|
||
| achieved_at | TIMESTAMPTZ | Fecha de logro |
|
||
|
||
#### Tabla: rank_history
|
||
| Campo | Tipo | Descripcion |
|
||
|-------|------|-------------|
|
||
| id | UUID | PK |
|
||
| tenant_id | UUID | FK tenants |
|
||
| node_id | UUID | FK nodes |
|
||
| rank_id | UUID | FK ranks (nuevo rango) |
|
||
| previous_rank_id | UUID | FK ranks (rango anterior) |
|
||
| personal_volume_at | DECIMAL(15,2) | Snapshot |
|
||
| group_volume_at | DECIMAL(15,2) | Snapshot |
|
||
| direct_referrals_at | INT | Snapshot |
|
||
| achieved_at | TIMESTAMPTZ | Fecha de logro |
|
||
|
||
### 2.2 Enums
|
||
|
||
```sql
|
||
mlm.structure_type: unilevel, binary, matrix, hybrid
|
||
mlm.node_status: pending, active, inactive, suspended
|
||
mlm.commission_type: level, matching, infinity, leadership, pool
|
||
mlm.commission_status: pending, approved, paid, cancelled
|
||
mlm.bonus_type: rank_achievement, rank_maintenance, fast_start, pool_share
|
||
```
|
||
|
||
---
|
||
|
||
## 3. LTREE para Consultas Eficientes
|
||
|
||
### 3.1 Estructura del Path
|
||
|
||
```
|
||
Ejemplo: root.node1.node2.node3
|
||
└── Ruta desde la raiz hasta el nodo
|
||
```
|
||
|
||
### 3.2 Consultas Comunes
|
||
|
||
```sql
|
||
-- Obtener todos los ancestros (upline)
|
||
SELECT * FROM mlm.nodes WHERE path @> 'root.a.b.c';
|
||
|
||
-- Obtener todos los descendientes (downline)
|
||
SELECT * FROM mlm.nodes WHERE path <@ 'root.a.b.c';
|
||
|
||
-- Obtener descendientes hasta N niveles
|
||
SELECT * FROM mlm.nodes
|
||
WHERE path <@ 'root.a.b.c'
|
||
AND nlevel(path) - nlevel('root.a.b.c') <= 3;
|
||
|
||
-- Obtener hermanos
|
||
SELECT * FROM mlm.nodes WHERE subpath(path, 0, -1) = 'root.a.b';
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Arquitectura Backend
|
||
|
||
### 4.1 Estructura de Archivos
|
||
|
||
```
|
||
backend/src/modules/mlm/
|
||
├── mlm.module.ts
|
||
├── controllers/
|
||
│ ├── structures.controller.ts
|
||
│ ├── ranks.controller.ts
|
||
│ ├── nodes.controller.ts
|
||
│ ├── commissions.controller.ts
|
||
│ └── my-network.controller.ts
|
||
├── services/
|
||
│ ├── structures.service.ts
|
||
│ ├── ranks.service.ts
|
||
│ ├── nodes.service.ts
|
||
│ ├── commissions.service.ts
|
||
│ ├── calculation.service.ts
|
||
│ └── genealogy.service.ts
|
||
├── entities/
|
||
│ ├── structure.entity.ts
|
||
│ ├── rank.entity.ts
|
||
│ ├── node.entity.ts
|
||
│ ├── commission.entity.ts
|
||
│ ├── bonus.entity.ts
|
||
│ └── rank-history.entity.ts
|
||
└── dto/
|
||
```
|
||
|
||
### 4.2 Endpoints API
|
||
|
||
#### Structures
|
||
| Metodo | Endpoint | Descripcion |
|
||
|--------|----------|-------------|
|
||
| GET | /mlm/structures | Listar |
|
||
| GET | /mlm/structures/:id | Obtener |
|
||
| POST | /mlm/structures | Crear |
|
||
| PUT | /mlm/structures/:id | Actualizar |
|
||
| DELETE | /mlm/structures/:id | Eliminar |
|
||
|
||
#### Ranks
|
||
| Metodo | Endpoint | Descripcion |
|
||
|--------|----------|-------------|
|
||
| GET | /mlm/ranks | Listar |
|
||
| GET | /mlm/ranks/:id | Obtener |
|
||
| POST | /mlm/ranks | Crear |
|
||
| PUT | /mlm/ranks/:id | Actualizar |
|
||
| DELETE | /mlm/ranks/:id | Eliminar |
|
||
| POST | /mlm/ranks/evaluate/:structureId | Evaluar todos |
|
||
|
||
#### Nodes
|
||
| Metodo | Endpoint | Descripcion |
|
||
|--------|----------|-------------|
|
||
| GET | /mlm/nodes | Listar |
|
||
| GET | /mlm/nodes/:id | Obtener |
|
||
| POST | /mlm/nodes | Crear (registrar en red) |
|
||
| PUT | /mlm/nodes/:id | Actualizar |
|
||
| PATCH | /mlm/nodes/:id/status | Cambiar status |
|
||
| GET | /mlm/nodes/:id/downline | Linea descendente |
|
||
| GET | /mlm/nodes/:id/upline | Linea ascendente |
|
||
| GET | /mlm/nodes/:id/tree | Arbol visual |
|
||
|
||
#### My Network (Usuario autenticado)
|
||
| Metodo | Endpoint | Descripcion |
|
||
|--------|----------|-------------|
|
||
| GET | /mlm/my/dashboard | Mi dashboard |
|
||
| GET | /mlm/my/network | Mi red |
|
||
| GET | /mlm/my/earnings | Mis ganancias |
|
||
| GET | /mlm/my/rank | Mi rango actual |
|
||
| POST | /mlm/my/invite | Generar link de invitacion |
|
||
|
||
#### Commissions
|
||
| Metodo | Endpoint | Descripcion |
|
||
|--------|----------|-------------|
|
||
| GET | /mlm/commissions | Listar |
|
||
| GET | /mlm/commissions/:id | Obtener |
|
||
| POST | /mlm/commissions/calculate | Calcular |
|
||
| PATCH | /mlm/commissions/:id/status | Cambiar status |
|
||
| GET | /mlm/commissions/by-level | Por nivel |
|
||
|
||
---
|
||
|
||
## 5. Logica de Comisiones
|
||
|
||
### 5.1 Algoritmo de Calculo por Nivel
|
||
|
||
```typescript
|
||
async calculateLevelCommissions(sale: Sale): Promise<Commission[]> {
|
||
const commissions: Commission[] = [];
|
||
|
||
// Obtener nodo del vendedor
|
||
const sourceNode = await this.getNodeByUserId(sale.userId);
|
||
|
||
// Obtener estructura y tasas
|
||
const structure = await this.getStructure(sourceNode.structureId);
|
||
const levelRates = structure.levelRates;
|
||
|
||
// Recorrer upline
|
||
let currentNode = await this.getParentNode(sourceNode.id);
|
||
let level = 1;
|
||
|
||
while (currentNode && level <= levelRates.length) {
|
||
const rate = levelRates.find(r => r.level === level)?.rate || 0;
|
||
|
||
if (rate > 0 && currentNode.status === 'active') {
|
||
commissions.push({
|
||
nodeId: currentNode.id,
|
||
sourceNodeId: sourceNode.id,
|
||
type: 'level',
|
||
level,
|
||
sourceAmount: sale.amount,
|
||
rateApplied: rate,
|
||
commissionAmount: sale.amount * rate
|
||
});
|
||
}
|
||
|
||
currentNode = await this.getParentNode(currentNode.id);
|
||
level++;
|
||
}
|
||
|
||
return commissions;
|
||
}
|
||
```
|
||
|
||
### 5.2 Ejemplo
|
||
|
||
```
|
||
Estructura: 3 niveles (10%, 5%, 3%)
|
||
Venta: $1,000
|
||
|
||
Usuario D hace venta →
|
||
Nivel 1 (Usuario C): $1,000 × 10% = $100
|
||
Nivel 2 (Usuario B): $1,000 × 5% = $50
|
||
Nivel 3 (Usuario A): $1,000 × 3% = $30
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Frontend (Pendiente)
|
||
|
||
### 6.1 Paginas Propuestas
|
||
|
||
| Ruta | Componente | Estado |
|
||
|------|------------|--------|
|
||
| /dashboard/mlm | MLMDashboard | Pendiente |
|
||
| /dashboard/mlm/network | NetworkPage | Pendiente |
|
||
| /dashboard/mlm/genealogy | GenealogyPage | Pendiente |
|
||
| /dashboard/mlm/ranks | RanksPage | Pendiente |
|
||
| /dashboard/mlm/commissions | MLMCommissionsPage | Pendiente |
|
||
| /dashboard/mlm/my-network | MyNetworkPage | Pendiente |
|
||
|
||
### 6.2 Hooks Existentes
|
||
|
||
```typescript
|
||
// frontend/src/hooks/useMlm.ts
|
||
useStructures, useStructure, useCreateStructure, useUpdateStructure
|
||
useRanks, useRank, useCreateRank, useEvaluateRanks
|
||
useNodes, useNode, useNodeDownline, useNodeUpline, useNodeTree
|
||
useMyDashboard, useMyNetwork, useMyEarnings, useMyRank, useGenerateInviteLink
|
||
useMLMCommissions, useCalculateCommissions, useCommissionsByLevel
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Seguridad
|
||
|
||
### 7.1 RLS
|
||
|
||
```sql
|
||
CREATE POLICY nodes_tenant_isolation ON mlm.nodes
|
||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||
|
||
CREATE POLICY commissions_tenant_isolation ON mlm.commissions
|
||
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
||
```
|
||
|
||
### 7.2 Permisos RBAC
|
||
|
||
| Permiso | Descripcion |
|
||
|---------|-------------|
|
||
| mlm:read | Ver red y comisiones |
|
||
| mlm:write | Gestionar nodos |
|
||
| mlm:admin | Administrar estructuras |
|
||
| mlm:calculate | Ejecutar calculos |
|
||
|
||
---
|
||
|
||
## 8. Integraciones
|
||
|
||
| Modulo | Integracion |
|
||
|--------|-------------|
|
||
| sales | Trigger en ventas para comisiones |
|
||
| commissions | Periodos compartidos |
|
||
| goals | Metas de volumen |
|
||
| notifications | Alertas de rango |
|
||
| audit | Log de cambios |
|
||
|
||
---
|
||
|
||
## 9. Referencias
|
||
|
||
- DDL: `database/ddl/schemas/mlm/`
|
||
- Backend: `backend/src/modules/mlm/`
|
||
- Frontend Services: `frontend/src/services/mlm/`
|
||
- Frontend Hooks: `frontend/src/hooks/useMlm.ts`
|