From 8a0deee109939db64d16727170adabfb88200f93 Mon Sep 17 00:00:00 2001 From: marsalva Date: Sun, 28 Dec 2025 20:52:31 +0000 Subject: [PATCH] Actualizar robot_cobros.js --- robot_cobros.js | 147 ++++++++++++++++++++++-------------------------- 1 file changed, 68 insertions(+), 79 deletions(-) diff --git a/robot_cobros.js b/robot_cobros.js index e92bba3..5e06f74 100644 --- a/robot_cobros.js +++ b/robot_cobros.js @@ -3,7 +3,7 @@ const { chromium } = require('playwright'); const admin = require('firebase-admin'); const cors = require('cors'); -// --- 1. CONFIGURACIÓN FIREBASE --- +// --- 1. CONFIGURACIÓN (Solo para leer credenciales) --- try { if (process.env.FIREBASE_PRIVATE_KEY) { if (!admin.apps.length) { @@ -15,41 +15,47 @@ try { }), }); } - console.log("✅ Firebase inicializado."); - } else { - console.warn("⚠️ SIN FIREBASE: Modo simulación."); + console.log("✅ Firebase inicializado (Solo para leer usuario/pass)."); } } catch (e) { console.error("❌ Error Firebase:", e.message); } const db = admin.apps.length ? admin.firestore() : null; -const APPOINTMENTS_COL = "appointments"; const app = express(); - -// --- 2. CORS PERMISIVO (Para evitar bloqueos) --- app.use(cors({ origin: '*' })); app.use(express.json()); -// --- 3. ENDPOINT PRINCIPAL --- +// --- 2. ENDPOINT (AHORA ESPERA AL ROBOT) --- app.post('/api/robot-cobros', async (req, res) => { - console.log("🔔 Orden recibida: Rescatar cobros."); - res.json({ success: true, message: "Robot iniciado. Procesando liquidaciones..." }); + console.log("🔔 Petición recibida: Modo Simulación."); - // Ejecutar en segundo plano - runLiquidationRobot().catch(err => console.error("❌ Error robot:", err)); + try { + // Esperamos a que el robot termine y nos de los datos + const resultados = await runLiquidationScanner(); + + // Se los enviamos al navegador + res.json({ + success: true, + message: "Escaneo completado", + data: resultados + }); + } catch (err) { + console.error("❌ Error en robot:", err); + res.status(500).json({ success: false, message: err.message }); + } }); -app.get('/', (req, res) => res.send('🤖 Robot Liquidaciones ONLINE (Usa POST para activar)')); +app.get('/', (req, res) => res.send('🤖 Robot Scanner ONLINE (Modo Lectura)')); -// --- 4. LÓGICA DE NAVEGACIÓN (Liquidaciones) --- -async function runLiquidationRobot() { +// --- 3. LÓGICA DE ESCANEO (SIN GUARDAR) --- +async function runLiquidationScanner() { let browser = null; try { console.log("🚀 Lanzando navegador..."); - // 1. Obtener credenciales - let user = ""; - let pass = ""; + // 1. Credenciales + let user = "TU_USUARIO"; + let pass = "TU_PASS"; if (db) { const doc = await db.collection("providerCredentials").doc("homeserve").get(); @@ -59,18 +65,15 @@ async function runLiquidationRobot() { } } - if (!user || !pass) throw new Error("No hay credenciales en Firebase"); - browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] }); const page = await browser.newPage(); // 2. Login - console.log("🌍 Entrando en HomeServe (PROF_PASS)..."); + console.log("🌍 Login HomeServe..."); await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=PROF_PASS', { timeout: 60000 }); const selUser = 'input[name="CODIGO"]'; if (await page.isVisible(selUser)) { - console.log("🔑 Logueándose..."); await page.fill(selUser, user); await page.fill('input[type="password"]', pass); await page.keyboard.press('Enter'); @@ -78,90 +81,76 @@ async function runLiquidationRobot() { } // 3. Ir a Liquidaciones - console.log("📂 Navegando a CONSULTALIQ_WEB..."); + console.log("📂 Sección Liquidaciones..."); await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=CONSULTALIQ_WEB'); await page.waitForTimeout(2000); - // 4. Buscar primera fecha (enlace) + // 4. Buscar enlace console.log("🔍 Buscando última liquidación..."); - // Buscamos el primer enlace dentro de la tabla principal const liquidacionClicked = await page.evaluate(() => { - const link = document.querySelector('table a'); // Clic en el primer enlace que encuentre en una tabla - if (link) { link.click(); return true; } + const links = Array.from(document.querySelectorAll('a')); + // Busca enlace dentro de una celda que tenga texto largo (la fecha) + const target = links.find(l => l.closest('td') && l.innerText.length > 5); + if (target) { target.click(); return true; } return false; }); - if (!liquidacionClicked) throw new Error("No se encontraron liquidaciones."); + if (!liquidacionClicked) console.log("⚠️ No clické fecha (quizá ya estamos dentro)."); await page.waitForTimeout(3000); - // 5. Buscar botón "Desglose" - console.log("📄 Buscando desglose..."); + // 5. Botón Desglose const desgloseClicked = await page.evaluate(() => { - // Buscar input tipo button o enlace que diga "Desglose" const inputs = Array.from(document.querySelectorAll('input[type="button"], a, button')); const target = inputs.find(el => (el.value || el.innerText || "").toLowerCase().includes('desglose')); if (target) { target.click(); return true; } return false; }); + if(desgloseClicked) await page.waitForTimeout(3000); - if (!desgloseClicked) console.warn("⚠️ No vi botón 'Desglose', intentando leer tabla actual..."); - await page.waitForTimeout(3000); - - // 6. Leer Tabla de Cobros - console.log("💰 Leyendo datos..."); - const cobros = await page.evaluate(() => { + // 6. LEER DATOS Y DEVOLVERLOS (NO GUARDAR) + console.log("💰 Extrayendo datos para el usuario..."); + + const extractedData = await page.evaluate(() => { const filas = Array.from(document.querySelectorAll('tr')); - const datos = []; + const lista = []; + filas.forEach(tr => { - const text = tr.innerText; - // Buscamos patrones de expediente (ej: 15xxxxxx) y dinero - // Esta es una heurística básica, ajustar según HTML real const tds = tr.querySelectorAll('td'); - let exp = ""; - let money = ""; - - tds.forEach(td => { - const t = td.innerText.trim(); - if (/^\d{6,}$/.test(t)) exp = t; // Numeros largos = expediente - if (t.includes(',') && (t.includes('€') || /\d/.test(t))) money = t; - }); + // Estructura detectada en tu saldo.html: + // Col 0: Servicio, Col 1: Dirección, Col 4: Importe Autofra (Base) + if (tds.length >= 5) { + const txtServicio = tds[0].innerText.trim(); + const txtDir = tds[1].innerText.trim(); + const txtImporte = tds[4].innerText.trim(); - if (exp && money) datos.push({ exp, money }); + if (/^\d{6,}$/.test(txtServicio)) { + lista.push({ + expediente: txtServicio, + direccion: txtDir, + importeRaw: txtImporte + }); + } + } }); - return datos; + return lista; }); - console.log(`✅ Detectados ${cobros.length} posibles cobros.`); + // Procesar importes (Limpieza de puntos decimales) + const finalData = extractedData.map(item => { + // "27.67" -> 27.67 (Float) + const clean = item.importeRaw.replace(/[^\d.-]/g, ''); + return { + ...item, + importeLimpio: parseFloat(clean) || 0 + }; + }); - // 7. Guardar en Firebase - if (db && cobros.length > 0) { - const batch = db.batch(); - let count = 0; - const nowISO = new Date().toISOString(); - - for (const item of cobros) { - // Limpiar importe - const importe = parseFloat(item.money.replace(/[^\d,]/g, '').replace(',', '.')); - if (!importe) continue; - - // Buscar servicio - const q = await db.collection(APPOINTMENTS_COL).where("serviceNumber", "==", item.exp).get(); - q.forEach(doc => { - batch.update(doc.ref, { - paidAmount: importe, - status: 'completed', // Asumimos completado si pagan - paymentDate: nowISO, - lastUpdatedByRobot: nowISO - }); - count++; - }); - } - await batch.commit(); - console.log(`💾 Base de datos actualizada: ${count} registros.`); - } + console.log(`✅ Devolviendo ${finalData.length} registros al frontend.`); + return finalData; } catch (e) { - console.error("❌ Error en proceso:", e); + console.error("❌ Error:", e); + throw e; } finally { if (browser) await browser.close(); }