From b07868a207b76742c574051a91622676f4a433ef Mon Sep 17 00:00:00 2001 From: marsalva Date: Sun, 27 Sep 2026 21:22:18 +0000 Subject: [PATCH] Fix: ranking usa fecha real de cierre (closed_at) en vez de "ahora" --- server.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/server.js b/server.js index b1b5e49..1db4c56 100644 --- a/server.js +++ b/server.js @@ -1058,6 +1058,8 @@ ALTER TABLE protection_subscriptions ALTER COLUMN status SET DEFAULT 'pendiente_ IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='scraped_services' AND column_name='automation_status') THEN ALTER TABLE scraped_services ADD COLUMN automation_status TEXT DEFAULT 'manual'; END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='scraped_services' AND column_name='closed_at') THEN ALTER TABLE scraped_services ADD COLUMN closed_at TIMESTAMP; END IF; + -- AÑADIDO: Token mágico para el Portal del Cliente IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='clients' AND column_name='portal_token') THEN ALTER TABLE clients ADD COLUMN portal_token TEXT UNIQUE; @@ -3848,8 +3850,8 @@ app.put('/providers/scraped/:id', authMiddleware, async (req, res) => { const mainStatus = isFinal ? 'archived' : 'imported'; await pool.query( - `UPDATE scraped_services SET raw_data = $1, assigned_to = $2, is_urgent = $3, status = $6 WHERE id = $4 AND owner_id = $5`, - [JSON.stringify(updatedRawData), finalAssignedTo, isUrgentFinal, id, req.user.accountId, mainStatus] + `UPDATE scraped_services SET raw_data = $1, assigned_to = $2, is_urgent = $3, status = $6, closed_at = CASE WHEN $7 THEN COALESCE(closed_at, NOW()) ELSE NULL END WHERE id = $4 AND owner_id = $5`, + [JSON.stringify(updatedRawData), finalAssignedTo, isUrgentFinal, id, req.user.accountId, mainStatus, isFinal] ); // BANDERAS INTELIGENTES @@ -4778,12 +4780,15 @@ function calculateScore(services) { let scoreCierre = 0; if (closedLast30Days.length > 0) { let totalDaysToClose = 0; - closedLast30Days.forEach(s => { - const created = new Date(s.created_at); - const closed = new Date(); // Estimamos el cierre en el ciclo actual - totalDaysToClose += Math.max(1, (closed - created) / (1000 * 60 * 60 * 24)); - }); - const avgCloseDays = totalDaysToClose / closedLast30Days.length; + let closedWithTimestamp = 0; + closedLast30Days.forEach(s => { + if (!s.closed_at) return; // 🟢 FIX: sin fecha real de cierre (servicio cerrado antes de esta corrección), no lo contamos + const created = new Date(s.created_at); + const closed = new Date(s.closed_at); // 🟢 FIX: usamos la fecha real de cierre, no "ahora" + totalDaysToClose += Math.max(1, (closed - created) / (1000 * 60 * 60 * 24)); + closedWithTimestamp++; + }); + const avgCloseDays = closedWithTimestamp > 0 ? (totalDaysToClose / closedWithTimestamp) : 999; if (avgCloseDays <= 2) scoreCierre = 30; else if (avgCloseDays >= 14) scoreCierre = 0; @@ -4863,7 +4868,7 @@ app.get("/ranking", authMiddleware, async (req, res) => { try { // CORRECCIÓN: Hemos quitado "updated_at" de aquí para evitar que la base de datos se queje const q = await pool.query(` - SELECT id, created_at, raw_data, + SELECT id, created_at, closed_at, raw_data, (SELECT is_final FROM service_statuses WHERE id::text = raw_data->>'status_operativo') as is_final FROM scraped_services WHERE assigned_to = $1