Actualizar server.js

This commit is contained in:
2026-03-10 22:33:07 +00:00
parent c289ea7cc7
commit 40952689cc

147
server.js
View File

@@ -1113,11 +1113,10 @@ app.get("/agenda/requests", authMiddleware, async (req, res) => {
} catch (e) { res.status(500).json({ ok: false }); }
});
// 4. APROBAR CITA (Aquí se establece la duración y se envía WA)
app.post("/agenda/requests/:id/approve", authMiddleware, async (req, res) => {
try {
const { id } = req.params;
const { duration } = req.body; // En minutos
const { duration } = req.body;
const current = await pool.query('SELECT raw_data FROM scraped_services WHERE id=$1 AND owner_id=$2', [id, req.user.accountId]);
if (current.rowCount === 0) return res.status(404).json({ok: false});
@@ -1142,8 +1141,18 @@ app.post("/agenda/requests/:id/approve", authMiddleware, async (req, res) => {
await pool.query("UPDATE scraped_services SET raw_data=$1 WHERE id=$2", [JSON.stringify(updatedRaw), id]);
console.log(`🤖 [PORTAL-CITA] Cita aprobada para ${reqDate}. Disparando eventos...`);
// Disparamos WhatsApp oficial de cita confirmada
await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_date');
// 🚀 DISPARAR ROBOT HOMESERVE (Faltaba esta línea)
const checkHs = await pool.query("SELECT provider FROM scraped_services WHERE id=$1", [id]);
if (checkHs.rows[0]?.provider === 'homeserve') {
console.log(`✅ [PORTAL-CITA] Disparando robot: CITA (${reqDate})`);
triggerHomeServeRobot(req.user.accountId, id, 'date').catch(console.error);
}
res.json({ok: true});
} catch (e) { res.status(500).json({ok: false}); }
});
@@ -1560,9 +1569,6 @@ app.post("/providers/automate/:id", authMiddleware, async (req, res) => {
} catch (e) { res.status(500).json({ ok: false }); }
});
// ==========================================
// 📝 ACTUALIZACIÓN MANUAL (CONEXIÓN ROBOT HOMESERVE)
// ==========================================
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;
@@ -1578,7 +1584,6 @@ app.put('/providers/scraped/:id', authMiddleware, async (req, res) => {
let rawActual = current.rows[0].raw_data || {};
// 1. Capturar valores antiguos y nuevos para comparar
let oldStatus = String(rawActual.status_operativo || "");
let newStatus = String(extra.status_operativo !== undefined ? extra.status_operativo : oldStatus);
@@ -1591,26 +1596,15 @@ app.put('/providers/scraped/:id', authMiddleware, async (req, res) => {
const oldWorkerId = current.rows[0].assigned_to || rawActual.assigned_to;
let finalAssignedTo = assigned_to !== undefined ? (assigned_to === "" ? null : assigned_to) : oldWorkerId;
// 2. Obtener nombre del estado para saber qué regla aplicar
let stName = "";
if (newStatus && newStatus !== "null") {
const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [newStatus]);
stName = (statusQ.rows[0]?.name || "").toLowerCase();
}
console.log(`🤖 [DEBUG HS] Exp: ${id} | Estado: ${stName} | statusChanged: ${statusChanged} | dateChanged: ${dateChanged}`);
console.log(`🤖 [DEBUG ADMIN-PANEL] Exp: ${id} | Estado: '${stName}' | statusChanged: ${statusChanged} | dateChanged: ${dateChanged}`);
// --- DISPARADORES WHATSAPP ---
if (stName.includes('asignado') && finalAssignedTo && statusChanged) {
triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned').catch(console.error);
}
if ((stName.includes('citado') || stName.includes('cita')) && (statusChanged || dateChanged)) {
if (oldDate === "" || oldDate === "null") triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_date').catch(console.error);
else triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_update').catch(console.error);
}
// 3. ACTUALIZAR BASE DE DATOS
// ACTUALIZAR BASE DE DATOS PRIMERO
const updatedRawData = {
...rawActual, ...extra,
"Nombre Cliente": name || rawActual["Nombre Cliente"],
@@ -1625,23 +1619,34 @@ app.put('/providers/scraped/:id', authMiddleware, async (req, res) => {
[JSON.stringify(updatedRawData), finalAssignedTo, id, req.user.accountId]
);
// 4. 🚀 DISPARAR ROBOT HOMESERVE (SÓLO SI ES HOMESERVE)
// Comprobamos si el proveedor es HomeServe antes de mandarlo a la cola
// BANDERAS INTELIGENTES
const isAsignado = stName.includes('asignado');
const isCitado = stName.includes('citado') || stName.includes('cita') || stName.includes('agendado');
const isNoLocalizado = stName.includes('no localizado') || stName.includes('buzon') || stName.includes('contesta');
// --- DISPARADORES WHATSAPP ---
if (statusChanged && isAsignado && finalAssignedTo) {
triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned').catch(console.error);
}
if (isCitado && (statusChanged || dateChanged)) {
if (oldDate === "" || oldDate === "null") triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_date').catch(console.error);
else triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_update').catch(console.error);
}
// 🚀 DISPARAR ROBOT HOMESERVE
const checkHs = await pool.query("SELECT provider FROM scraped_services WHERE id=$1", [id]);
if (checkHs.rows[0]?.provider === 'homeserve') {
// Regla de Asignación
if (statusChanged && stName.includes('asignado') && finalAssignedTo) {
console.log("✅ Enviando ASIGNACIÓN a HomeServe...");
if (statusChanged && isAsignado && finalAssignedTo) {
console.log("✅ [ADMIN] Disparando robot: ASIGNACIÓN");
triggerHomeServeRobot(req.user.accountId, id, 'assign').catch(console.error);
}
// Regla de Cita (Si cambia estado a citado O cambia la fecha estando citado)
if ((stName.includes('citado') || stName.includes('cita')) && (statusChanged || dateChanged)) {
if (newDate && newDate !== "null") {
console.log(`✅ Enviando CITA (${newDate}) a HomeServe...`);
triggerHomeServeRobot(req.user.accountId, id, 'date').catch(console.error);
}
if (isCitado && (statusChanged || dateChanged) && newDate !== "" && newDate !== "null") {
console.log(`✅ [ADMIN] Disparando robot: CITA (${newDate})`);
triggerHomeServeRobot(req.user.accountId, id, 'date').catch(console.error);
}
if (isNoLocalizado && statusChanged) {
console.log(`✅ [ADMIN] Disparando robot: NO LOCALIZADO`);
triggerHomeServeRobot(req.user.accountId, id, 'notfound').catch(console.error);
}
}
@@ -1710,9 +1715,6 @@ app.get("/services/active", authMiddleware, async (req, res) => {
}
});
// AÑADIDO: Ruta para fijar la cita o el estado operativo (CORREGIDA PARA NO PERDER LA FECHA)
// AÑADIDO: Ruta para fijar la cita o el estado operativo (CORREGIDA PARA NO PERDER LA FECHA Y ENVIAR BIEN EL WHATSAPP)
// AÑADIDO: Ruta para fijar la cita o el estado operativo (CON ENVÍO WA EN SEGUNDO PLANO)
app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => {
try {
const { id } = req.params;
@@ -1723,30 +1725,29 @@ app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => {
const rawActual = current.rows[0].raw_data || {};
// --- MEJORA: MANTENER FECHA SI NO SE ENVÍA ---
// FIX CRÍTICO: Si la app no envía estado, mantenemos el que tenía el expediente para no borrarlo
if (status_operativo === undefined || status_operativo === "") {
status_operativo = rawActual.status_operativo || null;
}
let newDate = (date !== undefined) ? date : (rawActual.scheduled_date || "");
let newTime = (time !== undefined) ? time : (rawActual.scheduled_time || "");
let finalAssignedTo = current.rows[0].assigned_to;
if (status_operativo === "") status_operativo = null;
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();
}
// --- REGLA ESTRICTA: BORRAR FECHAS SOLO SI SE ANULA O RETROCEDE A PENDIENTE ---
if (stName.includes('pendiente') || stName.includes('desasignado') || stName.includes('asignado') || stName.includes('anulado') || stName.includes('esperando')) {
if (stName.includes('pendiente') || stName.includes('desasignado') || stName.includes('anulado')) {
newDate = "";
newTime = "";
}
// 1. GUARDAMOS EN BBDD RÁPIDAMENTE
// GUARDAR EN BBDD
const updatedRawData = {
...rawActual,
...extra,
...rawActual, ...extra,
"scheduled_date": newDate,
"scheduled_time": newTime,
"status_operativo": status_operativo
@@ -1756,75 +1757,63 @@ app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => {
[JSON.stringify(updatedRawData), finalAssignedTo, id, req.user.accountId]
);
// --- INICIO TRAZABILIDAD ---
let logDetalle = `Estado modificado a: ${stName.toUpperCase() || 'MODIFICADO'}.`;
if (newDate) logDetalle += ` Cita para el ${newDate} a las ${newTime}.`;
await registrarMovimiento(id, req.user.sub, "Actualización desde App", logDetalle);
// 🚀 RESPONDEMOS AL NAVEGADOR INMEDIATAMENTE (La ventana se cierra al instante)
res.json({ ok: true });
// 2. MAGIA: TAREAS EN SEGUNDO PLANO (El WhatsApp tarda lo que tenga que tardar, sin bloquear)
// TAREAS EN SEGUNDO PLANO
(async () => {
try {
// 👇 Detectamos cambios en la fecha y el estado para no hacer disparos falsos
const statusChanged = status_operativo !== rawActual.status_operativo;
const statusChanged = String(status_operativo) !== String(rawActual.status_operativo);
const oldDate = rawActual.scheduled_date || "";
const dateChanged = newDate !== "" && newDate !== oldDate;
if (statusChanged && stName.includes('asignado')) {
triggerHomeServeRobot(req.user.accountId, id, 'assign').catch(console.error);
console.log(`🤖 [DEBUG APP-OP] Exp: ${id} | Estado: '${stName}' | statusChanged: ${statusChanged} | dateChanged: ${dateChanged}`);
const checkHs = await pool.query("SELECT provider FROM scraped_services WHERE id=$1", [id]);
const isHomeServe = checkHs.rows[0]?.provider === 'homeserve';
const isAsignado = stName.includes('asignado');
const isCitado = stName.includes('citado') || stName.includes('cita') || stName.includes('agendado');
// --- ASIGNADO ---
if (statusChanged && isAsignado) {
if (isHomeServe) triggerHomeServeRobot(req.user.accountId, id, 'assign').catch(console.error);
const waEnviadoExito = await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned');
if (waEnviadoExito) {
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) {
updatedRawData.status_operativo = estadoEsperando.rows[0].id;
await pool.query('UPDATE scraped_services SET raw_data = $1 WHERE id = $2 AND owner_id = $3', [JSON.stringify(updatedRawData), id, req.user.accountId]);
await pool.query('UPDATE scraped_services SET raw_data = $1 WHERE id = $2', [JSON.stringify(updatedRawData), id]);
}
}
}
else if (stName.includes('esperando') || stName.includes('pendiente de cita')) {
await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned');
}
else if (stName.includes('pendiente de asignar') || stName.includes('desasignado')) {
const oldWorkerId = finalAssignedTo || rawActual.assigned_to;
if (oldWorkerId) {
const workerQ = await pool.query("SELECT full_name, phone FROM users WHERE id=$1", [oldWorkerId]);
if (workerQ.rowCount > 0) {
const w = workerQ.rows[0];
const ref = rawActual.service_ref || rawActual["Referencia"] || id;
const msg = `⚠️ *AVISO DE DESASIGNACIÓN*\n\nHola ${w.full_name}, se te ha retirado el expediente *#${ref}*.\n\nYa no tienes que atender este servicio.`;
sendWhatsAppAuto(w.phone, msg, `cliente_${req.user.accountId}`, false).catch(console.error);
}
// --- CITADO (CONFIRMAR CITA) ---
else if (isCitado && (statusChanged || dateChanged)) {
if (isHomeServe && newDate !== "") {
console.log(`✅ [APP-OP] Disparando robot: CITA (${newDate})`);
triggerHomeServeRobot(req.user.accountId, id, 'date').catch(console.error);
}
updatedRawData.assigned_to = null;
updatedRawData.assigned_to_name = null;
await pool.query('UPDATE scraped_services SET raw_data = $1, assigned_to = null WHERE id = $2 AND owner_id = $3', [JSON.stringify(updatedRawData), id, req.user.accountId]);
}
// 🟢 NUEVO DISPARADOR ROBOT CITA (Se lanza si cambia estado O si cambia fecha)
else if ((statusChanged && stName.includes('citado') && newDate !== "") || (dateChanged && stName.includes('citado'))) {
triggerHomeServeRobot(req.user.accountId, id, 'date').catch(console.error);
if (oldDate === "") await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_date');
else if (oldDate !== newDate) await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_update');
}
// --- CAMINO / FINALIZADO ---
else if (stName.includes('camino')) {
await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_onway');
} else if (stName.includes('finalizado') || stName.includes('terminado')) {
if (!skip_survey) {
await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_survey');
}
if (!skip_survey) await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_survey');
}
} catch (errBckg) {
console.error("Error en tareas de fondo:", errBckg);
}
})(); // El paréntesis final ejecuta esto en las sombras
})();
} catch (e) {
console.error("Error agendando cita:", e);
// Solo enviamos error si no hemos respondido ya
if (!res.headersSent) res.status(500).json({ ok: false });
}
});