diff --git a/server.js b/server.js index a781a67..34f4b12 100644 --- a/server.js +++ b/server.js @@ -904,6 +904,119 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) { return null; } } + +// ========================================== +// 🌐 EMBUDO PÚBLICO DE ENTRADA DE CLIENTES +// ========================================== +app.post("/public/new-request", async (req, res) => { + const clientDb = await pool.connect(); + try { + const { phone, name, address, guild_id, description, is_urgent, owner_id } = req.body; + + if (!phone || !guild_id) return res.status(400).json({ ok: false, error: "Faltan datos clave" }); + + // Por ahora, como es público, asumiremos que entra a la instancia principal (owner_id = 1), + // a menos que uses subdominios por empresa. Lo forzamos a 1 para este ejemplo. + const targetOwnerId = owner_id || 1; + const cleanPhone = phone.replace(/\D/g, ""); + if (cleanPhone.length < 9) return res.status(400).json({ ok: false, error: "Teléfono inválido" }); + + await clientDb.query('BEGIN'); + + // 1. BUSCAR O CREAR CLIENTE + let clientId, clientToken; + const qClient = await clientDb.query("SELECT id, portal_token FROM clients WHERE phone LIKE $1 AND owner_id = $2 LIMIT 1", [`%${cleanPhone}%`, targetOwnerId]); + + if (qClient.rowCount > 0) { + clientId = qClient.rows[0].id; + clientToken = qClient.rows[0].portal_token; + } else { + clientToken = crypto.randomBytes(6).toString('hex'); + const newClient = await clientDb.query( + "INSERT INTO clients (owner_id, full_name, phone, addresses, portal_token) VALUES ($1, $2, $3, $4, $5) RETURNING id", + [targetOwnerId, name || "Cliente Web", phone, JSON.stringify([address || ""]), clientToken] + ); + clientId = newClient.rows[0].id; + } + + // 2. BUSCAR OPERARIO AL AZAR (Si es NORMAL) + let assignedWorkerId = null; + if (!is_urgent) { + // Buscamos un operario activo de ese gremio al azar + const 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.role = 'operario' AND u.status = 'active' AND ug.guild_id = $2 + ORDER BY RANDOM() LIMIT 1 + `, [targetOwnerId, guild_id]); + + if (qWorker.rowCount > 0) { + assignedWorkerId = qWorker.rows[0].id; + } else { + // Si no hay operarios para asignar calendario, lo forzamos a manual/bolsa + return res.status(400).json({ ok: false, error: "No hay técnicos disponibles para cita normal en este gremio. Llama a la oficina." }); + } + } + + // 3. CREAR EL SERVICIO + const rawData = { + "Nombre Cliente": name || "Cliente Web", + "Teléfono": phone, + "Dirección": address || "", + "Descripción": description || "Aviso desde Web", + "guild_id": guild_id, + "Compañía": "Particular", + "Origen": "Web Público" + }; + + const ref = `WEB-${Date.now().toString().slice(-6)}`; + + const qSvc = await clientDb.query(` + INSERT INTO scraped_services (owner_id, provider, service_ref, status, automation_status, assigned_to, is_urgent, raw_data) + VALUES ($1, 'particular', $2, 'pending', $3, $4, $5, $6) RETURNING id + `, [ + targetOwnerId, + ref, + is_urgent ? 'in_progress' : 'manual', // Si es urgente, va a la bolsa + assignedWorkerId, // Solo tendrá id si NO es urgente + is_urgent, + JSON.stringify(rawData) + ]); + + const newServiceId = qSvc.rows[0].id; + await clientDb.query('COMMIT'); + + // 4. BIFURCACIÓN DE RUTAS + if (is_urgent) { + // Mandamos a la cola automáticamente para que suene a los técnicos + // Lo ejecutamos en segundo plano llamando a tu propio endpoint + const port = process.env.PORT || 3000; + fetch(`http://127.0.0.1:${port}/providers/automate/${newServiceId}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.ADMIN_TOKEN || 'hack_interno'}` }, // Necesitarás un token interno para esto + body: JSON.stringify({ guild_id, cp: "00000", useDelay: false }) + }).catch(e => console.error("Error lanzando bolsa urgente:", e)); + + res.json({ ok: true, action: 'queued', message: "Aviso urgente en cola." }); + } else { + // Devolvemos el token del cliente y el ID del servicio para redirigirlo al calendario + res.json({ + ok: true, + action: 'calendar', + redirectUrl: `https://portal.integrarepara.es/?token=${clientToken}&service=${newServiceId}` + }); + } + + } catch (e) { + await clientDb.query('ROLLBACK'); + console.error("Error en Web Pública:", e); + res.status(500).json({ ok: false, error: "Error procesando solicitud" }); + } finally { + clientDb.release(); + } +}); + + // ========================================== // 🔗 PORTAL PÚBLICO DEL CLIENTE // ==========================================