Actualizar server.js

This commit is contained in:
2026-03-01 15:40:35 +00:00
parent 8c10ae4050
commit e2dc937403

View File

@@ -2143,6 +2143,86 @@ app.post("/budgets/:id/convert", authMiddleware, async (req, res) => {
}); });
// ==========================================
// 💰 MOTOR FINANCIERO Y CONTABILIDAD (PREPARADO PARA ROBOT PDF)
// ==========================================
// Creamos la tabla financiera preparada para facturas y robots
pool.query(`
CREATE TABLE IF NOT EXISTS service_financials (
id SERIAL PRIMARY KEY,
scraped_id INT REFERENCES scraped_services(id) ON DELETE CASCADE UNIQUE,
amount DECIMAL(10,2) DEFAULT 0.00,
payment_method TEXT,
is_paid BOOLEAN DEFAULT false,
invoice_ref TEXT,
pdf_raw_data JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
`).catch(console.error);
// Obtener toda la contabilidad
app.get("/financials", authMiddleware, async (req, res) => {
try {
// 1. Truco de magia: Si hay servicios que no tienen ficha financiera, se la creamos automáticamente.
// Si tiene compañía asignada, le ponemos "Cobro Banco" por defecto. Si no, "Pendiente".
await pool.query(`
INSERT INTO service_financials (scraped_id, payment_method)
SELECT id,
CASE WHEN raw_data->>'Compañía' IS NOT NULL AND raw_data->>'Compañía' != '' AND raw_data->>'Compañía' != 'Particular'
THEN 'Cobro Banco'
ELSE 'Pendiente' END
FROM scraped_services
WHERE owner_id = $1 AND id NOT IN (SELECT scraped_id FROM service_financials)
`, [req.user.accountId]);
// 2. Devolvemos la lista cruzando las finanzas con los datos del servicio
const q = await pool.query(`
SELECT f.*, s.service_ref, s.raw_data, s.status
FROM service_financials f
JOIN scraped_services s ON f.scraped_id = s.id
WHERE s.owner_id = $1
ORDER BY f.updated_at DESC
`, [req.user.accountId]);
res.json({ ok: true, financials: q.rows });
} catch(e) {
console.error("Error financiero:", e);
res.status(500).json({ ok: false });
}
});
// Guardar un cobro/pago
app.put("/financials/:id", authMiddleware, async (req, res) => {
try {
const { amount, payment_method } = req.body;
const parsedAmount = parseFloat(amount) || 0;
// NUEVA REGLA: Si el método de pago es "Pendiente", NO está pagado,
// independientemente del importe que tenga apuntado (Ej: Presupuestos).
const isPaid = payment_method !== 'Pendiente';
await pool.query(`
UPDATE service_financials
SET amount = $1, payment_method = $2, is_paid = $3, updated_at = NOW()
WHERE scraped_id = $4
`, [parsedAmount, payment_method, isPaid, req.params.id]);
// LOG AUTOMÁTICO DE TRAZABILIDAD
const userQ = await pool.query("SELECT full_name FROM users WHERE id=$1", [req.user.sub]);
const userName = userQ.rows[0]?.full_name || "Sistema";
await pool.query(
"INSERT INTO scraped_service_logs (scraped_id, user_name, action, details) VALUES ($1, $2, $3, $4)",
[req.params.id, userName, "Cobro Actualizado", `Importe: ${parsedAmount}€ | Método: ${payment_method}`]
);
res.json({ ok: true, is_paid: isPaid });
} catch(e) {
console.error(e);
res.status(500).json({ ok: false });
}
});
// ========================================== // ==========================================
// 📖 MOTOR DE TRAZABILIDAD (LOGS) // 📖 MOTOR DE TRAZABILIDAD (LOGS)
// ========================================== // ==========================================