Actualizar server.js

This commit is contained in:
2026-02-21 17:34:01 +00:00
parent 81c0d858c1
commit 139af0417c

View File

@@ -618,7 +618,7 @@ app.get("/whatsapp/status", authMiddleware, (req, res, next) => requirePlan(req,
// 🧠 MOTOR INTELIGENTE DE AGENDAMIENTO Y AGENDA ADMIN // 🧠 MOTOR INTELIGENTE DE AGENDAMIENTO Y AGENDA ADMIN
// ========================================== // ==========================================
// 1. Obtener huecos disponibles inteligentes (Bloqueando por duración) // 1. Obtener huecos disponibles inteligentes (Bloqueando por duración y GREMIO)
app.get("/public/portal/:token/slots", async (req, res) => { app.get("/public/portal/:token/slots", async (req, res) => {
try { try {
const { token } = req.params; const { token } = req.params;
@@ -636,14 +636,17 @@ app.get("/public/portal/:token/slots", async (req, res) => {
const raw = service.raw_data || {}; const raw = service.raw_data || {};
const targetZone = (raw["Población"] || raw["POBLACION-PROVINCIA"] || raw["Código Postal"] || "").toLowerCase().trim(); const targetZone = (raw["Población"] || raw["POBLACION-PROVINCIA"] || raw["Código Postal"] || "").toLowerCase().trim();
const targetGuildId = raw["guild_id"]; // Extraemos el gremio del servicio actual
// Extraemos la agenda respetando la duración (duration_minutes) // Extraemos la agenda respetando la duración (duration_minutes) y el gremio bloqueado
const agendaQ = await pool.query(` const agendaQ = await pool.query(`
SELECT raw_data->>'scheduled_date' as date, SELECT raw_data->>'scheduled_date' as date,
raw_data->>'scheduled_time' as time, raw_data->>'scheduled_time' as time,
raw_data->>'duration_minutes' as duration, raw_data->>'duration_minutes' as duration,
raw_data->>'Población' as poblacion, raw_data->>'Población' as poblacion,
raw_data->>'Código Postal' as cp raw_data->>'Código Postal' as cp,
provider,
raw_data->>'blocked_guild_id' as blocked_guild_id
FROM scraped_services FROM scraped_services
WHERE assigned_to = $1 WHERE assigned_to = $1
AND raw_data->>'scheduled_date' IS NOT NULL AND raw_data->>'scheduled_date' IS NOT NULL
@@ -652,6 +655,13 @@ app.get("/public/portal/:token/slots", async (req, res) => {
const agendaMap = {}; const agendaMap = {};
agendaQ.rows.forEach(row => { agendaQ.rows.forEach(row => {
// INTELIGENCIA DE GREMIOS:
// Si es un bloqueo de agenda que tiene un gremio específico, y NO coincide con el gremio del servicio actual...
// ¡Lo ignoramos! (El hueco sigue libre para este servicio)
if (row.provider === 'SYSTEM_BLOCK' && row.blocked_guild_id && String(row.blocked_guild_id) !== String(targetGuildId)) {
return;
}
if (!agendaMap[row.date]) agendaMap[row.date] = { times: [], zone: (row.poblacion || row.cp || "").toLowerCase().trim() }; if (!agendaMap[row.date]) agendaMap[row.date] = { times: [], zone: (row.poblacion || row.cp || "").toLowerCase().trim() };
// Calculamos cuántas horas bloquea este servicio según su duración // Calculamos cuántas horas bloquea este servicio según su duración
@@ -704,7 +714,7 @@ app.get("/public/portal/:token/slots", async (req, res) => {
d.setDate(d.getDate() + 1); d.setDate(d.getDate() + 1);
} }
res.json({ ok: true, days: availableDays }); res.json({ ok: true, days: availableDays });
} catch (e) { res.status(500).json({ ok: false }); } } catch (e) { console.error("Error Slots:", e); res.status(500).json({ ok: false }); }
}); });
// 2. Guardar la cita como "SOLICITUD PENDIENTE" // 2. Guardar la cita como "SOLICITUD PENDIENTE"
@@ -820,6 +830,60 @@ app.post("/agenda/requests/:id/reject", authMiddleware, async (req, res) => {
} catch (e) { res.status(500).json({ok: false}); } } catch (e) { res.status(500).json({ok: false}); }
}); });
// 6. RUTAS DE BLOQUEOS (AGENDA) CON SOPORTE PARA GREMIOS
app.post("/agenda/blocks", authMiddleware, async (req, res) => {
try {
const { worker_id, date, time, duration, reason, guild_id, guild_name } = req.body;
const raw = {
"Nombre Cliente": "BLOQUEO DE AGENDA",
"Descripción": reason,
scheduled_date: date,
scheduled_time: time,
duration_minutes: duration,
blocked_guild_id: guild_id || null, // Guardamos el gremio
blocked_guild_name: guild_name || null
};
await pool.query(`
INSERT INTO scraped_services (owner_id, provider, service_ref, status, assigned_to, raw_data)
VALUES ($1, 'SYSTEM_BLOCK', 'BLOCK-' || extract(epoch from now())::int, 'pending', $2, $3)
`, [req.user.accountId, worker_id, JSON.stringify(raw)]);
res.json({ ok: true });
} catch(e) { res.status(500).json({ ok: false }); }
});
app.get("/agenda/blocks", authMiddleware, async (req, res) => {
try {
const q = await pool.query(`
SELECT s.id, u.full_name as worker_name,
s.raw_data->>'scheduled_date' as date,
s.raw_data->>'scheduled_time' as time,
s.raw_data->>'duration_minutes' as duration,
s.raw_data->>'Descripción' as reason,
s.raw_data->>'blocked_guild_name' as guild_name
FROM scraped_services s
JOIN users u ON s.assigned_to = u.id
WHERE s.owner_id = $1 AND s.provider = 'SYSTEM_BLOCK'
AND s.raw_data->>'scheduled_date' >= CURRENT_DATE::text
ORDER BY s.raw_data->>'scheduled_date' ASC
`, [req.user.accountId]);
res.json({ ok: true, blocks: q.rows });
} catch(e) { res.status(500).json({ ok: false }); }
});
app.delete("/agenda/blocks/:id", authMiddleware, async (req, res) => {
try {
await pool.query("DELETE FROM scraped_services WHERE id=$1 AND owner_id=$2 AND provider='SYSTEM_BLOCK'", [req.params.id, req.user.accountId]);
res.json({ ok: true });
} catch(e) { res.status(500).json({ ok: false }); }
});
// ========================================== // ==========================================
// ⚙️ MOTOR AUTOMÁTICO DE WHATSAPP // ⚙️ MOTOR AUTOMÁTICO DE WHATSAPP
// ========================================== // ==========================================