From d60dcc05c0563fb73a587e59a02c3d5bc1388feb Mon Sep 17 00:00:00 2001 From: marsalva Date: Sun, 15 Feb 2026 17:34:52 +0000 Subject: [PATCH] Actualizar server.js --- server.js | 54 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/server.js b/server.js index 2c75a0e..bcf6acb 100644 --- a/server.js +++ b/server.js @@ -237,6 +237,8 @@ async function autoUpdateDB() { IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='users' AND column_name='plan_tier') THEN ALTER TABLE users ADD COLUMN plan_tier TEXT DEFAULT 'free'; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='users' AND column_name='company_slug') THEN ALTER TABLE users ADD COLUMN company_slug TEXT UNIQUE; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='users' AND column_name='zones') THEN ALTER TABLE users ADD COLUMN zones JSONB DEFAULT '[]'; END IF; + -- NUEVO: Columna de estado para Baja Temporal + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='users' AND column_name='status') THEN ALTER TABLE users ADD COLUMN status TEXT DEFAULT 'active'; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='services' AND column_name='provider_data') THEN ALTER TABLE services ADD COLUMN provider_data JSONB DEFAULT '{}'; END IF; IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='services' AND column_name='import_source') THEN ALTER TABLE services ADD COLUMN import_source TEXT; END IF; @@ -653,28 +655,40 @@ app.delete("/guilds/:id", authMiddleware, async (req, res) => { try { await pool // BUSCADOR GEOGRÁFICO: Consulta la tabla que poblaste en Adminer // BUSCADOR GEOGRÁFICO MEJORADO (Ignora tildes automáticamente) app.get("/api/geo/municipios/:provincia", authMiddleware, async (req, res) => { - try { - let { provincia } = req.params; - - // Normalizamos la entrada: quitamos tildes y ponemos en mayúsculas - // Ejemplo: "CÁDIZ" -> "CADIZ" - const provClean = provincia.toUpperCase() - .normalize("NFD") - .replace(/[\u0300-\u036f]/g, ""); +    try { +        let { provincia } = req.params; +         +        // Normalizamos la entrada: quitamos tildes y ponemos en mayúsculas +        // Ejemplo: "CÁDIZ" -> "CADIZ" +        const provClean = provincia.toUpperCase() +            .normalize("NFD") +            .replace(/[\u0300-\u036f]/g, ""); - const q = await pool.query( - "SELECT municipio, codigo_postal FROM master_geo_es WHERE provincia = $1 ORDER BY municipio ASC", - [provClean] - ); - res.json({ ok: true, municipios: q.rows }); +        const q = await pool.query( +            "SELECT municipio, codigo_postal FROM master_geo_es WHERE provincia = $1 ORDER BY municipio ASC", +            [provClean] +        ); +        res.json({ ok: true, municipios: q.rows }); +    } catch (e) { +        console.error("Error al leer master_geo_es:", e.message); +        res.status(500).json({ ok: false }); +    } +}); + +// NUEVO: Endpoint para activar/desactivar operario (Baja Temporal) +app.patch("/admin/users/:id/status", authMiddleware, async (req, res) => { + try { + const { status } = req.body; // 'active' o 'inactive' + await pool.query("UPDATE users SET status = $1 WHERE id = $2 AND owner_id = $3", + [status, req.params.id, req.user.accountId]); + res.json({ ok: true }); } catch (e) { - console.error("Error al leer master_geo_es:", e.message); res.status(500).json({ ok: false }); } }); -app.get("/admin/users", authMiddleware, async (req, res) => { try { const q = await pool.query(`SELECT u.id, u.full_name, u.email, u.phone, u.role, u.zones, COALESCE(json_agg(g.id) FILTER (WHERE g.id IS NOT NULL), '[]') as guilds FROM users u LEFT JOIN user_guilds ug ON u.id=ug.user_id LEFT JOIN guilds g ON ug.guild_id=g.id WHERE u.owner_id=$1 GROUP BY u.id ORDER BY u.id DESC`, [req.user.accountId]); res.json({ ok: true, users: q.rows }); } catch (e) { res.status(500).json({ ok: false }); } }); -app.post("/admin/users", authMiddleware, async (req, res) => { const client = await pool.connect(); try { const { fullName, email, password, role, guilds, phone, zones } = req.body; if (!email || !password || !fullName || !phone) return res.status(400).json({ ok: false }); const p = normalizePhone(phone); const hash = await bcrypt.hash(password, 10); const check = await client.query("SELECT id FROM users WHERE (phone=$1 OR email=$2) AND owner_id=$3", [p, email, req.user.accountId]); if (check.rowCount > 0) return res.status(400).json({ ok: false, error: "Duplicado" }); await client.query('BEGIN'); const insert = await client.query("INSERT INTO users (full_name, email, password_hash, role, phone, is_verified, owner_id, zones) VALUES ($1, $2, $3, $4, $5, TRUE, $6, $7) RETURNING id", [fullName, email, hash, role || 'operario', p, req.user.accountId, JSON.stringify(zones || [])]); const uid = insert.rows[0].id; if (guilds) for (const gid of guilds) await client.query("INSERT INTO user_guilds (user_id, guild_id) VALUES ($1, $2)", [uid, gid]); await client.query('COMMIT'); res.json({ ok: true }); } catch (e) { await client.query('ROLLBACK'); res.status(500).json({ ok: false }); } finally { client.release(); } }); +app.get("/admin/users", authMiddleware, async (req, res) => { try { const q = await pool.query(`SELECT u.id, u.full_name, u.email, u.phone, u.role, u.zones, u.status, COALESCE(json_agg(g.id) FILTER (WHERE g.id IS NOT NULL), '[]') as guilds FROM users u LEFT JOIN user_guilds ug ON u.id=ug.user_id LEFT JOIN guilds g ON ug.guild_id=g.id WHERE u.owner_id=$1 GROUP BY u.id ORDER BY u.id DESC`, [req.user.accountId]); res.json({ ok: true, users: q.rows }); } catch (e) { res.status(500).json({ ok: false }); } }); +app.post("/admin/users", authMiddleware, async (req, res) => { const client = await pool.connect(); try { const { fullName, email, password, role, guilds, phone, zones } = req.body; if (!email || !password || !fullName || !phone) return res.status(400).json({ ok: false }); const p = normalizePhone(phone); const hash = await bcrypt.hash(password, 10); const check = await client.query("SELECT id FROM users WHERE (phone=$1 OR email=$2) AND owner_id=$3", [p, email, req.user.accountId]); if (check.rowCount > 0) return res.status(400).json({ ok: false, error: "Duplicado" }); await client.query('BEGIN'); const insert = await client.query("INSERT INTO users (full_name, email, password_hash, role, phone, is_verified, owner_id, zones, status) VALUES ($1, $2, $3, $4, $5, TRUE, $6, $7, 'active') RETURNING id", [fullName, email, hash, role || 'operario', p, req.user.accountId, JSON.stringify(zones || [])]); const uid = insert.rows[0].id; if (guilds) for (const gid of guilds) await client.query("INSERT INTO user_guilds (user_id, guild_id) VALUES ($1, $2)", [uid, gid]); await client.query('COMMIT'); res.json({ ok: true }); } catch (e) { await client.query('ROLLBACK'); res.status(500).json({ ok: false }); } finally { client.release(); } }); app.put("/admin/users/:id", authMiddleware, async (req, res) => { const client = await pool.connect(); try { const userId = req.params.id; const { fullName, email, phone, role, guilds, password, zones } = req.body; const p = normalizePhone(phone); await client.query('BEGIN'); if(password) { const hash = await bcrypt.hash(password, 10); await client.query("UPDATE users SET full_name=$1, email=$2, phone=$3, role=$4, password_hash=$5, zones=$6 WHERE id=$7", [fullName, email, p, role, hash, JSON.stringify(zones || []), userId]); } else { await client.query("UPDATE users SET full_name=$1, email=$2, phone=$3, role=$4, zones=$5 WHERE id=$6", [fullName, email, p, role, JSON.stringify(zones || []), userId]); } if (guilds && Array.isArray(guilds)) { await client.query("DELETE FROM user_guilds WHERE user_id=$1", [userId]); for (const gid of guilds) await client.query("INSERT INTO user_guilds (user_id, guild_id) VALUES ($1, $2)", [userId, gid]); } await client.query('COMMIT'); res.json({ ok: true }); } catch (e) { await client.query('ROLLBACK'); res.status(500).json({ ok: false }); } finally { client.release(); } }); app.delete("/admin/users/:id", authMiddleware, async (req, res) => { try { await pool.query("DELETE FROM users WHERE id=$1 AND owner_id=$2", [req.params.id, req.user.accountId]); res.json({ ok: true }); } catch (e) { res.status(500).json({ ok: false }); } }); @@ -683,4 +697,10 @@ app.get("/config/company", authMiddleware, async (req, res) => { try { const q = app.post("/config/company", authMiddleware, async (req, res) => { const client = await pool.connect(); try { const { slug } = req.body; if (!slug || slug.length < 3) return res.status(400).json({ ok: false, error: "Mínimo 3 caracteres" }); const cleanSlug = slug.toLowerCase().replace(/[^a-z0-9-]/g, ""); if (cleanSlug !== slug) return res.status(400).json({ ok: false, error: "Carácteres inválidos" }); const check = await client.query("SELECT id FROM users WHERE company_slug=$1 AND id != $2", [cleanSlug, req.user.accountId]); if (check.rowCount > 0) return res.status(400).json({ ok: false, error: "Nombre en uso" }); await client.query("UPDATE users SET company_slug=$1 WHERE id=$2", [cleanSlug, req.user.accountId]); res.json({ ok: true, fullUrl: `https://${cleanSlug}.integrarepara.es` }); } catch (e) { res.status(500).json({ ok: false }); } finally { client.release(); } }); const port = process.env.PORT || 3000; -autoUpdateDB().then(() => { app.listen(port, "0.0.0.0", () => console.log(`🚀 Server OK en puerto ${port}`)); }); \ No newline at end of file +autoUpdateDB().then(() => { app.listen(port, "0.0.0.0", () => console.log(`🚀 Server OK en puerto ${port}`)); }); + +como seria para asignar por codigo postal el operario al traspasar de proveedores el expediente?? + +si el codigo postal del expediente de proveedores concuerda con algun operario activo que se asigne a el solo si tiene activo ese codigo postal. + +modifica solo lo necesario, NO TOQUES EL RESTO. \ No newline at end of file