1 Commits

View File

@@ -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='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 -- 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 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; 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'; const mainStatus = isFinal ? 'archived' : 'imported';
await pool.query( 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`, `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] [JSON.stringify(updatedRawData), finalAssignedTo, isUrgentFinal, id, req.user.accountId, mainStatus, isFinal]
); );
// BANDERAS INTELIGENTES // BANDERAS INTELIGENTES
@@ -4778,12 +4780,15 @@ function calculateScore(services) {
let scoreCierre = 0; let scoreCierre = 0;
if (closedLast30Days.length > 0) { if (closedLast30Days.length > 0) {
let totalDaysToClose = 0; let totalDaysToClose = 0;
let closedWithTimestamp = 0;
closedLast30Days.forEach(s => { 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 created = new Date(s.created_at);
const closed = new Date(); // Estimamos el cierre en el ciclo actual 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)); totalDaysToClose += Math.max(1, (closed - created) / (1000 * 60 * 60 * 24));
closedWithTimestamp++;
}); });
const avgCloseDays = totalDaysToClose / closedLast30Days.length; const avgCloseDays = closedWithTimestamp > 0 ? (totalDaysToClose / closedWithTimestamp) : 999;
if (avgCloseDays <= 2) scoreCierre = 30; if (avgCloseDays <= 2) scoreCierre = 30;
else if (avgCloseDays >= 14) scoreCierre = 0; else if (avgCloseDays >= 14) scoreCierre = 0;
@@ -4863,7 +4868,7 @@ app.get("/ranking", authMiddleware, async (req, res) => {
try { try {
// CORRECCIÓN: Hemos quitado "updated_at" de aquí para evitar que la base de datos se queje // CORRECCIÓN: Hemos quitado "updated_at" de aquí para evitar que la base de datos se queje
const q = await pool.query(` 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 (SELECT is_final FROM service_statuses WHERE id::text = raw_data->>'status_operativo') as is_final
FROM scraped_services FROM scraped_services
WHERE assigned_to = $1 WHERE assigned_to = $1