diff --git a/server.js b/server.js index 8e1d2db..e46a823 100644 --- a/server.js +++ b/server.js @@ -863,7 +863,7 @@ app.post("/providers/import/:id", authMiddleware, async (req, res) => { } finally { client.release(); } }); -// AÑADIDO: CAPTURA COMPLETA DE DATOS (...EXTRA) Y REGLA WHATSAPP MANUAL +// AÑADIDO Y BLINDADO: CAPTURA COMPLETA DE DATOS Y REGLA WHATSAPP MANUAL app.put('/providers/scraped/:id', authMiddleware, async (req, res) => { const { id } = req.params; let { automation_status, status, name, phone, address, cp, description, guild_id, assigned_to, assigned_to_name, internal_notes, client_notes, is_urgent, ...extra } = req.body; @@ -879,36 +879,31 @@ app.put('/providers/scraped/:id', authMiddleware, async (req, res) => { return res.json({ ok: true }); } - const current = await pool.query('SELECT raw_data, assigned_to FROM scraped_services WHERE id = $1 AND owner_id = $2', [id, req.user.accountId]); + const current = await pool.query('SELECT raw_data, assigned_to, status FROM scraped_services WHERE id = $1 AND owner_id = $2', [id, req.user.accountId]); if (current.rows.length === 0) return res.status(404).json({ error: 'No encontrado' }); - // --- NUEVA LÓGICA: DETECCIÓN DE ASIGNACIÓN MANUAL --- - const oldStatus = current.rows[0].raw_data.status_operativo; + // --- BLINDAJE DE ESTADO VACÍO --- + let oldStatus = current.rows[0].raw_data.status_operativo || null; let newStatus = extra.status_operativo || oldStatus; + if (newStatus === "") newStatus = null; - // Si el estado operativo ha cambiado... + // --- REGLA WA MANUAL --- if (newStatus && newStatus !== oldStatus) { const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [newStatus]); const stName = (statusQ.rows[0]?.name || "").toLowerCase(); - // Si lo has puesto manual a "Asignado" if (stName.includes('asignado')) { - // Disparamos WA y esperamos a ver si tiene éxito const waEnviadoExito = await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned'); - if (waEnviadoExito) { - // Si llega bien, forzamos la base de datos para que avance a Esperando al Cliente const estadoEsperando = await pool.query("SELECT id FROM service_statuses WHERE owner_id=$1 AND name='Esperando al Cliente' LIMIT 1", [req.user.accountId]); if(estadoEsperando.rowCount > 0) { newStatus = estadoEsperando.rows[0].id; - extra.status_operativo = newStatus; // Actualizamos la variable para que se guarde + extra.status_operativo = newStatus; } } } } - // ---------------------------------------------------- - // Fusión total: conservamos los extra para no perder información const updatedRawData = { ...current.rows[0].raw_data, ...extra, @@ -926,15 +921,22 @@ app.put('/providers/scraped/:id', authMiddleware, async (req, res) => { "status_operativo": newStatus }; - // ACTUALIZAMOS (Respetando el nuevo estado operativo y el nuevo operario asignado) + // --- BLINDAJE DE OPERARIO VACÍO (Evita el error 500) --- + let finalAssignedTo = assigned_to; + if (finalAssignedTo === "") finalAssignedTo = null; + else if (!finalAssignedTo) finalAssignedTo = current.rows[0].assigned_to; + + // Respetamos el estatus maestro original para que no desaparezca del tablero + let currentDbStatus = current.rows[0].status; + await pool.query( `UPDATE scraped_services SET raw_data = $1, - status = 'pending', - is_urgent = $2, - assigned_to = $3 - WHERE id = $4 AND owner_id = $5`, - [JSON.stringify(updatedRawData), is_urgent || false, assigned_to || current.rows[0].assigned_to, id, req.user.accountId] + status = $2, + is_urgent = $3, + assigned_to = $4 + WHERE id = $5 AND owner_id = $6`, + [JSON.stringify(updatedRawData), currentDbStatus, is_urgent || false, finalAssignedTo, id, req.user.accountId] ); res.json({ ok: true }); } catch (error) { @@ -976,7 +978,6 @@ app.get("/services/active", authMiddleware, async (req, res) => { app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => { try { const { id } = req.params; - // OJO: status_operativo con 'let' para que la IA pueda cambiarlo let { date, time, status_operativo, ...extra } = req.body; const current = await pool.query('SELECT raw_data FROM scraped_services WHERE id = $1 AND owner_id = $2', [id, req.user.accountId]); @@ -987,14 +988,19 @@ app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => { const newDate = date || ""; const newTime = time || ""; - const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [status_operativo]); - const stName = statusQ.rows[0]?.name.toLowerCase() || ""; + // BLINDAJE DE ESTADO VACÍO + if (status_operativo === "") status_operativo = null; - // --- REGLA ESTRICTA: ASIGNADO -> WA -> ESPERANDO AL CLIENTE --- + let stName = ""; + if (status_operativo) { + const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [status_operativo]); + stName = (statusQ.rows[0]?.name || "").toLowerCase(); + } + + // --- MOTOR DE EVENTOS --- if (stName.includes('asignado')) { const waEnviadoExito = await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned'); if (waEnviadoExito) { - // Si el WA se envía correctamente, forzamos el salto a "Esperando al Cliente" const estadoEsperando = await pool.query("SELECT id FROM service_statuses WHERE owner_id=$1 AND name='Esperando al Cliente' LIMIT 1", [req.user.accountId]); if (estadoEsperando.rowCount > 0) { status_operativo = estadoEsperando.rows[0].id; @@ -1009,7 +1015,6 @@ app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => { await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_survey'); } - // Finalmente, guardamos (con el estado original, o con el estado "Esperando" si el WA llegó) const updatedRawData = { ...current.rows[0].raw_data, ...extra, "scheduled_date": newDate, "scheduled_time": newTime, "status_operativo": status_operativo }; await pool.query('UPDATE scraped_services SET raw_data = $1 WHERE id = $2 AND owner_id = $3', [JSON.stringify(updatedRawData), id, req.user.accountId]);