diff --git a/server.js b/server.js index 0a7cd7d..9c93a84 100644 --- a/server.js +++ b/server.js @@ -915,8 +915,12 @@ app.post("/public/new-request", async (req, res) => { if (!phone || !guild_id) return res.status(400).json({ ok: false, error: "Faltan datos clave" }); - const targetOwnerId = owner_id || 1; - const cleanPhone = phone.replace(/\D/g, ""); + // 🛡️ CONVERSIÓN ESTRICTA DE DATOS (Para que PostgreSQL no lance Error 500) + const targetOwnerId = parseInt(owner_id) || 1; + const safeGuildId = parseInt(guild_id) || 0; + const isUrgentBool = is_urgent === true || is_urgent === 'true'; + const cleanPhone = String(phone).replace(/\D/g, ""); + if (cleanPhone.length < 9) return res.status(400).json({ ok: false, error: "Teléfono inválido" }); await clientDb.query('BEGIN'); @@ -939,13 +943,13 @@ app.post("/public/new-request", async (req, res) => { // 2. BUSCAR OPERARIO AL AZAR (Si es NORMAL) let assignedWorkerId = null; - if (!is_urgent) { + if (!isUrgentBool) { let qWorker = await clientDb.query(` SELECT u.id FROM users u JOIN user_guilds ug ON u.id = ug.user_id WHERE u.owner_id = $1 AND u.status = 'active' AND ug.guild_id = $2 ORDER BY RANDOM() LIMIT 1 - `, [targetOwnerId, guild_id]); + `, [targetOwnerId, safeGuildId]); // 🛡️ FALLBACK: Si nadie tiene ese gremio, coge a CUALQUIERA activo if (qWorker.rowCount === 0) { @@ -955,6 +959,7 @@ app.post("/public/new-request", async (req, res) => { if (qWorker.rowCount > 0) { assignedWorkerId = qWorker.rows[0].id; } else { + await clientDb.query('ROLLBACK'); // ⚠️ VITAL para no bloquear la BD return res.status(400).json({ ok: false, error: "No hay técnicos disponibles. Llama a la oficina." }); } } @@ -965,7 +970,7 @@ app.post("/public/new-request", async (req, res) => { "Teléfono": phone, "Dirección": address || "", "Descripción": description || "Aviso desde Web", - "guild_id": guild_id, + "guild_id": safeGuildId, "Compañía": "Particular", "Origen": "Web Público" }; @@ -978,25 +983,25 @@ app.post("/public/new-request", async (req, res) => { `, [ targetOwnerId, ref, - is_urgent ? 'in_progress' : 'manual', + isUrgentBool ? 'in_progress' : 'manual', assignedWorkerId, - is_urgent, + isUrgentBool, JSON.stringify(rawData) ]); const newServiceId = qSvc.rows[0].id; // 4. SI ES URGENTE, DISPARAMOS EL WHATSAPP DIRECTAMENTE - if (is_urgent) { + if (isUrgentBool) { // Buscamos un operario activo de ese gremio let workersQ = await clientDb.query(` SELECT u.id, u.full_name, u.phone FROM users u JOIN user_guilds ug ON u.id = ug.user_id WHERE u.owner_id = $1 AND u.status = 'active' AND ug.guild_id = $2 ORDER BY RANDOM() LIMIT 1 - `, [targetOwnerId, guild_id]); + `, [targetOwnerId, safeGuildId]); - // 🛡️ FALLBACK URGENCIA: Si nadie tiene ese gremio (o es admin), coge a CUALQUIERA activo. ¡Es urgente! + // 🛡️ FALLBACK URGENCIA: Si nadie tiene ese gremio, coge a CUALQUIERA activo if (workersQ.rowCount === 0) { workersQ = await clientDb.query(` SELECT id, full_name, phone FROM users @@ -1015,33 +1020,30 @@ app.post("/public/new-request", async (req, res) => { VALUES ($1, $2, $3, CURRENT_TIMESTAMP + INTERVAL '5 minutes') `, [newServiceId, worker.id, token]); - // Extraemos solo la calle (Corta al encontrar el primer número o un s/n) - let calleOculta = "Zona por determinar"; + // 🛑 EXTRAEMOS LA CALLE OCULTA (Sin números) + let calleOculta = "Zona asignada"; if (address) { calleOculta = address.replace(/\s*,?\s*(\d+|s\/n).*$/i, '').trim(); } // Enviamos el WA al operario const msg = `🚨 *URGENCIA PARTICULAR (WEB)*\n📍 Calle: ${calleOculta}\n🔗 https://web.integrarepara.es/aceptar.html?t=${token}`; - sendWhatsAppAuto(worker.phone, msg, `cliente_${targetOwnerId}`, false).catch(console.error); - // Dejamos huella await clientDb.query( "INSERT INTO scraped_service_logs (scraped_id, user_name, action, details) VALUES ($1, 'Sistema Automático', 'Bolsa de Urgencias', $2)", - [newServiceId, `Notificación enviada a: ${worker.full_name} (${worker.phone})`] + [newServiceId, `Notificación enviada a: ${worker.full_name}`] ); } else { - // Solo fallará si literalmente tienes 0 usuarios activos en el sistema await clientDb.query("UPDATE scraped_services SET automation_status = 'failed' WHERE id = $1", [newServiceId]); } await clientDb.query('COMMIT'); - res.json({ ok: true, action: 'queued', message: "Aviso urgente en cola." }); + return res.json({ ok: true, action: 'queued', message: "Aviso urgente en cola." }); } else { await clientDb.query('COMMIT'); - // Devolvemos el token del cliente y el ID del servicio para redirigirlo al calendario - res.json({ + // Devolvemos el token del cliente para llevarlo a pedir Cita Normal + return res.json({ ok: true, action: 'calendar', redirectUrl: `https://portal.integrarepara.es/?token=${clientToken}&service=${newServiceId}` @@ -1050,7 +1052,7 @@ app.post("/public/new-request", async (req, res) => { } catch (e) { await clientDb.query('ROLLBACK'); - console.error("Error en Web Pública:", e); + console.error("🚨 CRASH EN WEB PÚBLICA (NUEVO AVISO):", e.message); res.status(500).json({ ok: false, error: "Error procesando solicitud" }); } finally { clientDb.release();