Actualizar server.js
This commit is contained in:
147
server.js
147
server.js
@@ -1113,11 +1113,10 @@ app.get("/agenda/requests", authMiddleware, async (req, res) => {
|
|||||||
} catch (e) { res.status(500).json({ ok: false }); }
|
} 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) => {
|
app.post("/agenda/requests/:id/approve", authMiddleware, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
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]);
|
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});
|
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]);
|
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
|
// Disparamos WhatsApp oficial de cita confirmada
|
||||||
await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_date');
|
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});
|
res.json({ok: true});
|
||||||
} catch (e) { res.status(500).json({ok: false}); }
|
} 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 }); }
|
} catch (e) { res.status(500).json({ ok: false }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// 📝 ACTUALIZACIÓN MANUAL (CONEXIÓN ROBOT HOMESERVE)
|
|
||||||
// ==========================================
|
|
||||||
app.put('/providers/scraped/:id', authMiddleware, async (req, res) => {
|
app.put('/providers/scraped/:id', authMiddleware, async (req, res) => {
|
||||||
const { id } = req.params;
|
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;
|
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 || {};
|
let rawActual = current.rows[0].raw_data || {};
|
||||||
|
|
||||||
// 1. Capturar valores antiguos y nuevos para comparar
|
|
||||||
let oldStatus = String(rawActual.status_operativo || "");
|
let oldStatus = String(rawActual.status_operativo || "");
|
||||||
let newStatus = String(extra.status_operativo !== undefined ? extra.status_operativo : oldStatus);
|
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;
|
const oldWorkerId = current.rows[0].assigned_to || rawActual.assigned_to;
|
||||||
let finalAssignedTo = assigned_to !== undefined ? (assigned_to === "" ? null : assigned_to) : oldWorkerId;
|
let finalAssignedTo = assigned_to !== undefined ? (assigned_to === "" ? null : assigned_to) : oldWorkerId;
|
||||||
|
|
||||||
// 2. Obtener nombre del estado para saber qué regla aplicar
|
|
||||||
let stName = "";
|
let stName = "";
|
||||||
if (newStatus && newStatus !== "null") {
|
if (newStatus && newStatus !== "null") {
|
||||||
const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [newStatus]);
|
const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [newStatus]);
|
||||||
stName = (statusQ.rows[0]?.name || "").toLowerCase();
|
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 ---
|
// ACTUALIZAR BASE DE DATOS PRIMERO
|
||||||
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
|
|
||||||
const updatedRawData = {
|
const updatedRawData = {
|
||||||
...rawActual, ...extra,
|
...rawActual, ...extra,
|
||||||
"Nombre Cliente": name || rawActual["Nombre Cliente"],
|
"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]
|
[JSON.stringify(updatedRawData), finalAssignedTo, id, req.user.accountId]
|
||||||
);
|
);
|
||||||
|
|
||||||
// 4. 🚀 DISPARAR ROBOT HOMESERVE (SÓLO SI ES HOMESERVE)
|
// BANDERAS INTELIGENTES
|
||||||
// Comprobamos si el proveedor es HomeServe antes de mandarlo a la cola
|
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]);
|
const checkHs = await pool.query("SELECT provider FROM scraped_services WHERE id=$1", [id]);
|
||||||
if (checkHs.rows[0]?.provider === 'homeserve') {
|
if (checkHs.rows[0]?.provider === 'homeserve') {
|
||||||
|
if (statusChanged && isAsignado && finalAssignedTo) {
|
||||||
// Regla de Asignación
|
console.log("✅ [ADMIN] Disparando robot: ASIGNACIÓN");
|
||||||
if (statusChanged && stName.includes('asignado') && finalAssignedTo) {
|
|
||||||
console.log("✅ Enviando ASIGNACIÓN a HomeServe...");
|
|
||||||
triggerHomeServeRobot(req.user.accountId, id, 'assign').catch(console.error);
|
triggerHomeServeRobot(req.user.accountId, id, 'assign').catch(console.error);
|
||||||
}
|
}
|
||||||
|
if (isCitado && (statusChanged || dateChanged) && newDate !== "" && newDate !== "null") {
|
||||||
// Regla de Cita (Si cambia estado a citado O cambia la fecha estando citado)
|
console.log(`✅ [ADMIN] Disparando robot: CITA (${newDate})`);
|
||||||
if ((stName.includes('citado') || stName.includes('cita')) && (statusChanged || dateChanged)) {
|
triggerHomeServeRobot(req.user.accountId, id, 'date').catch(console.error);
|
||||||
if (newDate && newDate !== "null") {
|
}
|
||||||
console.log(`✅ Enviando CITA (${newDate}) a HomeServe...`);
|
if (isNoLocalizado && statusChanged) {
|
||||||
triggerHomeServeRobot(req.user.accountId, id, 'date').catch(console.error);
|
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) => {
|
app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
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 || {};
|
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 newDate = (date !== undefined) ? date : (rawActual.scheduled_date || "");
|
||||||
let newTime = (time !== undefined) ? time : (rawActual.scheduled_time || "");
|
let newTime = (time !== undefined) ? time : (rawActual.scheduled_time || "");
|
||||||
|
|
||||||
let finalAssignedTo = current.rows[0].assigned_to;
|
let finalAssignedTo = current.rows[0].assigned_to;
|
||||||
|
|
||||||
if (status_operativo === "") status_operativo = null;
|
|
||||||
|
|
||||||
let stName = "";
|
let stName = "";
|
||||||
if (status_operativo) {
|
if (status_operativo) {
|
||||||
const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [status_operativo]);
|
const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [status_operativo]);
|
||||||
stName = (statusQ.rows[0]?.name || "").toLowerCase();
|
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('anulado')) {
|
||||||
if (stName.includes('pendiente') || stName.includes('desasignado') || stName.includes('asignado') || stName.includes('anulado') || stName.includes('esperando')) {
|
|
||||||
newDate = "";
|
newDate = "";
|
||||||
newTime = "";
|
newTime = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. GUARDAMOS EN BBDD RÁPIDAMENTE
|
// GUARDAR EN BBDD
|
||||||
const updatedRawData = {
|
const updatedRawData = {
|
||||||
...rawActual,
|
...rawActual, ...extra,
|
||||||
...extra,
|
|
||||||
"scheduled_date": newDate,
|
"scheduled_date": newDate,
|
||||||
"scheduled_time": newTime,
|
"scheduled_time": newTime,
|
||||||
"status_operativo": status_operativo
|
"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]
|
[JSON.stringify(updatedRawData), finalAssignedTo, id, req.user.accountId]
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- INICIO TRAZABILIDAD ---
|
|
||||||
let logDetalle = `Estado modificado a: ${stName.toUpperCase() || 'MODIFICADO'}.`;
|
let logDetalle = `Estado modificado a: ${stName.toUpperCase() || 'MODIFICADO'}.`;
|
||||||
if (newDate) logDetalle += ` Cita para el ${newDate} a las ${newTime}.`;
|
if (newDate) logDetalle += ` Cita para el ${newDate} a las ${newTime}.`;
|
||||||
await registrarMovimiento(id, req.user.sub, "Actualización desde App", logDetalle);
|
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 });
|
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 () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
// 👇 Detectamos cambios en la fecha y el estado para no hacer disparos falsos
|
const statusChanged = String(status_operativo) !== String(rawActual.status_operativo);
|
||||||
const statusChanged = status_operativo !== rawActual.status_operativo;
|
|
||||||
const oldDate = rawActual.scheduled_date || "";
|
const oldDate = rawActual.scheduled_date || "";
|
||||||
const dateChanged = newDate !== "" && newDate !== oldDate;
|
const dateChanged = newDate !== "" && newDate !== oldDate;
|
||||||
|
|
||||||
if (statusChanged && stName.includes('asignado')) {
|
console.log(`🤖 [DEBUG APP-OP] Exp: ${id} | Estado: '${stName}' | statusChanged: ${statusChanged} | dateChanged: ${dateChanged}`);
|
||||||
triggerHomeServeRobot(req.user.accountId, id, 'assign').catch(console.error);
|
|
||||||
|
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');
|
const waEnviadoExito = await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned');
|
||||||
if (waEnviadoExito) {
|
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]);
|
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) {
|
if (estadoEsperando.rowCount > 0) {
|
||||||
updatedRawData.status_operativo = estadoEsperando.rows[0].id;
|
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')) {
|
// --- CITADO (CONFIRMAR CITA) ---
|
||||||
await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned');
|
else if (isCitado && (statusChanged || dateChanged)) {
|
||||||
}
|
|
||||||
else if (stName.includes('pendiente de asignar') || stName.includes('desasignado')) {
|
if (isHomeServe && newDate !== "") {
|
||||||
const oldWorkerId = finalAssignedTo || rawActual.assigned_to;
|
console.log(`✅ [APP-OP] Disparando robot: CITA (${newDate})`);
|
||||||
if (oldWorkerId) {
|
triggerHomeServeRobot(req.user.accountId, id, 'date').catch(console.error);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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');
|
if (oldDate === "") await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_date');
|
||||||
else if (oldDate !== newDate) await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_update');
|
else if (oldDate !== newDate) await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_update');
|
||||||
}
|
}
|
||||||
|
// --- CAMINO / FINALIZADO ---
|
||||||
else if (stName.includes('camino')) {
|
else if (stName.includes('camino')) {
|
||||||
await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_onway');
|
await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_onway');
|
||||||
} else if (stName.includes('finalizado') || stName.includes('terminado')) {
|
} else if (stName.includes('finalizado') || stName.includes('terminado')) {
|
||||||
if (!skip_survey) {
|
if (!skip_survey) await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_survey');
|
||||||
await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_survey');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (errBckg) {
|
} catch (errBckg) {
|
||||||
console.error("Error en tareas de fondo:", errBckg);
|
console.error("Error en tareas de fondo:", errBckg);
|
||||||
}
|
}
|
||||||
})(); // El paréntesis final ejecuta esto en las sombras
|
})();
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error agendando cita:", e);
|
console.error("Error agendando cita:", e);
|
||||||
// Solo enviamos error si no hemos respondido ya
|
|
||||||
if (!res.headersSent) res.status(500).json({ ok: false });
|
if (!res.headersSent) res.status(500).json({ ok: false });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user