feat: add docker-compose configuration for Dolibarr and MariaDB, update project seeding script with task management and project status handling
This commit is contained in:
parent
f1d19bd257
commit
5a9b509b1b
|
|
@ -0,0 +1,64 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:10.6
|
||||
container_name: dolibarr_db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root_password
|
||||
MYSQL_DATABASE: dolibarr
|
||||
MYSQL_USER: dolibarr
|
||||
MYSQL_PASSWORD: dolibarr_password
|
||||
volumes:
|
||||
- mariadb_data:/var/lib/mysql
|
||||
networks:
|
||||
- dolibarr_network
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot_password"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
dolibarr:
|
||||
image: dolibarr/dolibarr:latest
|
||||
container_name: dolibarr_app
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
mariadb:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
# Configuración de base de datos
|
||||
DOLI_DB_HOST: mariadb
|
||||
DOLI_DB_PORT: 3306
|
||||
DOLI_DB_NAME: dolibarr
|
||||
DOLI_DB_USER: dolibarr
|
||||
DOLI_DB_PASSWORD: dolibarr_password
|
||||
|
||||
# Configuración de Dolibarr
|
||||
DOLI_URL_ROOT: http://localhost:8200
|
||||
DOLI_ADMIN_LOGIN: admin
|
||||
DOLI_ADMIN_PASSWORD: p4ssw0rd
|
||||
|
||||
# Habilitar módulos necesarios
|
||||
DOLI_MODULES: modProjet
|
||||
|
||||
# Configuración adicional
|
||||
DOLI_CRON: 0
|
||||
|
||||
volumes:
|
||||
- dolibarr_documents:/var/www/documents
|
||||
- dolibarr_custom:/var/www/html/custom
|
||||
ports:
|
||||
- "8200:80"
|
||||
networks:
|
||||
- dolibarr_network
|
||||
|
||||
volumes:
|
||||
mariadb_data:
|
||||
dolibarr_documents:
|
||||
dolibarr_custom:
|
||||
|
||||
networks:
|
||||
dolibarr_network:
|
||||
driver: bridge
|
||||
|
|
@ -4,6 +4,11 @@
|
|||
* 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
|
||||
*/
|
||||
|
|
@ -12,6 +17,7 @@ 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",
|
||||
|
|
@ -20,15 +26,14 @@ const projectsData = [
|
|||
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),
|
||||
},
|
||||
{
|
||||
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: 90,
|
||||
date_start: Math.floor(new Date('2023-11-01').getTime() / 1000),
|
||||
date_end: Math.floor(new Date('2024-02-28').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",
|
||||
|
|
@ -38,6 +43,14 @@ const projectsData = [
|
|||
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",
|
||||
|
|
@ -47,15 +60,15 @@ const projectsData = [
|
|||
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),
|
||||
},
|
||||
{
|
||||
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: '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",
|
||||
|
|
@ -65,6 +78,15 @@ const projectsData = [
|
|||
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",
|
||||
|
|
@ -74,6 +96,15 @@ const projectsData = [
|
|||
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",
|
||||
|
|
@ -83,24 +114,16 @@ const projectsData = [
|
|||
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),
|
||||
},
|
||||
{
|
||||
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),
|
||||
},
|
||||
{
|
||||
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: '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",
|
||||
|
|
@ -110,6 +133,14 @@ const projectsData = [
|
|||
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",
|
||||
|
|
@ -119,22 +150,173 @@ const projectsData = [
|
|||
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(projectData),
|
||||
body: JSON.stringify(apiData),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
|
|
@ -167,12 +349,88 @@ async function validateProject(projectId) {
|
|||
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) {
|
||||
// Es normal que algunos proyectos no se puedan validar
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
|
|
@ -193,7 +451,7 @@ async function main() {
|
|||
console.log('🔍 Verificando conexión con Next.js...');
|
||||
try {
|
||||
const testResponse = await fetch('http://localhost:3000');
|
||||
if (!testResponse.ok && testResponse.status !== 404) {
|
||||
if (!testResponse.ok && testResponse.status !== 404 && testResponse.status !== 307) {
|
||||
throw new Error('Server not responding');
|
||||
}
|
||||
console.log('✅ Servidor Next.js está corriendo\n');
|
||||
|
|
@ -203,8 +461,30 @@ async function main() {
|
|||
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++) {
|
||||
|
|
@ -215,31 +495,60 @@ async function main() {
|
|||
// Indicador visual del progreso
|
||||
const progressBar = '▓'.repeat(Math.floor(percentage / 10)) + '░'.repeat(10 - Math.floor(percentage / 10));
|
||||
|
||||
console.log(`[${num}/${projectsData.length}] ${project.title}`);
|
||||
// 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 result = await createProject(project);
|
||||
console.log(` ✅ Creado con ID: ${result}`);
|
||||
const projectId = await createProject(project);
|
||||
console.log(` ✅ Proyecto creado con ID: ${projectId}`);
|
||||
|
||||
// Validar proyecto si tiene progreso > 0
|
||||
if (percentage > 0) {
|
||||
const validated = await validateProject(result);
|
||||
// Gestionar estado del proyecto
|
||||
if (project.targetStatus === 'open' || project.targetStatus === 'closed') {
|
||||
const validated = await validateProject(projectId);
|
||||
if (validated) {
|
||||
console.log(` ✓ Validado (estado: abierto)`);
|
||||
} else {
|
||||
console.log(` ⚠ Creado pero no validado (normal para algunos proyectos)`);
|
||||
|
||||
// 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, 500));
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
} catch (error) {
|
||||
console.log(` ❌ Error: ${error.message}`);
|
||||
errors.push({ project: project.title, error: error.message });
|
||||
|
|
@ -253,8 +562,19 @@ async function main() {
|
|||
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 }) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// types/project.ts
|
||||
export type ProjectStatus = '0' | '1' | '2'; // 0: borrador, 1: validado/abierto, 2: cerrado
|
||||
export type ProjectStatus = '0' | '1' | '2'; // 0: borrador, 1: activo, 2: cerrado
|
||||
|
||||
// Interface para los datos crudos que vienen de Dolibarr
|
||||
export interface DolibarrProject {
|
||||
|
|
@ -49,7 +49,7 @@ export const STATUS_CONFIG: Record<ProjectStatus, { label: string; getColorClass
|
|||
getColorClasses: () => "bg-gray-100 text-gray-800 border-gray-200"
|
||||
},
|
||||
'1': {
|
||||
label: "Abierto",
|
||||
label: "Activo",
|
||||
getColorClasses: () => "bg-blue-100 text-blue-800 border-blue-200"
|
||||
},
|
||||
'2': {
|
||||
|
|
|
|||
Loading…
Reference in New Issue