trading-platform/docs/02-definicion-modulos/OQI-004-investment-accounts/historias-usuario/US-INV-008-recibir-distribucion.md

353 lines
14 KiB
Markdown

# 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