From 4e942d0130dacf2410fe6cd3533c480c68106e2f Mon Sep 17 00:00:00 2001 From: marsalva Date: Thu, 2 Apr 2026 20:05:45 +0000 Subject: [PATCH] Actualizar server.js --- server.js | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/server.js b/server.js index 0a00b2a..5068bd7 100644 --- a/server.js +++ b/server.js @@ -4432,6 +4432,122 @@ app.post("/webhook/stripe", async (req, res) => { } }); +// ========================================== +// 🛡️ MÓDULO SAAS: PLANES DE PROTECCIÓN +// ========================================== + +// 1. DASHBOARD: Estadísticas +app.get("/protection/dashboard", authMiddleware, async (req, res) => { + const cid = req.user.accountId; + try { + const [statsQ, actQ, topQ] = await Promise.all([ + pool.query(` + SELECT + (SELECT COUNT(*) FROM protection_subscriptions WHERE company_id = $1) as total, + (SELECT COALESCE(SUM(p.price), 0) FROM protection_subscriptions s JOIN protection_plans p ON s.plan_id = p.id WHERE s.company_id = $1 AND s.status = 'activo') as mrr, + (SELECT COALESCE(SUM(urgencies_used), 0) FROM protection_subscriptions WHERE company_id = $1) as urgUsed, + (SELECT COALESCE(SUM(bricos_used), 0) FROM protection_subscriptions WHERE company_id = $1) as briUsed, + (SELECT COUNT(*) FROM protection_subscriptions WHERE company_id = $1 AND payment_status = 'impagado') as unpaid + `, [cid]), + pool.query("SELECT * FROM protection_activity WHERE company_id = $1 ORDER BY created_at DESC LIMIT 10", [cid]), + pool.query(` + SELECT p.*, COUNT(s.id) as users + FROM protection_plans p + LEFT JOIN protection_subscriptions s ON p.id = s.plan_id + WHERE p.company_id = $1 + GROUP BY p.id + `, [cid]) + ]); + + res.json({ ok: true, stats: statsQ.rows[0], activity: actQ.rows, topPlans: topQ.rows }); + } catch (e) { res.status(500).json({ ok: false, error: e.message }); } +}); + +// 2. PLANES +app.get("/protection/plans", authMiddleware, async (req, res) => { + try { + const q = await pool.query("SELECT * FROM protection_plans WHERE company_id = $1 ORDER BY created_at DESC", [req.user.accountId]); + res.json({ ok: true, plans: q.rows }); + } catch (e) { res.status(500).json({ ok: false, error: e.message }); } +}); + +app.post("/protection/plans", authMiddleware, async (req, res) => { + const cid = req.user.accountId; + const { name, type, price, renewal, urgencies, bricos, coverages } = req.body; + try { + await pool.query( + "INSERT INTO protection_plans (company_id, name, type, price, renewal_price, urgencies_limit, bricos_limit, coverages) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)", + [cid, name, type, price, renewal, urgencies, bricos, coverages] + ); + await pool.query("INSERT INTO protection_activity (company_id, type, description) VALUES ($1, 'alta', $2)", [cid, `Nuevo plan creado: ${name}`]); + res.json({ ok: true }); + } catch (e) { res.status(500).json({ ok: false, error: e.message }); } +}); + +// 3. SUSCRIPTORES +app.get("/protection/subscribers", authMiddleware, async (req, res) => { + try { + const q = await pool.query(` + SELECT s.*, p.name as plan_name, p.price as plan_price, p.urgencies_limit, p.bricos_limit + FROM protection_subscriptions s + LEFT JOIN protection_plans p ON s.plan_id = p.id + WHERE s.company_id = $1 ORDER BY s.created_at DESC + `, [req.user.accountId]); + res.json({ ok: true, subscribers: q.rows }); + } catch (e) { res.status(500).json({ ok: false, error: e.message }); } +}); + +app.post("/protection/subscribers", authMiddleware, async (req, res) => { + const cid = req.user.accountId; + const { plan_id, name, dni, phone, payment_status, status, renewal_date, bricos_used, urgencies_used } = req.body; + + try { + const d = renewal_date ? renewal_date : null; + await pool.query( + `INSERT INTO protection_subscriptions + (company_id, plan_id, client_name, client_dni, client_phone, payment_status, status, renewal_date, bricos_used, urgencies_used) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`, + [cid, plan_id, name, dni, phone, payment_status, status, d, bricos_used, urgencies_used] + ); + await pool.query("INSERT INTO protection_activity (company_id, type, description) VALUES ($1, 'alta', $2)", [cid, `Suscripción creada: ${name}`]); + res.json({ ok: true }); + } catch (e) { res.status(500).json({ ok: false, error: e.message }); } +}); + +app.put("/protection/subscribers/:id/toggle", authMiddleware, async (req, res) => { + const cid = req.user.accountId; + const { id } = req.params; + const { field, value } = req.body; + try { + if (!['payment_status', 'status'].includes(field)) throw new Error("Invalid field"); + await pool.query(`UPDATE protection_subscriptions SET ${field} = $1 WHERE id = $2 AND company_id = $3`, [value, id, cid]); + await pool.query("INSERT INTO protection_activity (company_id, type, description) VALUES ($1, 'cobro', $2)", [cid, `Cambiado a ${value} en ID: ${id}`]); + res.json({ ok: true }); + } catch (e) { res.status(500).json({ ok: false, error: e.message }); } +}); + +// 4. CONFIGURACIÓN +app.get("/protection/config", authMiddleware, async (req, res) => { + try { + const q = await pool.query("SELECT * FROM protection_config WHERE company_id = $1", [req.user.accountId]); + res.json({ ok: true, config: q.rows[0] || {} }); + } catch (e) { res.status(500).json({ ok: false, error: e.message }); } +}); + +app.post("/protection/config", authMiddleware, async (req, res) => { + const cid = req.user.accountId; + const { name, email, phone, auto_renew, pre_notice, billing_method, contract_text } = req.body; + try { + await pool.query(` + INSERT INTO protection_config (company_id, name, email, phone, auto_renew, pre_notice, billing_method, contract_text) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8) ON CONFLICT (company_id) DO UPDATE SET + name=EXCLUDED.name, email=EXCLUDED.email, phone=EXCLUDED.phone, auto_renew=EXCLUDED.auto_renew, + pre_notice=EXCLUDED.pre_notice, billing_method=EXCLUDED.billing_method, contract_text=EXCLUDED.contract_text + `, [cid, name, email, phone, auto_renew, pre_notice, billing_method, contract_text]); + res.json({ ok: true }); + } catch (e) { res.status(500).json({ ok: false, error: e.message }); } +}); + // ========================================== // 🕒 EL RELOJ DEL SISTEMA (Ejecutar cada minuto) // ==========================================