diff --git a/server.js b/server.js index a2d6680..270d5cd 100644 --- a/server.js +++ b/server.js @@ -618,7 +618,7 @@ app.get("/whatsapp/status", authMiddleware, (req, res, next) => requirePlan(req, // 馃 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) => { try { const { token } = req.params; @@ -636,14 +636,17 @@ app.get("/public/portal/:token/slots", async (req, res) => { const raw = service.raw_data || {}; 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(` SELECT raw_data->>'scheduled_date' as date, raw_data->>'scheduled_time' as time, raw_data->>'duration_minutes' as duration, 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 WHERE assigned_to = $1 AND raw_data->>'scheduled_date' IS NOT NULL @@ -652,6 +655,13 @@ app.get("/public/portal/:token/slots", async (req, res) => { const agendaMap = {}; 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() }; // 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); } 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" @@ -820,6 +830,60 @@ app.post("/agenda/requests/:id/reject", authMiddleware, async (req, res) => { } 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 // ==========================================