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