Quita en un clic todos los Bin IDs (⊗) asociados a una locación en Providence, para que deje de dar "Location … is not empty" en putaway.
Arrastra este botón a la barra de marcadores:
Seguridad: si el primer ⊗ no quita nada, o Providence muestra un error, se detiene solo. Solo actúa sobre filas cuyo Bin ID empieza con BN-.
(async () => {
if (window.__clearBins) { alert('Clear Bins ya está corriendo.'); return; }
window.__clearBins = true;
const sleep = ms => new Promise(r => setTimeout(r, ms));
const visible = el => el && el.offsetParent !== null;
const BIN_RE = /^BN-[A-Z0-9]+-[A-Z0-9]+/;
let stop = false;
// Panel de progreso
const box = document.createElement('div');
box.style.cssText = 'position:fixed;bottom:16px;right:16px;z-index:999999;background:#111;color:#fff;font:13px/1.4 system-ui,sans-serif;padding:12px 14px;border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.3);min-width:240px';
box.innerHTML = '<b>Clear Bins</b><div id="cb-msg">Preparando…</div><button id="cb-stop" style="margin-top:8px;padding:4px 10px;border:0;border-radius:4px;background:#e5484d;color:#fff;cursor:pointer">Detener</button>';
document.body.appendChild(box);
const msg = t => { box.querySelector('#cb-msg').textContent = t; };
box.querySelector('#cb-stop').onclick = () => { stop = true; msg('Deteniendo…'); };
const done = t => { msg(t); window.__clearBins = false; const b = box.querySelector('#cb-stop'); b.textContent = 'Cerrar'; b.onclick = () => box.remove(); };
// Botones ⊗ junto a cada Bin ID de la tabla
const icons = () => {
const out = [];
document.querySelectorAll('td').forEach(td => {
if (!BIN_RE.test(td.textContent.trim()) || !visible(td)) return;
const el = td.querySelector('button,[role="button"],.anticon,svg,img,i');
if (el) { const t = el.closest('button,[role="button"],.anticon') || el; t.__bin = td.textContent.trim().match(BIN_RE)[0]; out.push(t); }
});
return out;
};
const click = el => ['mousedown', 'mouseup', 'click'].forEach(type =>
el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, view: window })));
const totalText = () => { const m = document.body.innerText.match(/(\d+)\s+results?\s+in\s+total/i); return m ? +m[1] : null; };
const errEls = () => [...document.querySelectorAll('.ant-message-error,.ant-notification-notice-error,.ant-alert-error')]
.filter(e => visible(e) && !/success/i.test(e.textContent));
let seenErr = new Set();
const errorShown = () => errEls().find(e => !seenErr.has(e));
const detachedToast = bin => [...document.querySelectorAll('.ant-notification-notice,.ant-message-notice,[role="alert"]')]
.some(e => visible(e) && e.textContent.includes(bin) && /detached|success/i.test(e.textContent));
// Busca el botón de confirmación del popup (si aparece)
const CONFIRM_RE = /^(yes|confirm|ok|remove|delete|unlink|s[ií]|aceptar|eliminar|quitar|borrar)$/i;
const findConfirm = () => {
const scopes = document.querySelectorAll('.ant-modal,.ant-popover,.ant-popconfirm,[role="dialog"],[role="alertdialog"]');
for (const s of scopes) {
if (!visible(s)) continue;
for (const b of s.querySelectorAll('button')) {
if (visible(b) && !b.disabled && CONFIRM_RE.test(b.textContent.trim())) return b;
}
}
return null;
};
const loc = (document.querySelector('h1,h2') || {}).textContent || 'esta locación';
let list = icons();
if (!list.length) { done('No encontré Bin IDs con ⊗ en esta página. Abre el Location Detail.'); return; }
const total = totalText() ?? list.length;
if (!confirm(`Clear Bins\n\nLocación: ${loc.trim()}\nBins asociados: ${total}\n\n¿Quitar TODOS los Bin IDs de esta locación?`)) { box.remove(); window.__clearBins = false; return; }
let removed = 0, guard = 0, firstChecked = false;
while (!stop && guard++ < 1000) {
list = icons();
if (!list.length) {
const next = document.querySelector('.ant-pagination-next:not(.ant-pagination-disabled)');
if (next && visible(next)) { click(next.querySelector('button') || next); await sleep(1500); if (icons().length) continue; }
break;
}
const before = list.length, beforeTotal = totalText(), bin = list[0].__bin;
seenErr = new Set(errEls());
msg(`Quitando ${removed + 1}${total ? ' de ' + total : ''}: ${bin}…`);
click(list[0]);
// Esperar popup de confirmación (hasta 2.5 s)
for (let i = 0; i < 10; i++) { await sleep(250); const c = findConfirm(); if (c) { click(c); break; } }
// Esperar a que la tabla se actualice (hasta 8 s)
let ok = false;
for (let i = 0; i < 32; i++) {
await sleep(250);
if (errorShown()) break;
if (detachedToast(bin)) { ok = true; break; }
const t = totalText();
if (icons().length < before || (t != null && beforeTotal != null && t < beforeTotal)) { ok = true; break; }
}
const err = errorShown();
if (err) { done(`Providence mostró un error: "${err.textContent.trim().slice(0, 120)}". Me detuve tras ${removed}.`); return; }
if (!ok) {
done(firstChecked
? `La tabla dejó de cambiar. Me detuve tras ${removed}. Refresca la página y vuelve a correrlo.`
: 'El primer intento no quitó nada. No sigo. (Puede que el popup de confirmación tenga otro texto; avísame.)');
return;
}
firstChecked = true;
removed++;
for (let i = 0; i < 20; i++) { if (!icons().some(e => e.__bin === bin) || icons().length < before) break; await sleep(250); }
await sleep(500);
}
done(stop ? `Detenido. Quitados: ${removed}.` : `Listo. Quitados: ${removed}. Refresca y prueba el putaway.`);
})();