Actualizar server.js
This commit is contained in:
72
server.js
72
server.js
@@ -856,15 +856,53 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
||||
const esPrimerMensaje = historialChat.length === 0;
|
||||
|
||||
let agendaOcupadaTexto = "El técnico tiene la agenda libre en horario laboral.";
|
||||
let agendaOcupadaTexto = "El técnico tiene la agenda libre en horario laboral.";
|
||||
if (datosExpediente.worker_id) {
|
||||
const agendaQ = await pool.query("SELECT raw_data->>'scheduled_date' as date, raw_data->>'scheduled_time' as time, raw_data->>'Población' as pob FROM scraped_services WHERE assigned_to = $1 AND raw_data->>'scheduled_date' >= CURRENT_DATE::text AND status != 'archived' AND id != $2 ORDER BY date ASC, time ASC", [datosExpediente.worker_id, datosExpediente.dbId]);
|
||||
// 🛑 AÑADIDO: Consulta avanzada que lee Duraciones, Bloqueos y Citas Pendientes
|
||||
const agendaQ = await pool.query(`
|
||||
SELECT
|
||||
COALESCE(NULLIF(raw_data->>'scheduled_date', ''), raw_data->>'requested_date') as date,
|
||||
COALESCE(NULLIF(raw_data->>'scheduled_time', ''), raw_data->>'requested_time') as time,
|
||||
raw_data->>'duration_minutes' as duration,
|
||||
raw_data->>'Población' as pob,
|
||||
provider
|
||||
FROM scraped_services
|
||||
WHERE assigned_to = $1
|
||||
AND status != 'archived'
|
||||
AND id != $2
|
||||
AND (
|
||||
(raw_data->>'scheduled_date' IS NOT NULL AND raw_data->>'scheduled_date' >= CURRENT_DATE::text)
|
||||
OR
|
||||
(raw_data->>'appointment_status' = 'pending' AND raw_data->>'requested_date' >= CURRENT_DATE::text)
|
||||
)
|
||||
ORDER BY date ASC, time ASC
|
||||
`, [datosExpediente.worker_id, datosExpediente.dbId]);
|
||||
|
||||
if (agendaQ.rowCount > 0) {
|
||||
const ocupaciones = {};
|
||||
agendaQ.rows.forEach(r => { if(r.date && r.time) { if(!ocupaciones[r.date]) ocupaciones[r.date] = []; ocupaciones[r.date].push(`${r.time} (en ${r.pob || 'otra zona'})`); } });
|
||||
const lineas = Object.keys(ocupaciones).map(d => `- Día ${d}: Ocupado a las ${ocupaciones[d].join(", ")}`);
|
||||
agendaQ.rows.forEach(r => {
|
||||
if(r.date && r.time && r.time.includes(':')) {
|
||||
if(!ocupaciones[r.date]) ocupaciones[r.date] = [];
|
||||
|
||||
// Calculamos la hora de fin exacta sumando la duración (Ej: 60, 120, 180 min)
|
||||
let [h, m] = r.time.split(':').map(Number);
|
||||
let dur = parseInt(r.duration || 60);
|
||||
let endMin = (h * 60 + m) + dur;
|
||||
let endH = String(Math.floor(endMin / 60) % 24).padStart(2, '0');
|
||||
let endM = String(endMin % 60).padStart(2, '0');
|
||||
|
||||
let tipo = r.provider === 'SYSTEM_BLOCK' ? 'BLOQUEO/AUSENCIA' : 'CITA';
|
||||
let lugar = r.pob || 'Otra zona';
|
||||
|
||||
ocupaciones[r.date].push(`De ${r.time} a ${endH}:${endM} (${tipo} en ${lugar})`);
|
||||
}
|
||||
});
|
||||
|
||||
const lineas = Object.keys(ocupaciones).map(d => `- Día ${d}:\n * ${ocupaciones[d].join("\n * ")}`);
|
||||
if(lineas.length > 0) {
|
||||
agendaOcupadaTexto = "Citas actuales:\n " + lineas.join("\n ") +
|
||||
"\n 👉 IMPORTANTE: Todas las demás horas (09:00, 11:00, 12:00, etc.) ESTÁN TOTALMENTE LIBRES. Ofrécelas sin miedo.";
|
||||
agendaOcupadaTexto = "Ocupaciones actuales del técnico (Citas confirmadas, pendientes y bloqueos):\n" + lineas.join("\n") +
|
||||
"\n\n👉 IMPORTANTE: Todas las horas que NO se solapen con esos tramos exactos ESTÁN LIBRES." +
|
||||
"\n🚨 REGLA LOGÍSTICA ESTRICTA: El técnico necesita tiempo para viajar. Si el cliente actual es de una localidad distinta a la cita anterior o posterior (ej: Algeciras vs La Línea), ES OBLIGATORIO dejar un margen de al menos 45-60 minutos de viaje entre el final de una cita y el inicio de la siguiente. NUNCA ofrezcas horas pegadas si hay desplazamiento.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -891,7 +929,7 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
||||
tramoConfirmado = 'una hora por confirmar';
|
||||
}
|
||||
|
||||
// 🛑 NUEVO: MÁQUINA DEL TIEMPO (Saber si la cita ya pasó)
|
||||
// 🛑 AÑADIDO: MÁQUINA DEL TIEMPO (Saber si la cita ya pasó)
|
||||
let citaYaPaso = false;
|
||||
if (tieneCitaConfirmada && datosExpediente.cita) {
|
||||
const hoyTime = new Date().setHours(0,0,0,0);
|
||||
@@ -900,10 +938,8 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
||||
if (citaTime < hoyTime) citaYaPaso = true;
|
||||
}
|
||||
|
||||
// 🛑 NUEVO: DETECTOR DE ESTADO FINALIZADO
|
||||
// 🛑 AÑADIDO: DETECTOR DE ESTADO FINALIZADO Y COMPAÑÍA
|
||||
const esEstadoFinal = datosExpediente.estado && (datosExpediente.estado.toLowerCase().includes('finalizado') || datosExpediente.estado.toLowerCase().includes('terminado') || datosExpediente.estado.toLowerCase().includes('anulado'));
|
||||
|
||||
// 🛑 NUEVO: DETECTOR DE SEGURO VS PARTICULAR
|
||||
const nombreCia = datosExpediente.compania || "su Aseguradora";
|
||||
const esSeguro = !nombreCia.toLowerCase().includes('particular');
|
||||
|
||||
@@ -911,24 +947,24 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
||||
|
||||
if (esEstadoFinal) {
|
||||
if (esSeguro) {
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: SERVICIO CERRADO POR EL TÉCNICO.\nTU ÚNICO OBJETIVO: Informar al cliente que el informe ya ha sido enviado a ${nombreCia} y que estamos a la espera de que ellos nos den respuesta o autorización para continuar los trabajos.\nPROHIBICIÓN ABSOLUTA: NO intentes agendar cita ni dar horas. No asumas que la reparación está 100% terminada.`;
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: SERVICIO CERRADO. Informa al cliente que el informe está enviado a ${nombreCia} y esperamos respuesta. NO AGENDES NADA.`;
|
||||
} else {
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: SERVICIO FINALIZADO O ANULADO.\nTU ÚNICO OBJETIVO: Despedirte amablemente o dar soporte post-servicio si el cliente particular tiene alguna duda.\nPROHIBICIÓN ABSOLUTA: NO intentes agendar cita ni dar horas. El trabajo ya se ha terminado.`;
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: SERVICIO CERRADO. Despídete o da soporte post-servicio. NO AGENDES NADA.`;
|
||||
}
|
||||
} else if (citaYaPaso) {
|
||||
if (esSeguro) {
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: LA CITA YA HA PASADO.\n📅 La cita fue el día: ${datosExpediente.cita}.\nTU ÚNICO OBJETIVO: Informar que el técnico ya acudió a la visita y que actualmente estamos tramitando el informe con ${nombreCia}, a la espera de sus instrucciones.\nPROHIBICIÓN ABSOLUTA: No hables en futuro ni propongas más citas.`;
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: LA CITA YA PASÓ (${datosExpediente.cita}). Informa que estamos tramitando el informe con ${nombreCia}. NO AGENDES NADA.`;
|
||||
} else {
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: LA CITA YA HA PASADO.\n📅 La cita fue el día: ${datosExpediente.cita}.\nTU ÚNICO OBJETIVO: Preguntar amablemente al cliente particular si el técnico acudió y si el problema quedó resuelto o si estamos elaborando su presupuesto.\nPROHIBICIÓN ABSOLUTA: No hables en futuro ni agendes.`;
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: LA CITA YA PASÓ (${datosExpediente.cita}). Pregunta si el problema quedó resuelto. NO AGENDES NADA.`;
|
||||
}
|
||||
} else if (esUrgencia) {
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: SERVICIO DE URGENCIA.\nTU ÚNICO OBJETIVO: Tranquilizar al cliente. Dile que al ser urgencia el técnico está avisado.\nPROHIBICIÓN ABSOLUTA: No des cita ni propongas horas.`;
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: URGENCIA. Tranquiliza al cliente y dile que el técnico está avisado. NO PROPONGAS HORAS.`;
|
||||
} else if (hayCitaPendiente) {
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: CITA PENDIENTE DE APROBACIÓN POR TÉCNICO.\n📅 Propuesta actual: El día ${datosExpediente.cita_pendiente_fecha} ${tramoPendiente}.\nTU ÚNICO OBJETIVO: Informar al cliente que estamos esperando confirmación del técnico.\n⚠️ REGLA CRÍTICA: Ignora el historial si no coincide con esta propuesta.\nPROHIBICIÓN ABSOLUTA: No agendes de nuevo.`;
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: CITA PENDIENTE DE APROBACIÓN POR TÉCNICO.\n📅 Propuesta actual: El día ${datosExpediente.cita_pendiente_fecha} ${tramoPendiente}.\nTU ÚNICO OBJETIVO: Informar al cliente que estamos esperando confirmación.\n⚠️ EXCEPCIÓN CRÍTICA (REAGENDAR/CANCELAR): Si el cliente pide CAMBIAR, CANCELAR o ANULAR esta propuesta, DEBES PERMITIRLO. Ofrece un hueco libre nuevo y, si acepta, MANDA OBLIGATORIAMENTE el código: [PROPUESTA:YYYY-MM-DD HH:mm]`;
|
||||
} else if (tieneCitaConfirmada) {
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: CITA 100% CONFIRMADA.\n📅 Día: ${datosExpediente.cita}.\n⏰ Tramo horario: ${tramoConfirmado}.\nTU OBJETIVO PRINCIPAL: Recordar la cita actual.\n\n⚠️ EXCEPCIÓN CRÍTICA (REAGENDAR): \nSi el cliente te dice que NO PUEDE ir, o pide CAMBIAR, MODIFICAR o ANULAR la cita, DEBES PERMITIRLO:\n1. Dile que no hay problema en cambiarla.\n2. Mira la "AGENDA DEL TÉCNICO" y ofrécele un hueco libre nuevo.\n3. Si acepta el nuevo hueco, ⚠️ INDÍSCALE EXPRESAMENTE que le pasas la nota al técnico para que él se lo confirme (NUNCA digas que ya está 100% confirmada). MANDA OBLIGATORIAMENTE el código: [PROPUESTA:YYYY-MM-DD HH:mm]`;
|
||||
directivaEstricta = `🛑 ESTADO ACTUAL: CITA CONFIRMADA para el ${datosExpediente.cita} ${tramoConfirmado}. Recuerda la cita.\n⚠️ EXCEPCIÓN CRÍTICA (REAGENDAR): Si el cliente pide CAMBIARLA o CANCELARLA, DEBES PERMITIRLO. Busca un hueco libre nuevo y, si acepta, MANDA OBLIGATORIAMENTE el código: [PROPUESTA:YYYY-MM-DD HH:mm]`;
|
||||
} else {
|
||||
directivaEstricta = `🟢 ESTADO ACTUAL: PENDIENTE DE AGENDAR CITA.\nTU OBJETIVO: Acordar fecha y hora.\nREGLAS: Nunca ofrezcas horas ocupadas. Fines de semana solo URGENCIAS. \n⚠️ MUY IMPORTANTE: Si el cliente ACEPTA un hueco, aclárale que le pasas la propuesta al técnico para que él dé la confirmación final (no lo des por asegurado al 100%). Añade AL FINAL el código: [PROPUESTA:YYYY-MM-DD HH:mm]`;
|
||||
directivaEstricta = `🟢 ESTADO ACTUAL: PENDIENTE DE AGENDAR CITA.\nTU OBJETIVO: Acordar fecha y hora. NUNCA ofrezcas horas ocupadas. Fines de semana solo URGENCIAS.\n⚠️ MUY IMPORTANTE: Si el cliente ACEPTA FIRMEMENTE un hueco, añade AL FINAL el código: [PROPUESTA:YYYY-MM-DD HH:mm]`;
|
||||
}
|
||||
|
||||
const promptSistema = `
|
||||
@@ -937,6 +973,8 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
|
||||
--- CONTEXTO BÁSICO ---
|
||||
- Hoy es: ${fechaHoyTexto}. (Año 2026).
|
||||
- Horario de la empresa: L-V de ${horarios.m_start} a ${horarios.m_end} y de ${horarios.a_start} a ${horarios.a_end}. Fines de semana solo URGENCIAS.
|
||||
- Problema o Avería reportada por el cliente: ${datosExpediente.averia || 'Avería general (no especificada)'}.
|
||||
- Localidad del cliente actual: ${datosExpediente.poblacion || 'Localidad no especificada'}.
|
||||
|
||||
--- AGENDA DEL TÉCNICO ASIGNADO ---
|
||||
${agendaOcupadaTexto}
|
||||
|
||||
Reference in New Issue
Block a user