El script que reemplaza el registro manual, uno por uno, de operaciones repetitivas en un portal web — adaptado aquí a un portal ficticio para poder compartirlo sin exponer nada real.
Carga el Excel del periodo y confirma que exista el documento de sustento de cada fila antes de abrir el navegador.
Abre el portal, completa el formulario y adjunta el archivo correspondiente para cada registro, en orden.
Busca la confirmación exacta del código esperado. Si no la encuentra, el proceso se detiene — nunca reintenta a ciegas.
Esta es una versión simplificada, adaptada para funcionar contra un portal ficticio propio — no contra ningún sistema real. Conserva la misma lógica de validación y verificación del proceso original.
"""
DEMO PARA VIDEO / LINKEDIN — datos 100% ficticios.
Este script NO toca ningún sistema real. Corre contra portal_demo.html,
una página local hecha solo para esta demostración.
Cómo usarlo:
1. Deja este archivo y portal_demo.html en la misma carpeta, junto con
la carpeta dummy_pdfs/.
2. Activa tu entorno virtual (venv\\Scripts\\activate).
3. Corre: python demo_registro.py -- esto abre Chromium y el script
se queda ESPERANDO (no hace nada todavía).
4. Recién ahí abre el Bloc de recortes (Snipping Tool), modo grabación
de video, dibuja el área a grabar (la ventana de Chromium) y dale
Iniciar.
5. Vuelve a la consola y presiona Enter -- ahí arranca la automatización.
6. Cuando termine y Chromium se cierre solo, detén la grabación.
"""
import os
import time
from playwright.sync_api import sync_playwright
CARPETA = os.path.dirname(os.path.abspath(__file__))
RUTA_PORTAL = "file://" + os.path.join(CARPETA, "portal_demo.html")
BANCOS = ["Banco Alfa", "Banco Beta", "Banco Gamma", "Banco Delta"]
# Datos 100% ficticios, generados solo para esta demo.
LOTE_DEMO = []
for i in range(1, 13):
codigo = f"DEMO-2609-{i:03d}"
LOTE_DEMO.append({
"codigo": codigo,
"fecha": "2026-09-04",
"banco": BANCOS[(i - 1) % len(BANCOS)],
"moneda": "SOL" if i % 2 == 0 else "USD",
"cuenta": f"00{i:02d}-000{i}-11-22334455{i:02d}",
"motivo": f"NUEVO DEPÓSITO {BANCOS[(i - 1) % len(BANCOS)]}",
"pdf": os.path.join(CARPETA, "dummy_pdfs", f"{codigo}.pdf"),
})
print(f"Lote de demostración: {len(LOTE_DEMO)} registros ficticios\n")
with sync_playwright() as p:
browser = p.chromium.launch(headless=False, args=["--start-maximized"])
page = browser.new_page(no_viewport=True)
page.goto(RUTA_PORTAL)
input(
"\n>>> El navegador ya está abierto y visible. "
"Haz clic sobre esa ventana para que quede enfocada, "
"empieza tu grabación de pantalla (Win + Alt + R), "
"y RECIÉN AHÍ vuelve aquí y presiona Enter para arrancar...\n"
)
for idx, fila in enumerate(LOTE_DEMO):
page.click("#btn-nuevo")
time.sleep(0.4)
page.fill("#fecha", fila["fecha"])
page.select_option("#banco", fila["banco"])
page.select_option("#moneda", fila["moneda"])
page.fill("#cuenta", fila["cuenta"])
page.fill("#motivo", fila["motivo"])
time.sleep(0.3)
page.set_input_files("#archivo", fila["pdf"])
time.sleep(0.3)
print(f"✅ Documento adjuntado: {os.path.basename(fila['pdf'])}")
page.click("#btn-enviar")
time.sleep(0.3)
page.click("#btn-si")
time.sleep(0.6)
codigo_generado = page.evaluate("window.__ultimoCodigo")
if codigo_generado != fila["codigo"]:
print(f"⚠️ No coincide: esperado {fila['codigo']}, obtenido {codigo_generado}")
break
print(f"✅ CONFIRMADO: código {codigo_generado} enviado correctamente")
print(f"🎉 Registro {idx + 1}/{len(LOTE_DEMO)} completado\n")
time.sleep(0.3)
print("✅ Lote de demostración finalizado.")
time.sleep(2)
browser.close()
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Portal de Registro de Operaciones — ENTORNO DE DEMOSTRACIÓN</title>
<style>
:root {
--azul: #0b3d63;
--azul-claro: #eaf2f8;
--verde: #1e8449;
--borde: #cfd8dc;
}
* { box-sizing: border-box; }
body {
font-family: "Segoe UI", Arial, sans-serif;
background: #f4f6f8;
margin: 0;
color: #1c2833;
}
header {
background: var(--azul);
color: white;
padding: 18px 28px;
}
header h1 { margin: 0; font-size: 20px; }
header .tag {
display: inline-block;
margin-top: 6px;
background: #d68910;
color: #1c2833;
font-size: 12px;
font-weight: bold;
padding: 2px 10px;
border-radius: 12px;
}
main {
max-width: 780px;
margin: 24px auto;
background: white;
border: 1px solid var(--borde);
border-radius: 6px;
padding: 24px 28px;
}
.barra-superior {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 18px;
}
.contador {
background: var(--azul-claro);
padding: 8px 16px;
border-radius: 6px;
font-weight: bold;
color: var(--azul);
}
button {
background: var(--azul);
color: white;
border: none;
padding: 10px 18px;
border-radius: 5px;
font-size: 14px;
cursor: pointer;
}
button:hover { background: #08304d; }
button.secundario {
background: white;
color: var(--azul);
border: 1px solid var(--azul);
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
font-size: 13px;
}
th, td {
text-align: left;
padding: 8px 6px;
border-bottom: 1px solid var(--borde);
}
th { color: #566573; font-weight: 600; }
.vacio { color: #909497; font-style: italic; padding: 14px 0; }
#formulario { display: none; margin-top: 10px; }
.campo { margin-bottom: 14px; }
.campo label {
display: block;
font-size: 13px;
font-weight: 600;
margin-bottom: 4px;
color: #34495e;
}
.campo input, .campo select {
width: 100%;
padding: 8px 10px;
border: 1px solid var(--borde);
border-radius: 4px;
font-size: 14px;
}
.fila-doble { display: flex; gap: 16px; }
.fila-doble .campo { flex: 1; }
#overlay {
display: none;
position: fixed; inset: 0;
background: rgba(0,0,0,0.35);
align-items: center; justify-content: center;
}
#dialogo {
background: white;
padding: 22px 26px;
border-radius: 8px;
max-width: 360px;
text-align: center;
}
#dialogo p { margin-top: 0; }
#dialogo .acciones { display: flex; gap: 10px; justify-content: center; margin-top: 16px; }
#toast {
display: none;
position: fixed;
bottom: 24px; right: 24px;
background: #eafaf1;
border: 1px solid var(--verde);
color: #145a32;
padding: 14px 18px;
border-radius: 6px;
max-width: 360px;
font-size: 13px;
box-shadow: 0 2px 10px rgba(0,0,0,0.15);
}
#toast b { display: block; margin-bottom: 4px; }
footer {
text-align: center;
color: #909497;
font-size: 12px;
margin: 30px 0;
}
</style>
</head>
<body>
<header>
<h1>Portal de Registro de Operaciones</h1>
<span class="tag">ENTORNO DE DEMOSTRACIÓN — DATOS FICTICIOS</span>
</header>
<main>
<div class="barra-superior">
<div class="contador">Registros completados: <span id="contador">0</span></div>
<button id="btn-nuevo">Nuevo registro</button>
</div>
<div id="listado">
<table>
<thead>
<tr><th>#</th><th>Código</th><th>Entidad</th><th>Estado</th></tr>
</thead>
<tbody id="cuerpo-tabla"></tbody>
</table>
<div id="vacio" class="vacio">No se ha encontrado ningún registro.</div>
</div>
<form id="formulario">
<div class="fila-doble">
<div class="campo">
<label for="fecha">Fecha de operación</label>
<input type="date" id="fecha" />
</div>
<div class="campo">
<label for="moneda">Moneda</label>
<select id="moneda">
<option value="SOL">Soles</option>
<option value="USD">Dólares</option>
</select>
</div>
</div>
<div class="fila-doble">
<div class="campo">
<label for="banco">Entidad financiera</label>
<select id="banco">
<option value="Banco Alfa">Banco Alfa</option>
<option value="Banco Beta">Banco Beta</option>
<option value="Banco Gamma">Banco Gamma</option>
<option value="Banco Delta">Banco Delta</option>
</select>
</div>
<div class="campo">
<label for="cuenta">Número de cuenta</label>
<input type="text" id="cuenta" />
</div>
</div>
<div class="campo">
<label for="motivo">Motivo</label>
<input type="text" id="motivo" />
</div>
<div class="campo">
<label for="archivo">Adjuntar documento sustentatorio</label>
<input type="file" id="archivo" />
<div id="nombre-archivo" style="font-size:12px;color:#1e8449;margin-top:4px;"></div>
</div>
<button type="button" id="btn-enviar">Guardar y Enviar</button>
<button type="button" class="secundario" id="btn-cancelar">Cancelar</button>
</form>
</main>
<footer>Portal de Registro de Operaciones · Entorno de demostración · v-DEMO</footer>
<div id="overlay">
<div id="dialogo">
<p>¿Confirma el envío de este registro?</p>
<div class="acciones">
<button id="btn-si">Sí</button>
<button class="secundario" id="btn-no">No</button>
</div>
</div>
</div>
<div id="toast"></div>
<script>
let contador = 0;
let siguienteCodigo = 1;
const btnNuevo = document.getElementById('btn-nuevo');
const formulario = document.getElementById('formulario');
const listado = document.getElementById('listado');
const overlay = document.getElementById('overlay');
const toast = document.getElementById('toast');
const cuerpoTabla = document.getElementById('cuerpo-tabla');
const vacio = document.getElementById('vacio');
btnNuevo.addEventListener('click', () => {
listado.style.display = 'none';
formulario.style.display = 'block';
formulario.reset();
document.getElementById('nombre-archivo').textContent = '';
});
document.getElementById('btn-cancelar').addEventListener('click', () => {
formulario.style.display = 'none';
listado.style.display = 'block';
});
document.getElementById('archivo').addEventListener('change', (e) => {
const f = e.target.files[0];
document.getElementById('nombre-archivo').textContent = f ? ('Archivo adjuntado: ' + f.name) : '';
});
document.getElementById('btn-enviar').addEventListener('click', () => {
overlay.style.display = 'flex';
});
document.getElementById('btn-no').addEventListener('click', () => {
overlay.style.display = 'none';
});
document.getElementById('btn-si').addEventListener('click', () => {
overlay.style.display = 'none';
const codigo = 'DEMO-2609-' + String(siguienteCodigo).padStart(3, '0');
siguienteCodigo++;
const banco = document.getElementById('banco').value;
contador++;
document.getElementById('contador').textContent = contador;
const fila = document.createElement('tr');
fila.innerHTML = `<td>${contador}</td><td>${codigo}</td><td>${banco}</td><td>Enviado</td>`;
cuerpoTabla.prepend(fila);
vacio.style.display = 'none';
toast.innerHTML = `<b>Éxito</b>El registro con código ${codigo} ha sido enviado correctamente.`;
toast.style.display = 'block';
setTimeout(() => { toast.style.display = 'none'; }, 4000);
formulario.style.display = 'none';
listado.style.display = 'block';
window.__ultimoCodigo = codigo;
});
</script>
</body>
</html>