Actualizar worker-multi-estado.js
This commit is contained in:
parent
dda9dd1a1b
commit
244678b69d
|
|
@ -22,6 +22,7 @@ function toServerTimestamp() { return admin.firestore.FieldValue.serverTimestamp
|
|||
function timeToMultiValue(timeStr) {
|
||||
if (!timeStr) return "";
|
||||
const [h, m] = timeStr.split(':').map(Number);
|
||||
// Formato interno de Multi: segundos desde medianoche
|
||||
return String((h * 3600) + (m * 60));
|
||||
}
|
||||
|
||||
|
|
@ -40,19 +41,6 @@ function initFirebase() {
|
|||
return admin.firestore();
|
||||
}
|
||||
|
||||
// --- HELPERS AVANZADOS ---
|
||||
// Esta función es la clave: obliga a la web a reconocer el cambio
|
||||
async function triggerEvents(page, selector) {
|
||||
await page.evaluate((sel) => {
|
||||
const el = document.querySelector(sel);
|
||||
if (el) {
|
||||
el.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
el.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
||||
}
|
||||
}, selector);
|
||||
}
|
||||
|
||||
// --- LOGIN ---
|
||||
async function loginMulti(page, db) {
|
||||
let user = "", pass = "";
|
||||
|
|
@ -84,6 +72,17 @@ async function withBrowser(fn) {
|
|||
try { return await fn(page); } finally { await browser.close().catch(() => {}); }
|
||||
}
|
||||
|
||||
// --- HELPERS DE EVENTOS ---
|
||||
async function forceUpdate(elementHandle) {
|
||||
if (elementHandle) {
|
||||
await elementHandle.evaluate(el => {
|
||||
el.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
el.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- LÓGICA PRINCIPAL ---
|
||||
async function processChangeState(page, db, jobData) {
|
||||
const { serviceNumber, reasonValue, comment, dateStr, timeStr } = jobData;
|
||||
|
|
@ -93,76 +92,76 @@ async function processChangeState(page, db, jobData) {
|
|||
|
||||
// 2. IR AL SERVICIO
|
||||
const targetUrl = `${CONFIG.MULTI_ACTION_BASE}?reparacion=${serviceNumber}&modo=0&navid=%2Fw3multi%2Ffrepasos_new.php%FDGET%FDrefresh%3D1%FC`;
|
||||
console.log(`📂 Abriendo servicio ${serviceNumber}...`);
|
||||
console.log(`📂 Abriendo servicio 27867185...`); // Log limpio para depurar
|
||||
await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
||||
|
||||
// Esperar formulario
|
||||
await page.waitForSelector('select.answer-select', { timeout: 20000 });
|
||||
await page.waitForTimeout(2000); // Esperar a que Angular "se asiente"
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
console.log('📝 Rellenando formulario (Modo Fuerza Bruta)...');
|
||||
console.log('📝 Rellenando formulario (Modo Preciso)...');
|
||||
|
||||
// 3. MOTIVO (SELECT)
|
||||
// Seleccionamos y forzamos el evento 'change'
|
||||
const reasonSel = 'select.answer-select';
|
||||
await page.selectOption(reasonSel, String(reasonValue));
|
||||
await triggerEvents(page, reasonSel);
|
||||
await page.waitForTimeout(500);
|
||||
// 3. MOTIVO (Primer select de la página suele ser el motivo)
|
||||
const reasonSel = page.locator('select.answer-select').first();
|
||||
await reasonSel.selectOption(String(reasonValue));
|
||||
await forceUpdate(await reasonSel.elementHandle());
|
||||
|
||||
// 4. COMENTARIO
|
||||
if (comment) {
|
||||
const commentSel = 'textarea[formcontrolname="comment"]';
|
||||
await page.fill(commentSel, comment);
|
||||
await triggerEvents(page, commentSel);
|
||||
const commentBox = page.locator('textarea[formcontrolname="comment"]');
|
||||
await commentBox.fill(comment);
|
||||
await forceUpdate(await commentBox.elementHandle());
|
||||
}
|
||||
|
||||
// 5. FECHA
|
||||
if (dateStr) {
|
||||
const dateSel = 'input[type="date"]';
|
||||
await page.fill(dateSel, dateStr);
|
||||
await triggerEvents(page, dateSel);
|
||||
// Truco: hacer click fuera para validar
|
||||
const dateInput = page.locator('input[type="date"]');
|
||||
await dateInput.fill(dateStr);
|
||||
await forceUpdate(await dateInput.elementHandle());
|
||||
// Click fuera para asegurar validación de fecha
|
||||
await page.click('body');
|
||||
}
|
||||
|
||||
// 6. HORA
|
||||
// 6. HORA (El punto crítico)
|
||||
if (timeStr) {
|
||||
const secondsValue = timeToMultiValue(timeStr);
|
||||
// Buscamos el select de hora. Puede ser el segundo select de la página.
|
||||
// Usamos una estrategia de búsqueda por valor para ser precisos.
|
||||
const foundTime = await page.evaluate((val) => {
|
||||
const selects = Array.from(document.querySelectorAll('select'));
|
||||
for (const s of selects) {
|
||||
// Si tiene la opción con ese valor (ej: 28800)
|
||||
if (s.querySelector(`option[value="${val}"]`)) {
|
||||
s.value = val;
|
||||
s.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
s.dispatchEvent(new Event('blur', { bubbles: true }));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}, secondsValue);
|
||||
const secondsValue = timeToMultiValue(timeStr); // ej: "28800" para 08:00
|
||||
console.log(`🕒 Intentando poner hora: ${timeStr} (Valor interno: ${secondsValue})`);
|
||||
|
||||
if (!foundTime) console.log(`⚠️ Advertencia: No encontré donde poner la hora ${timeStr}`);
|
||||
// ESTRATEGIA INFALIBLE: Buscar el select que contiene esa opción específica usando XPath
|
||||
// "Búscame un <select> que tenga dentro una <option> con value='28800'"
|
||||
const timeSelectHandle = await page.$(`xpath=//select[.//option[@value="${secondsValue}"]]`);
|
||||
|
||||
if (timeSelectHandle) {
|
||||
await timeSelectHandle.selectOption(secondsValue);
|
||||
await forceUpdate(timeSelectHandle);
|
||||
console.log('✅ Hora seleccionada correctamente.');
|
||||
} else {
|
||||
console.log(`⚠️ ERROR GRAVE: No encuentro ningún desplegable que tenga la opción de valor "${secondsValue}".`);
|
||||
// Fallback desesperado: intentar ponerlo en el segundo select que encuentre
|
||||
const allSelects = await page.$$('select');
|
||||
if (allSelects.length > 1) {
|
||||
console.log('⚠️ Intentando fallback en el segundo select...');
|
||||
await allSelects[1].selectOption(secondsValue).catch(() => {});
|
||||
await forceUpdate(allSelects[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// 7. GUARDAR - VERIFICACIÓN DE ESTADO DEL BOTÓN
|
||||
const btnSelector = 'button.form-container-button-submit';
|
||||
const btn = page.locator(btnSelector);
|
||||
// 7. GUARDAR
|
||||
const btn = page.locator('button.form-container-button-submit');
|
||||
|
||||
// Comprobar si está habilitado
|
||||
// Verificación final antes de clickar
|
||||
if (await btn.isDisabled()) {
|
||||
console.log('⛔ El botón Guardar sigue deshabilitado. Intentando "despertar" el formulario...');
|
||||
// Intentar hacer focus/blur en el comentario otra vez
|
||||
await page.focus('textarea[formcontrolname="comment"]');
|
||||
console.log('⛔ Botón deshabilitado. Intentando reactivación final...');
|
||||
// A veces hacer click en el comentario ayuda a que Angular se entere
|
||||
await page.click('textarea[formcontrolname="comment"]');
|
||||
await page.keyboard.press('Tab');
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
if (await btn.isDisabled()) {
|
||||
throw new Error("IMPOSIBLE GUARDAR: El formulario no valida los datos (botón gris). Revisa si la fecha/hora son correctas.");
|
||||
throw new Error(`IMPOSIBLE GUARDAR: Formulario incompleto. ¿La hora ${timeStr} es válida para este servicio?`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,34 +169,28 @@ async function processChangeState(page, db, jobData) {
|
|||
await btn.click();
|
||||
|
||||
// 8. VERIFICAR RESULTADO
|
||||
// Esperamos a ver si sale mensaje de éxito o error
|
||||
await page.waitForTimeout(3000);
|
||||
await page.waitForTimeout(4000);
|
||||
|
||||
// Buscamos mensajes en la pantalla
|
||||
// Captura de mensajes de éxito/error de la web
|
||||
const message = await page.evaluate(() => {
|
||||
// En tu HTML vi esta etiqueta: <encastrables-success-error-message>
|
||||
const msgEl = document.querySelector('encastrables-success-error-message');
|
||||
const errorEl = document.querySelector('.form-container-error');
|
||||
const successEl = document.querySelector('.form-container-success');
|
||||
const err = document.querySelector('.form-container-error');
|
||||
const ok = document.querySelector('.form-container-success');
|
||||
const toast = document.querySelector('encastrables-success-error-message');
|
||||
|
||||
if (errorEl) return `ERROR WEB: ${errorEl.innerText}`;
|
||||
if (successEl) return `EXITO WEB: ${successEl.innerText}`;
|
||||
if (msgEl && msgEl.innerText.trim().length > 0) return `MSG: ${msgEl.innerText}`;
|
||||
if (err && err.innerText) return `ERROR WEB: ${err.innerText}`;
|
||||
if (ok && ok.innerText) return `EXITO WEB: ${ok.innerText}`;
|
||||
if (toast && toast.innerText) return `MSG: ${toast.innerText}`;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (message && message.includes('ERROR')) {
|
||||
throw new Error(message);
|
||||
}
|
||||
console.log(`ℹ️ Estado final: ${message || 'Sin mensaje (Redirección correcta)'}`);
|
||||
|
||||
console.log(`ℹ️ Resultado pantalla: ${message || 'Sin mensaje explícito (probablemente OK)'}`);
|
||||
if (message && message.includes('ERROR')) throw new Error(message);
|
||||
|
||||
// Si la URL cambió, es buena señal
|
||||
const currentUrl = page.url();
|
||||
return { success: true, finalUrl: currentUrl, webMessage: message };
|
||||
return { success: true, finalUrl: page.url() };
|
||||
}
|
||||
|
||||
// --- WORKER LOOP ---
|
||||
// --- WORKER ---
|
||||
async function claimJobById(db, jobId) {
|
||||
const ref = db.collection(CONFIG.QUEUE_COLLECTION).doc(jobId);
|
||||
return await db.runTransaction(async (tx) => {
|
||||
|
|
@ -249,7 +242,7 @@ function startWorker(db) {
|
|||
db.collection(CONFIG.QUEUE_COLLECTION).where('status', '==', 'PENDING').onSnapshot(s => {
|
||||
s.docChanges().forEach(c => { if(c.type==='added') { queue.push(c.doc.id); run(); } });
|
||||
});
|
||||
console.log('🚀 Worker Multiasistencia Estados (FUERZA BRUTA) LISTO.');
|
||||
console.log('🚀 Worker Multiasistencia Estados (BÚSQUEDA XPATH) LISTO.');
|
||||
}
|
||||
|
||||
const db = initFirebase();
|
||||
|
|
|
|||
Loading…
Reference in New Issue