diff --git a/server.js b/server.js index 95979eb..e887cbf 100644 --- a/server.js +++ b/server.js @@ -88,12 +88,12 @@ async function autoUpdateDB() { created_at TIMESTAMP DEFAULT NOW() ); -// Añade esto dentro del DO $$ BEGIN de autoUpdateDB -IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='scraped_services' AND column_name='assigned_to') THEN - ALTER TABLE scraped_services ADD COLUMN assigned_to INT REFERENCES users(id); -END IF; - - + -- CONFIGURACIÓN NEGOCIO + CREATE TABLE IF NOT EXISTS guilds ( + id SERIAL PRIMARY KEY, + owner_id INT REFERENCES users(id) ON DELETE CASCADE, + name TEXT NOT NULL, + created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS user_guilds ( user_id INT REFERENCES users(id) ON DELETE CASCADE, @@ -228,15 +228,6 @@ END IF; created_at TIMESTAMP DEFAULT NOW() ); -// Así debe quedar ese bloque dentro de autoUpdateDB: -await client.query(` - DO $$ BEGIN - -- AÑADE ESTA LÍNEA AQUÍ DENTRO: - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='scraped_services' AND column_name='assigned_to') THEN ALTER TABLE scraped_services ADD COLUMN assigned_to INT REFERENCES users(id); END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='services' AND column_name='client_id') THEN ... -`); - -- TABLA PARA ASIGNACIÓN AUTOMÁTICA CREATE TABLE IF NOT EXISTS assignment_pings ( id SERIAL PRIMARY KEY, @@ -252,6 +243,11 @@ await client.query(` // PARCHE DE ACTUALIZACIÓN await client.query(` DO $$ BEGIN + -- AÑADIDO: Columna física de operario para el panel operativo + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='scraped_services' AND column_name='assigned_to') THEN + ALTER TABLE scraped_services ADD COLUMN assigned_to INT REFERENCES users(id); + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='services' AND column_name='client_id') THEN ALTER TABLE services ADD COLUMN client_id INT REFERENCES clients(id) ON DELETE SET NULL; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='services' AND column_name='status_id') THEN ALTER TABLE services ADD COLUMN status_id INT REFERENCES service_statuses(id) ON DELETE SET NULL; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='services' AND column_name='contact_phone') THEN ALTER TABLE services ADD COLUMN contact_phone TEXT; END IF; @@ -353,7 +349,6 @@ app.post("/public/assignment/respond", async (req, res) => { const { token, action } = req.body; await client.query('BEGIN'); - // 1. Buscamos el ping para saber qué operario y qué servicio es const q = await client.query( "SELECT * FROM assignment_pings WHERE token = $1 AND status = 'pending' AND expires_at > NOW()", [token] @@ -363,20 +358,17 @@ app.post("/public/assignment/respond", async (req, res) => { const ping = q.rows[0]; if (action === 'accept') { - // Marcamos el ping como aceptado await client.query("UPDATE assignment_pings SET status = 'accepted' WHERE id = $1", [ping.id]); - // ACCIÓN CLAVE: Actualizamos el servicio con el operario asignado - // Guardamos el user_id en el JSON raw_data para que el panel lo lea correctamente - // Sustituye el UPDATE dentro de action === 'accept' -await client.query(` - UPDATE scraped_services - SET status = 'imported', - automation_status = 'completed', - assigned_to = $1, - raw_data = raw_data || jsonb_build_object('assigned_to', $1) - WHERE id = $2 -`, [ping.user_id, ping.scraped_id]); + // AÑADIDO: Guardar ID de operario en columna física y JSON + await client.query(` + UPDATE scraped_services + SET status = 'imported', + automation_status = 'completed', + assigned_to = $1, + raw_data = raw_data || jsonb_build_object('assigned_to', $1) + WHERE id = $2 + `, [ping.user_id, ping.scraped_id]); } else { await client.query("UPDATE assignment_pings SET status = 'rejected', expires_at = NOW() WHERE id = $1", [ping.id]); @@ -622,7 +614,7 @@ app.get("/discovery/keys/:provider", authMiddleware, async (req, res) => { } catch (e) { res.status(500).json({ ok: false }); } }); -// Ruta para obtener servicios aceptados con datos de operarios y gremios +// AÑADIDO: Ruta para el Panel Operativo (Asignados vs Citados) app.get("/services/active", authMiddleware, async (req, res) => { try { const q = await pool.query(` @@ -644,7 +636,7 @@ app.get("/services/active", authMiddleware, async (req, res) => { } catch (e) { res.status(500).json({ ok: false }); } }); -// Ruta para fijar la cita (Mueve el servicio de la izquierda a la derecha) +// AÑADIDO: Ruta para fijar la cita o el estado operativo app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => { try { const { id } = req.params; @@ -658,6 +650,20 @@ app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => { } catch (e) { res.status(500).json({ ok: false }); } }); +// AÑADIDO: Ruta para alta de expedientes manuales (Cola o Directo) +app.post("/services/manual-high", authMiddleware, async (req, res) => { + try { + const { phone, name, address, description, guild_id, assigned_to, mode } = req.body; + const serviceRef = "MAN-" + Date.now().toString().slice(-6); + const rawData = { "Nombre Cliente": name, "Teléfono": phone, "Dirección": address, "Descripción": description, "guild_id": guild_id }; + await pool.query(` + INSERT INTO scraped_services (owner_id, provider, service_ref, raw_data, status, automation_status, assigned_to) + VALUES ($1, 'MANUAL', $2, $3, 'pending', $4, $5) + `, [req.user.accountId, serviceRef, JSON.stringify(rawData), mode === 'auto' ? 'manual' : 'completed', mode === 'manual' ? assigned_to : null]); + res.json({ ok: true }); + } catch (e) { res.status(500).json({ ok: false }); } +}); + app.get("/discovery/mappings", authMiddleware, async (req, res) => { try {