#!/usr/bin/env node /** * Script para crear proyectos de prueba en Dolibarr usando la API Route * Este script usa la API Route segura de Next.js * * Incluye: * - Proyectos en diferentes estados (borrador, abierto, cerrado) * - Tareas asociadas a cada proyecto * - Limpieza de proyectos existentes antes de crear nuevos * * REQUISITO: El servidor de Next.js debe estar corriendo (npm run dev) * Uso: node scripts/seed-projects-via-api.js */ const NEXT_API_URL = 'http://localhost:3000/api/dolibarr'; // Datos de proyectos de ejemplo con variedad de estados y presupuestos const projectsData = [ // ==================== PROYECTOS ABIERTOS (EN PROGRESO) ==================== { title: "Desarrollo de App Móvil", ref: "PROJ-2024-001", description: "Aplicación móvil para gestión de inventario con funcionalidades de escaneo QR y sincronización en tiempo real.", opp_amount: 45000, opp_percent: 65, date_start: Math.floor(new Date('2024-01-15').getTime() / 1000), date_end: Math.floor(new Date('2024-06-30').getTime() / 1000), targetStatus: 'open', // Validar y dejar abierto tasks: [ { label: "Diseño de wireframes", progress: 100, priority: 2, planned_hours: 24, status: 'completed' }, { label: "Desarrollo frontend React Native", progress: 80, priority: 3, planned_hours: 120, status: 'open' }, { label: "Integración API backend", progress: 60, priority: 3, planned_hours: 80, status: 'open' }, { label: "Implementar escáner QR", progress: 40, priority: 2, planned_hours: 40, status: 'open' }, { label: "Testing y QA", progress: 20, priority: 2, planned_hours: 60, status: 'open' }, ] }, { title: "Sistema de Facturación Automática", ref: "PROJ-2024-003", description: "Desarrollo de sistema automatizado para generación y envío de facturas con integración a contabilidad.", opp_amount: 62000, opp_percent: 35, date_start: Math.floor(new Date('2024-02-01').getTime() / 1000), date_end: Math.floor(new Date('2024-08-31').getTime() / 1000), targetStatus: 'open', tasks: [ { label: "Análisis de requisitos", progress: 100, priority: 3, planned_hours: 16, status: 'completed' }, { label: "Diseño de base de datos", progress: 100, priority: 2, planned_hours: 24, status: 'completed' }, { label: "Módulo de generación PDF", progress: 50, priority: 3, planned_hours: 40, status: 'open' }, { label: "Integración con pasarela de pago", progress: 10, priority: 2, planned_hours: 32, status: 'open' }, { label: "Panel de administración", progress: 0, priority: 1, planned_hours: 48, status: 'draft' }, ] }, { title: "Migración a Cloud AWS", ref: "PROJ-2024-004", description: "Migración completa de infraestructura on-premise a AWS con configuración de alta disponibilidad.", opp_amount: 85000, opp_percent: 20, date_start: Math.floor(new Date('2024-03-01').getTime() / 1000), date_end: Math.floor(new Date('2024-12-31').getTime() / 1000), targetStatus: 'open', tasks: [ { label: "Auditoría de infraestructura actual", progress: 100, priority: 3, planned_hours: 40, status: 'completed' }, { label: "Diseño de arquitectura AWS", progress: 60, priority: 3, planned_hours: 32, status: 'open' }, { label: "Configuración de VPC y networking", progress: 20, priority: 2, planned_hours: 24, status: 'open' }, { label: "Migración de bases de datos", progress: 0, priority: 3, planned_hours: 48, status: 'draft' }, { label: "Configuración de CI/CD", progress: 0, priority: 2, planned_hours: 32, status: 'draft' }, { label: "Testing de carga", progress: 0, priority: 2, planned_hours: 24, status: 'draft' }, ] }, { title: "API REST Microservicios", ref: "PROJ-2024-006", description: "Desarrollo de arquitectura de microservicios con API REST para integración de sistemas legacy.", opp_amount: 72000, opp_percent: 50, date_start: Math.floor(new Date('2024-01-20').getTime() / 1000), date_end: Math.floor(new Date('2024-07-31').getTime() / 1000), targetStatus: 'open', tasks: [ { label: "Diseño de arquitectura", progress: 100, priority: 3, planned_hours: 24, status: 'completed' }, { label: "Servicio de autenticación", progress: 100, priority: 3, planned_hours: 40, status: 'completed' }, { label: "Servicio de usuarios", progress: 80, priority: 2, planned_hours: 32, status: 'open' }, { label: "Servicio de productos", progress: 60, priority: 2, planned_hours: 32, status: 'open' }, { label: "Gateway API", progress: 30, priority: 3, planned_hours: 24, status: 'open' }, { label: "Documentación OpenAPI", progress: 20, priority: 1, planned_hours: 16, status: 'open' }, ] }, { title: "App de Gestión de Proyectos", ref: "PROJ-2024-007", description: "Plataforma web para gestión ágil de proyectos con tableros Kanban, Gantt y reporting automático.", opp_amount: 55000, opp_percent: 75, date_start: Math.floor(new Date('2023-12-01').getTime() / 1000), date_end: Math.floor(new Date('2024-05-31').getTime() / 1000), targetStatus: 'open', tasks: [ { label: "Maquetación UI/UX", progress: 100, priority: 2, planned_hours: 32, status: 'completed' }, { label: "Sistema de autenticación", progress: 100, priority: 3, planned_hours: 24, status: 'completed' }, { label: "Tablero Kanban", progress: 100, priority: 3, planned_hours: 48, status: 'completed' }, { label: "Vista Gantt", progress: 80, priority: 2, planned_hours: 40, status: 'open' }, { label: "Sistema de notificaciones", progress: 50, priority: 2, planned_hours: 24, status: 'open' }, { label: "Exportación de reportes", progress: 30, priority: 1, planned_hours: 20, status: 'open' }, ] }, { title: "E-commerce con Marketplace", ref: "PROJ-2024-008", description: "Plataforma de e-commerce completa con funcionalidad de marketplace multivendedor y pasarela de pagos.", opp_amount: 120000, opp_percent: 15, date_start: Math.floor(new Date('2024-04-01').getTime() / 1000), date_end: Math.floor(new Date('2025-03-31').getTime() / 1000), targetStatus: 'open', tasks: [ { label: "Investigación de mercado", progress: 100, priority: 2, planned_hours: 20, status: 'completed' }, { label: "Diseño de experiencia de usuario", progress: 70, priority: 3, planned_hours: 40, status: 'open' }, { label: "Catálogo de productos", progress: 20, priority: 2, planned_hours: 60, status: 'open' }, { label: "Carrito de compras", progress: 0, priority: 3, planned_hours: 40, status: 'draft' }, { label: "Integración Stripe/PayPal", progress: 0, priority: 3, planned_hours: 32, status: 'draft' }, { label: "Panel de vendedores", progress: 0, priority: 2, planned_hours: 48, status: 'draft' }, { label: "Sistema de reviews", progress: 0, priority: 1, planned_hours: 24, status: 'draft' }, ] }, { title: "Sistema de Reservas Online", ref: "PROJ-2024-011", description: "Plataforma de reservas con calendario interactivo, confirmación automática y pasarela de pago.", opp_amount: 42000, opp_percent: 45, date_start: Math.floor(new Date('2024-02-15').getTime() / 1000), date_end: Math.floor(new Date('2024-07-15').getTime() / 1000), targetStatus: 'open', tasks: [ { label: "Diseño de flujo de reservas", progress: 100, priority: 2, planned_hours: 16, status: 'completed' }, { label: "Calendario interactivo", progress: 80, priority: 3, planned_hours: 40, status: 'open' }, { label: "Sistema de disponibilidad", progress: 60, priority: 3, planned_hours: 32, status: 'open' }, { label: "Notificaciones por email/SMS", progress: 20, priority: 2, planned_hours: 24, status: 'open' }, { label: "Integración de pagos", progress: 0, priority: 3, planned_hours: 28, status: 'draft' }, ] }, { title: "App de Formación Online", ref: "PROJ-2024-012", description: "Plataforma LMS para formación online con seguimiento de progreso, certificados y evaluaciones.", opp_amount: 68000, opp_percent: 30, date_start: Math.floor(new Date('2024-03-10').getTime() / 1000), date_end: Math.floor(new Date('2024-10-31').getTime() / 1000), targetStatus: 'open', tasks: [ { label: "Análisis de requisitos LMS", progress: 100, priority: 2, planned_hours: 20, status: 'completed' }, { label: "Sistema de cursos y lecciones", progress: 50, priority: 3, planned_hours: 60, status: 'open' }, { label: "Reproductor de vídeo", progress: 40, priority: 2, planned_hours: 32, status: 'open' }, { label: "Sistema de evaluaciones", progress: 10, priority: 2, planned_hours: 40, status: 'open' }, { label: "Generación de certificados", progress: 0, priority: 1, planned_hours: 24, status: 'draft' }, { label: "Gamificación y badges", progress: 0, priority: 1, planned_hours: 32, status: 'draft' }, ] }, // ==================== PROYECTOS CERRADOS (COMPLETADOS) ==================== { title: "Rediseño Web Corporativo", ref: "PROJ-2024-002", description: "Renovación completa del sitio web corporativo con nuevo diseño responsive y optimización SEO.", opp_amount: 28000, opp_percent: 100, date_start: Math.floor(new Date('2023-11-01').getTime() / 1000), date_end: Math.floor(new Date('2024-02-28').getTime() / 1000), targetStatus: 'closed', // Cerrar el proyecto tasks: [ { label: "Auditoría web actual", progress: 100, priority: 2, planned_hours: 16, status: 'completed' }, { label: "Diseño nuevo branding", progress: 100, priority: 3, planned_hours: 32, status: 'completed' }, { label: "Desarrollo frontend", progress: 100, priority: 3, planned_hours: 80, status: 'completed' }, { label: "Optimización SEO", progress: 100, priority: 2, planned_hours: 24, status: 'completed' }, { label: "Migración de contenidos", progress: 100, priority: 2, planned_hours: 20, status: 'completed' }, { label: "Testing cross-browser", progress: 100, priority: 2, planned_hours: 16, status: 'completed' }, ] }, { title: "Dashboard Analítico BI", ref: "PROJ-2024-005", description: "Creación de dashboard de Business Intelligence para análisis de ventas y KPIs en tiempo real.", opp_amount: 38000, opp_percent: 100, date_start: Math.floor(new Date('2023-09-01').getTime() / 1000), date_end: Math.floor(new Date('2023-12-15').getTime() / 1000), targetStatus: 'closed', tasks: [ { label: "Definición de KPIs", progress: 100, priority: 3, planned_hours: 16, status: 'completed' }, { label: "Conexión a fuentes de datos", progress: 100, priority: 3, planned_hours: 32, status: 'completed' }, { label: "Diseño de visualizaciones", progress: 100, priority: 2, planned_hours: 24, status: 'completed' }, { label: "Desarrollo de dashboard", progress: 100, priority: 3, planned_hours: 48, status: 'completed' }, { label: "Reportes automatizados", progress: 100, priority: 2, planned_hours: 20, status: 'completed' }, { label: "Formación a usuarios", progress: 100, priority: 1, planned_hours: 8, status: 'completed' }, ] }, { title: "Portal de Empleados", ref: "PROJ-2024-010", description: "Portal interno para empleados con gestión de vacaciones, nóminas, formación y comunicación interna.", opp_amount: 35000, opp_percent: 100, date_start: Math.floor(new Date('2023-10-01').getTime() / 1000), date_end: Math.floor(new Date('2023-12-31').getTime() / 1000), targetStatus: 'closed', tasks: [ { label: "Portal de noticias interno", progress: 100, priority: 2, planned_hours: 24, status: 'completed' }, { label: "Gestión de vacaciones", progress: 100, priority: 3, planned_hours: 32, status: 'completed' }, { label: "Visualización de nóminas", progress: 100, priority: 3, planned_hours: 28, status: 'completed' }, { label: "Directorio de empleados", progress: 100, priority: 1, planned_hours: 16, status: 'completed' }, { label: "Integración con RRHH", progress: 100, priority: 2, planned_hours: 24, status: 'completed' }, ] }, { title: "Migración ERP Legacy", ref: "PROJ-2023-015", description: "Migración completa del sistema ERP legacy a nueva plataforma con cero tiempo de inactividad.", opp_amount: 95000, opp_percent: 100, date_start: Math.floor(new Date('2023-06-01').getTime() / 1000), date_end: Math.floor(new Date('2023-11-30').getTime() / 1000), targetStatus: 'closed', tasks: [ { label: "Mapeo de datos legacy", progress: 100, priority: 3, planned_hours: 40, status: 'completed' }, { label: "Desarrollo de scripts migración", progress: 100, priority: 3, planned_hours: 60, status: 'completed' }, { label: "Pruebas de migración", progress: 100, priority: 3, planned_hours: 48, status: 'completed' }, { label: "Migración en producción", progress: 100, priority: 3, planned_hours: 24, status: 'completed' }, { label: "Validación post-migración", progress: 100, priority: 2, planned_hours: 32, status: 'completed' }, { label: "Documentación técnica", progress: 100, priority: 1, planned_hours: 16, status: 'completed' }, ] }, // ==================== PROYECTOS EN BORRADOR ==================== { title: "Sistema CRM Personalizado", ref: "PROJ-2024-009", description: "CRM a medida con automatización de marketing, gestión de leads y pipeline de ventas.", opp_amount: 48000, opp_percent: 0, date_start: Math.floor(new Date('2024-05-01').getTime() / 1000), date_end: Math.floor(new Date('2024-11-30').getTime() / 1000), targetStatus: 'draft', // Dejar como borrador tasks: [ { label: "Definición de requisitos", progress: 0, priority: 3, planned_hours: 24, status: 'draft' }, { label: "Diseño de arquitectura", progress: 0, priority: 2, planned_hours: 20, status: 'draft' }, { label: "Módulo de contactos", progress: 0, priority: 2, planned_hours: 40, status: 'draft' }, { label: "Pipeline de ventas", progress: 0, priority: 3, planned_hours: 48, status: 'draft' }, { label: "Automatización de emails", progress: 0, priority: 2, planned_hours: 32, status: 'draft' }, ] }, { title: "App de Gestión de Flota", ref: "PROJ-2024-013", description: "Sistema de tracking GPS y gestión de flota vehicular con alertas y reportes de consumo.", opp_amount: 58000, opp_percent: 0, date_start: Math.floor(new Date('2024-06-01').getTime() / 1000), date_end: Math.floor(new Date('2024-12-31').getTime() / 1000), targetStatus: 'draft', tasks: [ { label: "Estudio de viabilidad", progress: 0, priority: 2, planned_hours: 16, status: 'draft' }, { label: "Integración GPS", progress: 0, priority: 3, planned_hours: 40, status: 'draft' }, { label: "Dashboard de tracking", progress: 0, priority: 3, planned_hours: 48, status: 'draft' }, { label: "Sistema de alertas", progress: 0, priority: 2, planned_hours: 24, status: 'draft' }, ] }, ]; /** * Obtener todos los proyectos existentes */ async function getExistingProjects() { try { const response = await fetch(`${NEXT_API_URL}/projects`); if (!response.ok) { if (response.status === 404) return []; throw new Error(`Error ${response.status}`); } return await response.json(); } catch (error) { console.log(' ⚠ No se pudieron obtener proyectos existentes'); return []; } } /** * Eliminar un proyecto */ async function deleteProject(projectId) { try { const response = await fetch(`${NEXT_API_URL}/projects/${projectId}`, { method: 'DELETE', }); return response.ok; } catch (error) { return false; } } /** * Crear un proyecto usando la API Route de Next.js */ async function createProject(projectData) { const url = `${NEXT_API_URL}/projects`; // Limpiar datos que no van a la API de creación const { tasks, targetStatus, ...apiData } = projectData; try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(apiData), }); if (!response.ok) { const errorData = await response.json(); throw new Error(`Error ${response.status}: ${errorData.error || 'Unknown error'}`); } const result = await response.json(); return result; } catch (error) { if (error.code === 'ECONNREFUSED') { throw new Error('No se puede conectar al servidor Next.js. ¿Está corriendo "npm run dev"?'); } throw error; } } /** * Validar proyecto (cambiar estado a validado/abierto) */ async function validateProject(projectId) { const url = `${NEXT_API_URL}/projects/${projectId}/validate`; try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ notrigger: 0 }), }); return response.ok; } catch (error) { return false; } } /** * Cerrar proyecto (cambiar estado a cerrado usando PUT) */ async function closeProject(projectId) { const url = `${NEXT_API_URL}/projects/${projectId}`; try { const response = await fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ status: "2" }), }); return response.ok; } catch (error) { return false; } } /** * Crear una tarea para un proyecto */ async function createTask(projectId, taskData, index) { const url = `${NEXT_API_URL}/tasks`; const timestamp = Date.now().toString(36).toUpperCase(); const taskPayload = { ref: `TK${projectId}-${index}-${timestamp}`, label: taskData.label, fk_project: projectId, progress: taskData.progress || 0, priority: String(taskData.priority || 0), planned_workload: (taskData.planned_hours || 0) * 3600, // Convertir horas a segundos }; try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(taskPayload), }); if (!response.ok) { return null; } const taskId = await response.json(); // Si la tarea debe estar completada o abierta, validarla if (taskData.status === 'completed' || taskData.status === 'open') { await validateTask(taskId); } return taskId; } catch (error) { return null; } } /** * Validar tarea */ async function validateTask(taskId) { try { const response = await fetch(`${NEXT_API_URL}/tasks/${taskId}/validate`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ notrigger: 0 }), }); return response.ok; } catch (error) { return false; } } /** * Script principal */ async function main() { console.log('\n🚀 SEED DE PROYECTOS DE PRUEBA PARA DOLIBARR\n'); console.log('═'.repeat(60)); console.log(`📡 API Next.js: ${NEXT_API_URL}`); console.log(`📊 Proyectos a crear: ${projectsData.length}`); console.log('═'.repeat(60)); console.log(''); // Verificar que Next.js esté corriendo console.log('🔍 Verificando conexión con Next.js...'); try { const testResponse = await fetch('http://localhost:3000'); if (!testResponse.ok && testResponse.status !== 404 && testResponse.status !== 307) { throw new Error('Server not responding'); } console.log('✅ Servidor Next.js está corriendo\n'); } catch (error) { console.error('❌ ERROR: No se puede conectar al servidor Next.js'); console.error(' Por favor, ejecuta "npm run dev" en otra terminal primero.\n'); process.exit(1); } // ==================== LIMPIEZA DE PROYECTOS EXISTENTES ==================== console.log('🧹 Limpiando proyectos existentes...'); const existingProjects = await getExistingProjects(); if (existingProjects.length > 0) { let deletedCount = 0; for (const project of existingProjects) { const projectId = project.id || project; const deleted = await deleteProject(projectId); if (deleted) { deletedCount++; process.stdout.write(`\r Eliminados: ${deletedCount}/${existingProjects.length}`); } await new Promise(resolve => setTimeout(resolve, 200)); } console.log(`\n ✅ ${deletedCount} proyectos eliminados\n`); } else { console.log(' ℹ No hay proyectos existentes\n'); } // ==================== CREACIÓN DE NUEVOS PROYECTOS ==================== let successCount = 0; let errorCount = 0; let tasksCreated = 0; const errors = []; for (let i = 0; i < projectsData.length; i++) { const project = projectsData[i]; const num = i + 1; const percentage = project.opp_percent; // Indicador visual del progreso const progressBar = '▓'.repeat(Math.floor(percentage / 10)) + '░'.repeat(10 - Math.floor(percentage / 10)); // Emoji según estado objetivo const statusEmoji = project.targetStatus === 'closed' ? '🏁' : project.targetStatus === 'draft' ? '📝' : '🔵'; console.log(`[${num}/${projectsData.length}] ${statusEmoji} ${project.title}`); console.log(` 📈 Progreso: [${progressBar}] ${percentage}%`); console.log(` 💰 Presupuesto: €${project.opp_amount.toLocaleString()}`); console.log(` 🎯 Estado objetivo: ${project.targetStatus}`); try { // Crear proyecto const projectId = await createProject(project); console.log(` ✅ Proyecto creado con ID: ${projectId}`); // Gestionar estado del proyecto if (project.targetStatus === 'open' || project.targetStatus === 'closed') { const validated = await validateProject(projectId); if (validated) { console.log(` ✓ Validado (estado: abierto)`); // Si debe estar cerrado, cerrarlo if (project.targetStatus === 'closed') { const closed = await closeProject(projectId); if (closed) { console.log(` 🏁 Cerrado (completado)`); } } } } else { console.log(` 📋 Dejado como borrador`); } // Crear tareas del proyecto if (project.tasks && project.tasks.length > 0) { console.log(` 📋 Creando ${project.tasks.length} tareas...`); let projectTasksCreated = 0; for (let j = 0; j < project.tasks.length; j++) { const task = project.tasks[j]; const taskId = await createTask(projectId, task, j + 1); if (taskId) { projectTasksCreated++; tasksCreated++; } await new Promise(resolve => setTimeout(resolve, 150)); } console.log(` ✅ ${projectTasksCreated}/${project.tasks.length} tareas creadas`); } successCount++; // Pequeña pausa para no saturar la API await new Promise(resolve => setTimeout(resolve, 300)); } catch (error) { console.log(` ❌ Error: ${error.message}`); errors.push({ project: project.title, error: error.message }); errorCount++; } console.log(''); } // Resumen final console.log('═'.repeat(60)); console.log('✨ PROCESO COMPLETADO\n'); console.log(`✅ Proyectos creados exitosamente: ${successCount}`); console.log(`📋 Tareas creadas: ${tasksCreated}`); console.log(`❌ Proyectos con error: ${errorCount}`); // Desglose por estado const openProjects = projectsData.filter(p => p.targetStatus === 'open').length; const closedProjects = projectsData.filter(p => p.targetStatus === 'closed').length; const draftProjects = projectsData.filter(p => p.targetStatus === 'draft').length; console.log('\n📊 Desglose por estado:'); console.log(` 🔵 Abiertos: ${openProjects}`); console.log(` 🏁 Cerrados: ${closedProjects}`); console.log(` 📝 Borradores: ${draftProjects}`); if (errors.length > 0) { console.log('\n📋 Errores encontrados:'); errors.forEach(({ project, error }) => { console.log(` • ${project}: ${error}`); }); } console.log('═'.repeat(60)); if (successCount > 0) { console.log('\n💡 ¡Recarga tu aplicación para ver los proyectos!'); console.log(' Abre http://localhost:3000 en tu navegador\n'); } } // Ejecutar main().catch(error => { console.error('\n❌ ERROR FATAL:', error.message); console.error('\nAsegúrate de que:'); console.error(' 1. El servidor Next.js está corriendo (npm run dev)'); console.error(' 2. Dolibarr está accesible'); console.error(' 3. Las credenciales en .env.local son correctas\n'); process.exit(1); });