Actualizar robot.js
This commit is contained in:
parent
25ef322f81
commit
1cab22b1a8
99
robot.js
99
robot.js
|
|
@ -113,8 +113,20 @@ async function getAllPendingDocIds() {
|
||||||
return snap.docs.map(d => d.id);
|
return snap.docs.map(d => d.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function archiveDoc(docRef, nowISO, reason, extra = {}) {
|
||||||
|
// Marca como archivado para que NO aparezca como "nuevo" (verde)
|
||||||
|
await docRef.set({
|
||||||
|
status: "archived",
|
||||||
|
archivedReason: reason,
|
||||||
|
archivedAt: nowISO,
|
||||||
|
updatedAt: nowISO,
|
||||||
|
...extra
|
||||||
|
}, { merge: true });
|
||||||
|
}
|
||||||
|
|
||||||
async function runRobot() {
|
async function runRobot() {
|
||||||
console.log('🤖 [V7.5] Robot HomeServe (no dupes + in_system + archiva solo completed)...');
|
console.log('🤖 [V7.6] Robot HomeServe (no dupes + in_system + archiva completed + archiva missing/bloqueado)...');
|
||||||
|
|
||||||
// 0) Credenciales
|
// 0) Credenciales
|
||||||
let creds;
|
let creds;
|
||||||
try {
|
try {
|
||||||
|
|
@ -124,6 +136,7 @@ async function runRobot() {
|
||||||
console.error("❌ No se pudieron cargar credenciales:", e.message);
|
console.error("❌ No se pudieron cargar credenciales:", e.message);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const browser = await chromium.launch({
|
const browser = await chromium.launch({
|
||||||
headless: true,
|
headless: true,
|
||||||
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
||||||
|
|
@ -131,12 +144,14 @@ async function runRobot() {
|
||||||
const context = await browser.newContext();
|
const context = await browser.newContext();
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
const nowISO = new Date().toISOString();
|
const nowISO = new Date().toISOString();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// LOGIN
|
// LOGIN
|
||||||
console.log('🔐 Entrando al login...');
|
console.log('🔐 Entrando al login...');
|
||||||
await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=PROF_PASS', { timeout: 60000 });
|
await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=PROF_PASS', { timeout: 60000 });
|
||||||
const selectorUsuario = 'input[name="CODIGO"]';
|
const selectorUsuario = 'input[name="CODIGO"]';
|
||||||
const selectorPass = 'input[type="password"]';
|
const selectorPass = 'input[type="password"]';
|
||||||
|
|
||||||
if (await page.isVisible(selectorUsuario)) {
|
if (await page.isVisible(selectorUsuario)) {
|
||||||
await page.fill(selectorUsuario, "");
|
await page.fill(selectorUsuario, "");
|
||||||
await page.fill(selectorPass, "");
|
await page.fill(selectorPass, "");
|
||||||
|
|
@ -148,9 +163,11 @@ async function runRobot() {
|
||||||
} else {
|
} else {
|
||||||
console.log("⚠️ No veo login (quizá ya logueado).");
|
console.log("⚠️ No veo login (quizá ya logueado).");
|
||||||
}
|
}
|
||||||
|
|
||||||
// LISTA
|
// LISTA
|
||||||
console.log('📂 Leyendo lista de servicios...');
|
console.log('📂 Leyendo lista de servicios...');
|
||||||
await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=lista_servicios_total');
|
await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=lista_servicios_total');
|
||||||
|
|
||||||
const referenciasEnWeb = await page.evaluate(() => {
|
const referenciasEnWeb = await page.evaluate(() => {
|
||||||
const filas = Array.from(document.querySelectorAll('table tr'));
|
const filas = Array.from(document.querySelectorAll('table tr'));
|
||||||
const refs = [];
|
const refs = [];
|
||||||
|
|
@ -164,20 +181,25 @@ async function runRobot() {
|
||||||
});
|
});
|
||||||
return Array.from(new Set(refs));
|
return Array.from(new Set(refs));
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`🔎 Encontrados ${referenciasEnWeb.length} servicios válidos.`);
|
console.log(`🔎 Encontrados ${referenciasEnWeb.length} servicios válidos.`);
|
||||||
const referenciasNormalizadas = referenciasEnWeb
|
const referenciasNormalizadas = referenciasEnWeb
|
||||||
.map(normalizeServiceNumber)
|
.map(normalizeServiceNumber)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
const webSet = new Set(referenciasNormalizadas);
|
const webSet = new Set(referenciasNormalizadas);
|
||||||
|
|
||||||
// ✅ precargar info del sistema
|
// ✅ precargar info del sistema
|
||||||
console.log("🧠 Precargando datos del sistema (appointments/services)...");
|
console.log("🧠 Precargando datos del sistema (appointments/services)...");
|
||||||
const apptInfoMap = await preloadAppointmentsInfo(referenciasNormalizadas);
|
const apptInfoMap = await preloadAppointmentsInfo(referenciasNormalizadas);
|
||||||
const servicesSet = await preloadServicesExistence(referenciasNormalizadas);
|
const servicesSet = await preloadServicesExistence(referenciasNormalizadas);
|
||||||
console.log(`🧾 En appointments: ${apptInfoMap.size} | En services: ${servicesSet.size}`);
|
console.log(`🧾 En appointments: ${apptInfoMap.size} | En services: ${servicesSet.size}`);
|
||||||
|
|
||||||
// ✅ ids que ya estaban en homeserve_pendientes
|
// ✅ ids que ya estaban en homeserve_pendientes
|
||||||
console.log("📦 Cargando documentos actuales de homeserve_pendientes...");
|
console.log("📦 Cargando documentos actuales de homeserve_pendientes...");
|
||||||
const pendingDocIds = await getAllPendingDocIds();
|
const pendingDocIds = await getAllPendingDocIds();
|
||||||
const pendingSet = new Set(pendingDocIds);
|
const pendingSet = new Set(pendingDocIds);
|
||||||
|
|
||||||
// --- CONTADORES ---
|
// --- CONTADORES ---
|
||||||
let actualizados = 0;
|
let actualizados = 0;
|
||||||
let nuevos = 0;
|
let nuevos = 0;
|
||||||
|
|
@ -185,14 +207,18 @@ async function runRobot() {
|
||||||
let saltadosSinDatos = 0;
|
let saltadosSinDatos = 0;
|
||||||
let marcadosInSystem = 0;
|
let marcadosInSystem = 0;
|
||||||
let archivadosCompleted = 0;
|
let archivadosCompleted = 0;
|
||||||
let marcadosMissingNoArchive = 0;
|
let archivadosMissing = 0;
|
||||||
|
let archivadosBlocked = 0;
|
||||||
|
|
||||||
for (const ref of referenciasEnWeb) {
|
for (const ref of referenciasEnWeb) {
|
||||||
const normalized = normalizeServiceNumber(ref);
|
const normalized = normalizeServiceNumber(ref);
|
||||||
if (!normalized) continue;
|
if (!normalized) continue;
|
||||||
|
|
||||||
const appt = apptInfoMap.get(normalized);
|
const appt = apptInfoMap.get(normalized);
|
||||||
const existsInAppointments = !!appt;
|
const existsInAppointments = !!appt;
|
||||||
const existsInServices = servicesSet.has(normalized);
|
const existsInServices = servicesSet.has(normalized);
|
||||||
const existsInSystem = existsInAppointments || existsInServices;
|
const existsInSystem = existsInAppointments || existsInServices;
|
||||||
|
|
||||||
// ✅ Si ya existe en sistema:
|
// ✅ Si ya existe en sistema:
|
||||||
// - SI completed => archived
|
// - SI completed => archived
|
||||||
// - SI NO completed => in_system
|
// - SI NO completed => in_system
|
||||||
|
|
@ -227,24 +253,36 @@ async function runRobot() {
|
||||||
// No hace falta scrapear si ya existe el doc y ya está integrado
|
// No hace falta scrapear si ya existe el doc y ya está integrado
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si existe en sistema pero NO existe en homeserve_pendientes (no lo tenías):
|
// Si existe en sistema pero NO existe en homeserve_pendientes (no lo tenías):
|
||||||
// lo tratamos como antes (scrape) y lo guardamos con status in_system (no archived).
|
// si está completed, no creamos doc nuevo (no aporta mucho).
|
||||||
// Si está completed, no creamos doc nuevo (no aporta mucho).
|
|
||||||
if (existsInSystem && !pendingSet.has(normalized) && existsInAppointments && isCompletedStatus(appt.status)) {
|
if (existsInSystem && !pendingSet.has(normalized) && existsInAppointments && isCompletedStatus(appt.status)) {
|
||||||
archivadosCompleted++;
|
archivadosCompleted++;
|
||||||
console.log(`⏭️ SALTADO (completed y sin doc pendiente): ${normalized}`);
|
console.log(`⏭️ SALTADO (completed y sin doc pendiente): ${normalized}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navegar a la lista y click
|
// Navegar a la lista y click
|
||||||
await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=lista_servicios_total');
|
await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=lista_servicios_total');
|
||||||
try {
|
try {
|
||||||
await page.click(`text="${normalized}"`, { timeout: 5000 });
|
await page.click(`text="${normalized}"`, { timeout: 5000 });
|
||||||
await page.waitForTimeout(1500);
|
await page.waitForTimeout(1500);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
// 🔥 CAMBIO: si está bloqueado/no accesible, lo archivamos para que NO salga como "nuevo"
|
||||||
saltadosBloqueo++;
|
saltadosBloqueo++;
|
||||||
console.warn(`⛔ SALTADO (bloqueado/no accesible): ${normalized}`);
|
console.warn(`⛔ SALTADO (bloqueado/no accesible): ${normalized}`);
|
||||||
|
|
||||||
|
const docRef = db.collection(COLLECTION_NAME).doc(normalized);
|
||||||
|
await archiveDoc(docRef, nowISO, "blocked_or_unreadable", {
|
||||||
|
blockedAt: nowISO,
|
||||||
|
missingFromHomeServe: false,
|
||||||
|
lastSeenAt: nowISO
|
||||||
|
});
|
||||||
|
archivadosBlocked++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const detalles = await page.evaluate(() => {
|
const detalles = await page.evaluate(() => {
|
||||||
const d = {};
|
const d = {};
|
||||||
const filas = Array.from(document.querySelectorAll('tr'));
|
const filas = Array.from(document.querySelectorAll('tr'));
|
||||||
|
|
@ -268,19 +306,32 @@ async function runRobot() {
|
||||||
});
|
});
|
||||||
return d;
|
return d;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!hasMinimumData(detalles)) {
|
if (!hasMinimumData(detalles)) {
|
||||||
|
// 🔥 CAMBIO: si no hay datos mínimos, también lo archivamos para que NO se quede "nuevo"
|
||||||
saltadosSinDatos++;
|
saltadosSinDatos++;
|
||||||
console.warn(`⛔ SALTADO (sin datos mínimos): ${normalized}`);
|
console.warn(`⛔ SALTADO (sin datos mínimos): ${normalized}`);
|
||||||
|
|
||||||
|
const docRef = db.collection(COLLECTION_NAME).doc(normalized);
|
||||||
|
await archiveDoc(docRef, nowISO, "missing_minimum_data", {
|
||||||
|
missingFromHomeServe: false,
|
||||||
|
lastSeenAt: nowISO
|
||||||
|
});
|
||||||
|
archivadosBlocked++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullAddress = `${detalles.addressPart || ""} ${detalles.cityPart || ""}`.trim();
|
const fullAddress = `${detalles.addressPart || ""} ${detalles.cityPart || ""}`.trim();
|
||||||
let rawCompany = detalles.company || "";
|
let rawCompany = detalles.company || "";
|
||||||
if (rawCompany && !rawCompany.toUpperCase().includes("HOMESERVE")) {
|
if (rawCompany && !rawCompany.toUpperCase().includes("HOMESERVE")) {
|
||||||
rawCompany = `HOMESERVE - ${rawCompany}`;
|
rawCompany = `HOMESERVE - ${rawCompany}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const docRef = db.collection(COLLECTION_NAME).doc(normalized);
|
const docRef = db.collection(COLLECTION_NAME).doc(normalized);
|
||||||
const docSnapshot = await docRef.get();
|
const docSnapshot = await docRef.get();
|
||||||
const datosAntiguos = docSnapshot.exists ? docSnapshot.data() : null;
|
const datosAntiguos = docSnapshot.exists ? docSnapshot.data() : null;
|
||||||
|
|
||||||
// Estado según sistema (si existe, es in_system; si no, pendiente_validacion)
|
// Estado según sistema (si existe, es in_system; si no, pendiente_validacion)
|
||||||
let status = "pendiente_validacion";
|
let status = "pendiente_validacion";
|
||||||
let integratedIn = "";
|
let integratedIn = "";
|
||||||
|
|
@ -294,6 +345,7 @@ async function runRobot() {
|
||||||
integratedIn = "services";
|
integratedIn = "services";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const servicioFinal = {
|
const servicioFinal = {
|
||||||
serviceNumber: normalized,
|
serviceNumber: normalized,
|
||||||
clientName: detalles.clientName || "Desconocido",
|
clientName: detalles.clientName || "Desconocido",
|
||||||
|
|
@ -310,7 +362,9 @@ async function runRobot() {
|
||||||
updatedAt: nowISO,
|
updatedAt: nowISO,
|
||||||
missingFromHomeServe: false,
|
missingFromHomeServe: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!datosAntiguos) servicioFinal.createdAt = nowISO;
|
if (!datosAntiguos) servicioFinal.createdAt = nowISO;
|
||||||
|
|
||||||
if (!datosAntiguos) {
|
if (!datosAntiguos) {
|
||||||
await docRef.set(servicioFinal);
|
await docRef.set(servicioFinal);
|
||||||
console.log(`NUEVO: ${normalized} (status=${status})`);
|
console.log(`NUEVO: ${normalized} (status=${status})`);
|
||||||
|
|
@ -322,6 +376,7 @@ async function runRobot() {
|
||||||
const cambioCliente = (datosAntiguos.clientName || "") !== (servicioFinal.clientName || "");
|
const cambioCliente = (datosAntiguos.clientName || "") !== (servicioFinal.clientName || "");
|
||||||
const cambioCompany = (datosAntiguos.company || "") !== (servicioFinal.company || "");
|
const cambioCompany = (datosAntiguos.company || "") !== (servicioFinal.company || "");
|
||||||
const cambioStatus = (datosAntiguos.status || "") !== (servicioFinal.status || "");
|
const cambioStatus = (datosAntiguos.status || "") !== (servicioFinal.status || "");
|
||||||
|
|
||||||
if (cambioEstado || cambioTelefono || cambioAddress || cambioCliente || cambioCompany || cambioStatus) {
|
if (cambioEstado || cambioTelefono || cambioAddress || cambioCliente || cambioCompany || cambioStatus) {
|
||||||
console.log(`♻️ ACTUALIZADO: ${normalized}`);
|
console.log(`♻️ ACTUALIZADO: ${normalized}`);
|
||||||
await docRef.set(servicioFinal, { merge: true });
|
await docRef.set(servicioFinal, { merge: true });
|
||||||
|
|
@ -329,6 +384,7 @@ async function runRobot() {
|
||||||
} else {
|
} else {
|
||||||
await docRef.set({ lastSeenAt: nowISO, updatedAt: nowISO, missingFromHomeServe: false }, { merge: true });
|
await docRef.set({ lastSeenAt: nowISO, updatedAt: nowISO, missingFromHomeServe: false }, { merge: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si estaba archivado pero reaparece y NO está completed => lo “desarchivamos”
|
// Si estaba archivado pero reaparece y NO está completed => lo “desarchivamos”
|
||||||
if ((datosAntiguos.status || "") === "archived") {
|
if ((datosAntiguos.status || "") === "archived") {
|
||||||
const shouldStayArchived = existsInAppointments && isCompletedStatus(appt?.status);
|
const shouldStayArchived = existsInAppointments && isCompletedStatus(appt?.status);
|
||||||
|
|
@ -343,26 +399,30 @@ async function runRobot() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Gestionar lo que ha desaparecido de HomeServe
|
// ✅ Gestionar lo que ha desaparecido de HomeServe
|
||||||
// Regla NUEVA: NO archivar por desaparecer; SOLO archivar si completed.
|
console.log("🗄️ Revisando los que han desaparecido de HomeServe...");
|
||||||
console.log("🗄️ Revisando los que han desaparecido de HomeServe (sin archivar salvo completed)...");
|
|
||||||
const missingIds = pendingDocIds
|
const missingIds = pendingDocIds
|
||||||
.map(normalizeServiceNumber)
|
.map(normalizeServiceNumber)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.filter(sn => !webSet.has(sn));
|
.filter(sn => !webSet.has(sn));
|
||||||
|
|
||||||
// precargamos su estado en sistema para decidir
|
// precargamos su estado en sistema para decidir
|
||||||
const apptMissingMap = await preloadAppointmentsInfo(missingIds);
|
const apptMissingMap = await preloadAppointmentsInfo(missingIds);
|
||||||
const servicesMissingSet = await preloadServicesExistence(missingIds);
|
const servicesMissingSet = await preloadServicesExistence(missingIds);
|
||||||
|
|
||||||
for (const sn of missingIds) {
|
for (const sn of missingIds) {
|
||||||
const ref = db.collection(COLLECTION_NAME).doc(sn);
|
const ref = db.collection(COLLECTION_NAME).doc(sn);
|
||||||
const snap = await ref.get();
|
const snap = await ref.get();
|
||||||
const data = snap.exists ? (snap.data() || {}) : {};
|
const data = snap.exists ? (snap.data() || {}) : {};
|
||||||
|
|
||||||
const appt = apptMissingMap.get(sn);
|
const appt = apptMissingMap.get(sn);
|
||||||
const existsInAppointments = !!appt;
|
const existsInAppointments = !!appt;
|
||||||
const existsInServices = servicesMissingSet.has(sn);
|
const existsInServices = servicesMissingSet.has(sn);
|
||||||
const existsInSystem = existsInAppointments || existsInServices;
|
const existsInSystem = existsInAppointments || existsInServices;
|
||||||
|
|
||||||
if (existsInAppointments && isCompletedStatus(appt.status)) {
|
if (existsInAppointments && isCompletedStatus(appt.status)) {
|
||||||
// ✅ SOLO AQUÍ archivamos
|
// ✅ SOLO AQUÍ archivamos por completed
|
||||||
if ((data.status || "") !== "archived") {
|
if ((data.status || "") !== "archived") {
|
||||||
await ref.set({
|
await ref.set({
|
||||||
status: "archived",
|
status: "archived",
|
||||||
|
|
@ -375,9 +435,16 @@ async function runRobot() {
|
||||||
missingAt: nowISO
|
missingAt: nowISO
|
||||||
}, { merge: true });
|
}, { merge: true });
|
||||||
archivadosCompleted++;
|
archivadosCompleted++;
|
||||||
|
} else {
|
||||||
|
await ref.set({
|
||||||
|
updatedAt: nowISO,
|
||||||
|
missingFromHomeServe: true,
|
||||||
|
missingAt: nowISO
|
||||||
|
}, { merge: true });
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existsInSystem) {
|
if (existsInSystem) {
|
||||||
// Está en tu sistema pero no está completed => en sistema, NO archived
|
// Está en tu sistema pero no está completed => en sistema, NO archived
|
||||||
await ref.set({
|
await ref.set({
|
||||||
|
|
@ -391,20 +458,27 @@ async function runRobot() {
|
||||||
marcadosInSystem++;
|
marcadosInSystem++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// No está en sistema y ha desaparecido de HomeServe -> NO archivar, solo marcar missing
|
|
||||||
|
// 🔥 CAMBIO: No está en sistema y ha desaparecido de HomeServe -> ARCHIVAR
|
||||||
|
// (para que NO aparezca como nuevo/verde y te quede rastro de que existió)
|
||||||
await ref.set({
|
await ref.set({
|
||||||
|
status: "archived",
|
||||||
|
archivedReason: "missing_from_homeserve",
|
||||||
|
archivedAt: nowISO,
|
||||||
missingFromHomeServe: true,
|
missingFromHomeServe: true,
|
||||||
missingAt: nowISO,
|
missingAt: nowISO,
|
||||||
updatedAt: nowISO
|
updatedAt: nowISO
|
||||||
}, { merge: true });
|
}, { merge: true });
|
||||||
marcadosMissingNoArchive++;
|
archivadosMissing++;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`🏁 FIN V7.5: ${nuevos} nuevos, ${actualizados} actualizados, ` +
|
`🏁 FIN V7.6: ${nuevos} nuevos, ${actualizados} actualizados, ` +
|
||||||
`${saltadosBloqueo} saltados (bloqueados), ${saltadosSinDatos} saltados (sin datos), ` +
|
`${saltadosBloqueo} bloqueados/no accesibles, ${saltadosSinDatos} sin datos mínimos, ` +
|
||||||
`${marcadosInSystem} marcados in_system, ${archivadosCompleted} archivados (completed), ` +
|
`${marcadosInSystem} marcados in_system, ${archivadosCompleted} archivados (completed), ` +
|
||||||
`${marcadosMissingNoArchive} marcados missing (NO archived).`
|
`${archivadosMissing} archivados (missing), ${archivadosBlocked} archivados (blocked/sin_datos).`
|
||||||
);
|
);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ ERROR:', error.message);
|
console.error('❌ ERROR:', error.message);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|
@ -422,4 +496,5 @@ async function runRobot() {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runRobot();
|
runRobot();
|
||||||
Loading…
Reference in New Issue