---
id: "US-INV-008"
title: "Recibir Distribución de Utilidades"
type: "User Story"
status: "Done"
priority: "Media"
epic: "OQI-004"
project: "trading-platform"
story_points: 3
created_date: "2025-12-05"
updated_date: "2026-01-04"
---

# US-INV-008: Recibir Distribución de Utilidades

## Metadata

| Campo | Valor |
|-------|-------|
| **ID** | US-INV-008 |
| **Épica** | OQI-004 - Cuentas de Inversión |
| **Módulo** | investment |
| **Prioridad** | P1 |
| **Story Points** | 5 |
| **Sprint** | Sprint 6 |
| **Estado** | Pendiente |
| **Asignado a** | Por asignar |

---

## Historia de Usuario

**Como** inversor,
**quiero** recibir distribuciones automáticas de utilidades mensuales,
**para** obtener retornos periódicos de mi inversión sin tener que hacer nada manualmente.

## Descripción Detallada

El sistema debe ejecutar un proceso automático mensual (primer día de cada mes) que calcula las utilidades generadas por cada cuenta de inversión en el mes anterior, y distribuye las ganancias (o registra las pérdidas) actualizando el balance. Los usuarios deben recibir notificación de la distribución y poder ver el histórico.

## Mockups/Wireframes

```
┌─────────────────────────────────────────────────────────────────┐
│                    DISTRIBUCIÓN DE UTILIDADES                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  📬 Nueva Distribución Mensual - Noviembre 2025                 │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │  🎉 ¡Felicidades!                                        │  │
│  │                                                           │  │
│  │  Tu agente Atlas generó utilidades en Noviembre 2025     │  │
│  │                                                           │  │
│  │  Balance inicial (01 Nov):    $1,000.00                  │  │
│  │  Balance final (30 Nov):      $1,048.00                  │  │
│  │  ─────────────────────────────                           │  │
│  │  Utilidad del mes:            +$48.00 (+4.8%)            │  │
│  │                                                           │  │
│  │  Nuevo balance:               $1,048.00                  │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │  📊 Desglose del Mes                                     │  │
│  │                                                           │  │
│  │  Total trades:            28                              │  │
│  │  Trades ganadores:        22 (78.6%)                      │  │
│  │  Trades perdedores:       6 (21.4%)                       │  │
│  │                                                           │  │
│  │  Ganancia total trades:   +$125.30                        │  │
│  │  Pérdida total trades:    -$45.20                         │  │
│  │  Comisiones exchange:     -$32.10                         │  │
│  │  ─────────────────────────────                           │  │
│  │  Utilidad neta:           +$48.00                         │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │  📈 Historial de Distribuciones                         │  │
│  │                                                           │  │
│  │  Nov 2025:  +$48.00 (+4.8%)  🟢                          │  │
│  │  Oct 2025:  +$32.00 (+3.2%)  🟢                          │  │
│  │  Sep 2025:  +$51.00 (+5.1%)  🟢                          │  │
│  │  Ago 2025:  +$29.00 (+2.9%)  🟢                          │  │
│  │  Jul 2025:  -$12.00 (-1.2%)  🔴                          │  │
│  │  Jun 2025:  +$63.00 (+6.3%)  🟢                          │  │
│  │                                                           │  │
│  │  [Ver historial completo →]                             │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
```

---

## Criterios de Aceptación

**Escenario 1: Distribución mensual automática exitosa**
```gherkin
DADO que es el día 1 del mes a las 00:00 UTC
Y existen cuentas de inversión activas
CUANDO el cron job executeMonthlyDistribution() se ejecuta
ENTONCES para cada cuenta activa:
  Y se calcula utilidad del mes anterior
  Y se crea transacción tipo "distribution"
  Y se actualiza balance de la cuenta
  Y se envía email con resumen de distribución
  Y se envía notificación push
Y se registra log de ejecución exitosa
```

**Escenario 2: Distribución con utilidad positiva**
```gherkin
DADO que la cuenta tuvo balance inicial $1,000
Y balance final $1,048
CUANDO se ejecuta distribución
ENTONCES se calcula utilidad = $48 (+4.8%)
Y se crea transacción con amount = 48, type = "distribution"
Y el nuevo balance es $1,048
Y se envía email "¡Ganaste $48 este mes!"
```

**Escenario 3: Distribución con pérdida**
```gherkin
DADO que la cuenta tuvo balance inicial $1,000
Y balance final $988
CUANDO se ejecuta distribución
ENTONCES se calcula pérdida = -$12 (-1.2%)
Y se crea transacción con amount = -12, type = "distribution"
Y el nuevo balance es $988
Y se envía email "Reporte mensual: -$12 este mes"
```

**Escenario 4: Cuenta sin actividad en el mes**
```gherkin
DADO que la cuenta no tuvo trades en el mes
Y el balance no cambió
CUANDO se ejecuta distribución
ENTONCES se registra utilidad = $0 (0%)
Y NO se crea transacción
Y NO se envía email
```

**Escenario 5: Ver historial de distribuciones**
```gherkin
DADO que el usuario tiene cuenta con 6 meses de historial
CUANDO navega a /investment/distributions/:accountId
ENTONCES se muestra lista de distribuciones mensuales
Y cada distribución muestra: mes, utilidad, porcentaje, desglose
Y se muestra gráfico de evolución
```

**Escenario 6: Cuenta cerrada durante el mes**
```gherkin
DADO que la cuenta se cerró el día 15 del mes
CUANDO se ejecuta distribución mensual
ENTONCES se calcula utilidad solo hasta día 15
Y se procesa distribución proporcional
Y se marca la cuenta como "final distribution"
```

## Criterios Adicionales

- [ ] Calcular métricas detalladas (win rate, total trades, etc.)
- [ ] Guardar snapshot del balance al inicio de cada mes
- [ ] Permitir re-ejecutar distribución si falla
- [ ] Dashboard de admin para monitorear distribuciones
- [ ] Alertas si alguna distribución falla

---

## Tareas Técnicas

**Database:**
- [ ] DB-INV-001: Tabla investment.distributions
- [ ] DB-INV-002: Tabla investment.monthly_snapshots
- [ ] DB-INV-003: Función PL/pgSQL para calcular utilidades
- [ ] DB-INV-004: Índices en (account_id, period)

**Backend:**
- [ ] BE-INV-001: Implementar DistributionService.executeMonthly()
- [ ] BE-INV-002: Implementar DistributionService.calculateProfit()
- [ ] BE-INV-003: Cron job (node-cron) ejecutar primer día de mes
- [ ] BE-INV-004: Endpoint GET /investment/accounts/:id/distributions
- [ ] BE-INV-005: Crear transacciones de distribución
- [ ] BE-INV-006: Enviar emails de distribución
- [ ] BE-INV-007: Endpoint POST /admin/distributions/rerun (manual)
- [ ] BE-INV-008: Logging y monitoreo de distribuciones

**Frontend:**
- [ ] FE-INV-001: Crear página DistributionsPage.tsx
- [ ] FE-INV-002: Crear componente DistributionCard.tsx
- [ ] FE-INV-003: Crear componente MonthlyBreakdown.tsx
- [ ] FE-INV-004: Crear componente DistributionHistory.tsx
- [ ] FE-INV-005: Notificación toast para nueva distribución
- [ ] FE-INV-006: Implementar distributionsStore

**Tests:**
- [ ] TEST-INV-001: Test cálculo de utilidades
- [ ] TEST-INV-002: Test cron job execution
- [ ] TEST-INV-003: Test distribución con pérdidas
- [ ] TEST-INV-004: Test cuenta cerrada mid-month
- [ ] TEST-INV-005: Test email sending
- [ ] TEST-INV-006: Test E2E flujo completo

---

## Dependencias

**Depende de:**
- [ ] US-INV-004: Ver dashboard portfolio - Estado: Pendiente
- [ ] US-INV-007: Ver transacciones - Estado: Pendiente
- [ ] Email service configurado

**Bloquea:**
- [ ] US-INV-011: Exportar reporte PDF

---

## Notas Técnicas

**Endpoints involucrados:**
| Método | Endpoint | Descripción |
|--------|----------|-------------|
| GET | /investment/accounts/:id/distributions | Lista de distribuciones |
| GET | /investment/distributions/:id | Detalle de distribución |
| POST | /admin/distributions/execute | Ejecutar manualmente |

**Entidades/Tablas:**
- `investment.distributions`: Registro de distribuciones
- `investment.monthly_snapshots`: Snapshots de inicio de mes
- `investment.transactions`: Transacciones de distribución

**Schema investment.distributions:**
```sql
CREATE TABLE investment.distributions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  account_id UUID NOT NULL REFERENCES investment.accounts(id),
  period VARCHAR(7) NOT NULL, -- "2025-11"
  start_date DATE NOT NULL,
  end_date DATE NOT NULL,
  opening_balance DECIMAL(15,2) NOT NULL,
  closing_balance DECIMAL(15,2) NOT NULL,
  profit_amount DECIMAL(15,2) NOT NULL,
  profit_percentage DECIMAL(5,2) NOT NULL,
  total_trades INTEGER NOT NULL,
  winning_trades INTEGER NOT NULL,
  losing_trades INTEGER NOT NULL,
  total_gains DECIMAL(15,2) NOT NULL,
  total_losses DECIMAL(15,2) NOT NULL,
  total_fees DECIMAL(15,2) NOT NULL,
  transaction_id UUID REFERENCES investment.transactions(id),
  executed_at TIMESTAMP DEFAULT NOW(),
  UNIQUE(account_id, period)
);
```

**Response GET /distributions:**
```typescript
{
  distributions: [
    {
      id: "uuid",
      accountId: "uuid",
      period: "2025-11",
      startDate: "2025-11-01",
      endDate: "2025-11-30",
      openingBalance: 1000,
      closingBalance: 1048,
      profitAmount: 48,
      profitPercentage: 4.8,
      breakdown: {
        totalTrades: 28,
        winningTrades: 22,
        losingTrades: 6,
        winRate: 78.6,
        totalGains: 125.30,
        totalLosses: -45.20,
        totalFees: -32.10
      },
      executedAt: "2025-12-01T00:00:00Z"
    }
  ]
}
```

**Cron Schedule:**
```javascript
// Ejecutar el día 1 de cada mes a las 00:00 UTC
cron.schedule('0 0 1 * *', async () => {
  await DistributionService.executeMonthlyDistribution();
});
```

**Cálculo de Utilidad:**
```typescript
profit = closingBalance - openingBalance - depositsInMonth + withdrawalsInMonth
profitPercentage = (profit / openingBalance) * 100
```

**Email Template:**
```
Subject: 📊 Distribución Mensual - Noviembre 2025

Hola {userName},

¡Tu agente {agentName} completó otro mes de trading!

Resumen del Mes:
- Balance inicial: ${openingBalance}
- Balance final: ${closingBalance}
- Utilidad: ${profit} ({profitPercentage}%)

Estadísticas:
- Total trades: {totalTrades}
- Win rate: {winRate}%

[Ver detalle completo →]
```

**Manejo de Errores:**
- Si falla distribución para una cuenta, continuar con las demás
- Registrar error en tabla `distribution_errors`
- Enviar alerta a admin
- Permitir re-ejecución manual desde admin panel

---

## Definition of Ready (DoR)

- [x] Historia claramente escrita
- [x] Criterios de aceptación definidos
- [x] Story points estimados
- [x] Dependencias identificadas
- [x] Fórmula de cálculo definida
- [ ] Email templates diseñados
- [x] API spec disponible

## Definition of Done (DoD)

- [ ] Código implementado según criterios
- [ ] Tests unitarios escritos y pasando
- [ ] Tests de integración pasando
- [ ] Cron job configurado y testeado
- [ ] Email service funcionando
- [ ] Manejo de errores robusto
- [ ] Logging completo
- [ ] Code review aprobado
- [ ] Documentación actualizada
- [ ] Tested en staging con fecha simulada
- [ ] QA aprobado
- [ ] Desplegado en ambiente de pruebas

---

## Historial de Cambios

| Fecha | Cambio | Autor |
|-------|--------|-------|
| 2025-12-05 | Creación | Requirements-Analyst |

---

**Creada por:** Requirements-Analyst
**Fecha:** 2025-12-05
**Última actualización:** 2025-12-05
