diff --git a/server.js b/server.js index 593360f..bbbddf1 100644 --- a/server.js +++ b/server.js @@ -3481,6 +3481,9 @@ app.post("/budgets/:id/convert", authMiddleware, async (req, res) => { if (bq.rowCount === 0) return res.status(404).json({ok: false}); const budget = bq.rows[0]; + // 🟢 Saber si ya estaba pagado antes de convertirlo + const isAlreadyPaid = budget.status === 'paid'; + // 1. Montamos el Raw Data para el servicio const rawData = { "Nombre Cliente": budget.client_name, @@ -3491,7 +3494,8 @@ app.post("/budgets/:id/convert", authMiddleware, async (req, res) => { "guild_id": guild_id || null, "assigned_to": assigned_to || null, "scheduled_date": date || "", - "scheduled_time": time || "" + "scheduled_time": time || "", + "is_paid": isAlreadyPaid // 🟢 Inyección limpia estructural }; // 2. Insertamos en el Panel Operativo (Buzón) empezando en manual @@ -3507,11 +3511,13 @@ app.post("/budgets/:id/convert", authMiddleware, async (req, res) => { const newServiceId = insertSvc.rows[0].id; - // 3. Marcamos presupuesto como convertido y le enlazamos la ficha financiera por el total - await pool.query("UPDATE budgets SET status='converted' WHERE id=$1", [budget.id]); + // 3. Marcamos presupuesto y enlazamos ficha financiera + const finalBudgetStatus = isAlreadyPaid ? 'paid' : 'converted'; + await pool.query("UPDATE budgets SET status=$1 WHERE id=$2", [finalBudgetStatus, budget.id]); + await pool.query( - "INSERT INTO service_financials (scraped_id, amount, payment_method) VALUES ($1, $2, 'Pendiente')", - [newServiceId, budget.total] + "INSERT INTO service_financials (scraped_id, amount, payment_method, is_paid) VALUES ($1, $2, $3, $4)", + [newServiceId, budget.total, isAlreadyPaid ? 'Tarjeta (Stripe)' : 'Pendiente', isAlreadyPaid] ); // 4. Si pide automatización, la disparamos internamente llamando a nuestra propia IP (127.0.0.1) @@ -4319,34 +4325,32 @@ app.post("/public/portal/:token/budget/:id/checkout", async (req, res) => { }); // B) WEBHOOK DE STRIPE (El chivatazo invisible que avisa cuando el cliente YA ha pagado) -// Nota: Stripe necesita que el body llegue "crudo" para verificar las firmas, por eso le quitamos el express.json app.post("/webhook/stripe", express.raw({ type: 'application/json' }), async (req, res) => { try { - const sig = req.headers['stripe-signature']; const body = req.body; - - // Por ahora, como es un entorno SaaS complejo, procesaremos el evento de forma directa - // En producción, es altamente recomendable verificar el 'endpoint_secret' de cada webhook - const event = JSON.parse(body); if (event.type === 'checkout.session.completed') { const session = event.data.object; - // Extraer la "matrícula" oculta que le pusimos al pago const budgetId = session.metadata.budget_id; const ownerId = session.metadata.owner_id; - const amountTotal = (session.amount_total / 100).toFixed(2); // De céntimos a Euros + const amountTotal = (session.amount_total / 100).toFixed(2); - console.log(`💰 [STRIPE WEBHOOK] ¡PAGO RECIBIDO! Presupuesto PRE-${budgetId} por ${amountTotal}€ (Owner: ${ownerId})`); + console.log(`💰 [STRIPE WEBHOOK] ¡PAGO RECIBIDO! Presupuesto PRE-${budgetId} por ${amountTotal}€`); - // 1. Marcar el presupuesto como "Convertido/Pagado" - await pool.query("UPDATE budgets SET status = 'converted' WHERE id = $1 AND owner_id = $2", [budgetId, ownerId]); + // 1. 🟢 Lo marcamos con el estado puro 'paid' + await pool.query("UPDATE budgets SET status = 'paid' WHERE id = $1 AND owner_id = $2", [budgetId, ownerId]); - // 2. Si ya existía un servicio asociado, marcarlo en contabilidad - const sq = await pool.query("SELECT id FROM scraped_services WHERE service_ref = $1 AND owner_id = $2", [`PRE-${budgetId}`, ownerId]); + // 2. Si ya existía un servicio asociado, le inyectamos la variable "is_paid: true" + const sq = await pool.query("SELECT id, raw_data FROM scraped_services WHERE service_ref = $1 AND owner_id = $2", [`PRE-${budgetId}`, ownerId]); if (sq.rowCount > 0) { const serviceId = sq.rows[0].id; + let rawData = sq.rows[0].raw_data || {}; + + rawData.is_paid = true; // 🟢 Inyección limpia en el JSON de datos + + await pool.query("UPDATE scraped_services SET raw_data = $1 WHERE id = $2", [JSON.stringify(rawData), serviceId]); await pool.query(` INSERT INTO service_financials (scraped_id, amount, payment_method, is_paid) @@ -4354,7 +4358,6 @@ app.post("/webhook/stripe", express.raw({ type: 'application/json' }), async (re ON CONFLICT (scraped_id) DO UPDATE SET is_paid = true, payment_method = 'Tarjeta (Stripe)' `, [serviceId, amountTotal]); - // 3. Trazabilidad await pool.query("INSERT INTO scraped_service_logs (scraped_id, user_name, action, details) VALUES ($1, $2, $3, $4)", [serviceId, "Stripe API", "Pago Confirmado", `El cliente ha abonado ${amountTotal}€ por pasarela segura.`] );