Files
Plataforma-de-desarrolladores/SIG.Web/Scripts/DocumentosEtapaCargaMasiva.js
Jorge Luis caudillo esparza 0776ed8c59 Cambios en Gis lotes
2026-10-03 14:43:42 -07:00

1102 lines
31 KiB
JavaScript

(function (window, document) {
'use strict';
if (window.SIGDocumentosEtapaCargaMasiva) {
return;
}
var stopWords = {
'de': true,
'del': true,
'la': true,
'las': true,
'el': true,
'los': true,
'y': true,
'para': true,
'por': true,
'en': true,
'un': true,
'una': true,
'copia': true,
'archivo': true,
'documento': true,
'pdf': true
};
function texto(valor) {
return valor == null ? '' : String(valor);
}
function quitarExtension(nombre) {
var valor = texto(nombre);
var indice = valor.lastIndexOf('.');
return indice > 0 ? valor.substring(0, indice) : valor;
}
function sinAcentos(valor) {
var resultado = texto(valor);
if (resultado.normalize) {
return resultado
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '');
}
return resultado
.replace(/[áàäâ]/gi, 'a')
.replace(/[éèëê]/gi, 'e')
.replace(/[íìïî]/gi, 'i')
.replace(/[óòöô]/gi, 'o')
.replace(/[úùüû]/gi, 'u')
.replace(/ñ/gi, 'n');
}
function normalizar(valor) {
return sinAcentos(quitarExtension(valor))
.toLowerCase()
.replace(/[_\-\.(),/\\]+/g, ' ')
.replace(/[^a-z0-9\s]/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
function tokens(valor) {
return normalizar(valor)
.split(' ')
.filter(function (item) {
return item && !stopWords[item] && !/^\d+$/.test(item);
});
}
function puntaje(nombreArchivo, tipoDocumento) {
var archivo = normalizar(nombreArchivo);
var tipo = normalizar(tipoDocumento);
if (!archivo || !tipo) {
return 0;
}
if (archivo === tipo) {
return 1;
}
if (archivo.indexOf(tipo) >= 0 || tipo.indexOf(archivo) >= 0) {
return 0.95;
}
var archivoTokens = tokens(nombreArchivo);
var tipoTokens = tokens(tipoDocumento);
if (!archivoTokens.length || !tipoTokens.length) {
return 0;
}
var tipoMapa = {};
var interseccion = 0;
var indice;
for (indice = 0; indice < tipoTokens.length; indice++) {
tipoMapa[tipoTokens[indice]] = true;
}
for (indice = 0; indice < archivoTokens.length; indice++) {
if (tipoMapa[archivoTokens[indice]]) {
interseccion++;
}
}
var coberturaArchivo = interseccion / archivoTokens.length;
var coberturaTipo = interseccion / tipoTokens.length;
return (coberturaArchivo * 0.65) + (coberturaTipo * 0.35);
}
function buscarCoincidencia(nombreArchivo, catalogo) {
var candidatos = [];
var indice;
for (indice = 0; indice < catalogo.length; indice++) {
candidatos.push({
indice: indice,
puntaje: puntaje(nombreArchivo, catalogo[indice].TipoDocumento)
});
}
candidatos.sort(function (a, b) {
return b.puntaje - a.puntaje;
});
if (!candidatos.length || candidatos[0].puntaje < 0.58) {
return -1;
}
if (candidatos.length > 1 &&
Math.abs(candidatos[0].puntaje - candidatos[1].puntaje) < 0.05) {
return -1;
}
return candidatos[0].indice;
}
function leerCatalogo(root) {
var campo = root.querySelector('input[id$="CatalogoCargaMasiva"]');
if (!campo || !campo.value) {
return [];
}
try {
return JSON.parse(campo.value) || [];
}
catch (error) {
return [];
}
}
function fechaHoy(root) {
var campo = root.querySelector('input[id$="FechaHoyCargaMasiva"]');
return campo ? campo.value : '';
}
/*
* La carga masiva utiliza el control genérico ADSdropzoneLocal.
* Ese control parte de un input hidden y genera un contenedor
* {ClientID}-drop con el input type=file real.
*/
function obtenerControlCarga(root) {
return root.querySelector(
'.ADSdropzoneLocal[data-sig-carga-masiva-archivo]'
);
}
function obtenerContenedorDropzone(root) {
var control = obtenerControlCarga(root);
if (!control || !control.id) {
return null;
}
return document.getElementById(control.id + '-drop');
}
function claveArchivo(archivo) {
if (!archivo) {
return '';
}
return [
texto(archivo.name),
texto(archivo.size),
texto(archivo.lastModified)
].join('|');
}
/*
* ADSdropzoneLocal puede conservar más de un input type=file
* internamente. No debemos tomar únicamente el primero porque,
* después de seleccionar archivos, el control puede crear otro
* input vacío para permitir una nueva selección.
*/
function obtenerArchivosInputs(root) {
var contenedor = obtenerContenedorDropzone(root);
var archivos = [];
var vistos = {};
if (!contenedor) {
return archivos;
}
var inputs = contenedor.querySelectorAll('input[type="file"]');
var indiceInput;
var indiceArchivo;
for (
indiceInput = 0;
indiceInput < inputs.length;
indiceInput++
) {
var input = inputs[indiceInput];
if (!input.files) {
continue;
}
for (
indiceArchivo = 0;
indiceArchivo < input.files.length;
indiceArchivo++
) {
var archivo = input.files[indiceArchivo];
var clave = claveArchivo(archivo);
if (!vistos[clave]) {
vistos[clave] = true;
archivos.push(archivo);
}
}
}
return archivos;
}
/*
* Cuando el control genérico está respaldado por Dropzone,
* la fuente más confiable después de que éste reemplaza su
* input interno es la colección dropzone.files.
*/
function obtenerArchivosDropzone(root) {
var contenedor = obtenerContenedorDropzone(root);
var instancia = null;
var archivos = [];
if (!contenedor) {
return archivos;
}
if (contenedor.dropzone) {
instancia = contenedor.dropzone;
}
else if (
window.Dropzone &&
typeof window.Dropzone.forElement === 'function'
) {
try {
instancia = window.Dropzone.forElement(contenedor);
}
catch (error) {
instancia = null;
}
}
if (!instancia || !instancia.files) {
return archivos;
}
for (var indice = 0; indice < instancia.files.length; indice++) {
var archivo = instancia.files[indice];
if (!archivo || !archivo.name) {
continue;
}
/*
* removeFile() elimina el archivo de la colección. Este filtro
* solamente evita tomar archivos cancelados si la versión de
* Dropzone conserva alguno temporalmente.
*/
if (
window.Dropzone &&
window.Dropzone.CANCELED &&
archivo.status === window.Dropzone.CANCELED
) {
continue;
}
archivos.push(archivo);
}
return archivos;
}
function obtenerArchivos(root) {
/*
* Primero usamos los inputs reales porque mantienen el mismo orden
* que Request.Files durante el postback. Si el control genérico ya
* sustituyó esos inputs y quedaron vacíos, usamos dropzone.files.
*/
var archivos = obtenerArchivosInputs(root);
if (archivos.length > 0) {
return archivos;
}
return obtenerArchivosDropzone(root);
}
function crearOpcion(textoOpcion, valor) {
var opcion = document.createElement('option');
opcion.value = valor;
opcion.textContent = textoOpcion;
return opcion;
}
function actualizarVencimiento(fila, catalogo) {
var select = fila.querySelector('[data-sig-carga-tipo]');
var contenedor = fila.querySelector('[data-sig-carga-vencimiento-contenedor]');
var input = fila.querySelector('[data-sig-carga-vencimiento]');
var noAplica = fila.querySelector('[data-sig-carga-vencimiento-no-aplica]');
var indice = select && select.value !== '' ? parseInt(select.value, 10) : -1;
var requiere = indice >= 0 && catalogo[indice] &&
catalogo[indice].TieneVigencia === true;
if (requiere) {
contenedor.style.display = '';
noAplica.style.display = 'none';
input.disabled = false;
input.required = true;
}
else {
input.value = '';
input.disabled = true;
input.required = false;
contenedor.style.display = 'none';
noAplica.style.display = '';
}
}
function actualizarFilaIncluida(fila, catalogo) {
var incluir = fila.querySelector(
'[data-sig-carga-incluir]'
);
var incluida = !incluir || incluir.checked;
var select = fila.querySelector(
'[data-sig-carga-tipo]'
);
var fecha = fila.querySelector(
'[data-sig-carga-fecha]'
);
var vencimiento = fila.querySelector(
'[data-sig-carga-vencimiento]'
);
if (incluida) {
fila.classList.remove(
'sig-carga-masiva-fila-omitida'
);
if (select) {
select.disabled = false;
}
if (fecha) {
fecha.disabled = false;
}
actualizarVencimiento(fila, catalogo);
return;
}
fila.classList.add(
'sig-carga-masiva-fila-omitida'
);
if (select) {
select.disabled = true;
}
if (fecha) {
fecha.disabled = true;
fecha.required = false;
}
if (vencimiento) {
vencimiento.disabled = true;
vencimiento.required = false;
}
}
function crearFila(root, archivo, indiceArchivo, catalogo) {
var fila = document.createElement('tr');
fila.setAttribute('data-sig-carga-fila', '1');
fila.setAttribute('data-index', String(indiceArchivo));
var celdaIncluir = document.createElement('td');
celdaIncluir.className = 'sig-carga-masiva-col-incluir';
var incluir = document.createElement('input');
incluir.type = 'checkbox';
incluir.checked = true;
incluir.setAttribute('data-sig-carga-incluir', '1');
incluir.setAttribute(
'aria-label',
'Incluir ' + archivo.name
);
incluir.title = 'Incluir archivo en la carga';
celdaIncluir.appendChild(incluir);
fila.appendChild(celdaIncluir);
var celdaArchivo = document.createElement('td');
var nombre = document.createElement('strong');
nombre.className = 'sig-carga-masiva-nombre';
nombre.textContent = archivo.name;
celdaArchivo.appendChild(nombre);
var estado = document.createElement('span');
estado.className = 'sig-carga-masiva-coincidencia';
estado.setAttribute('data-sig-carga-coincidencia', '1');
celdaArchivo.appendChild(estado);
fila.appendChild(celdaArchivo);
var celdaTipo = document.createElement('td');
var select = document.createElement('select');
select.className = 'form-control input-sm';
select.setAttribute('data-sig-carga-tipo', '1');
select.appendChild(crearOpcion('Seleccione tipo de documento', ''));
var indice;
for (indice = 0; indice < catalogo.length; indice++) {
select.appendChild(
crearOpcion(catalogo[indice].Etiqueta, String(indice))
);
}
var coincidencia = buscarCoincidencia(archivo.name, catalogo);
if (coincidencia >= 0) {
select.value = String(coincidencia);
estado.textContent = 'Coincidencia automática';
estado.className += ' sig-carga-masiva-coincidencia-ok';
}
else {
estado.textContent = 'Revise el tipo de documento';
}
select.addEventListener('change', function () {
estado.textContent = select.value === ''
? 'Revise el tipo de documento'
: 'Tipo seleccionado';
actualizarVencimiento(fila, catalogo);
});
celdaTipo.appendChild(select);
fila.appendChild(celdaTipo);
var celdaFecha = document.createElement('td');
var fecha = document.createElement('input');
fecha.type = 'date';
fecha.className = 'form-control input-sm';
fecha.setAttribute('data-sig-carga-fecha', '1');
fecha.required = true;
fecha.value = fechaHoy(root);
fecha.max = fechaHoy(root);
celdaFecha.appendChild(fecha);
fila.appendChild(celdaFecha);
var celdaVencimiento = document.createElement('td');
var vencimientoContenedor = document.createElement('div');
vencimientoContenedor.setAttribute(
'data-sig-carga-vencimiento-contenedor',
'1'
);
var vencimiento = document.createElement('input');
vencimiento.type = 'date';
vencimiento.className = 'form-control input-sm';
vencimiento.setAttribute('data-sig-carga-vencimiento', '1');
vencimientoContenedor.appendChild(vencimiento);
celdaVencimiento.appendChild(vencimientoContenedor);
var noAplica = document.createElement('span');
noAplica.className = 'text-muted';
noAplica.setAttribute(
'data-sig-carga-vencimiento-no-aplica',
'1'
);
noAplica.textContent = 'No aplica';
celdaVencimiento.appendChild(noAplica);
fila.appendChild(celdaVencimiento);
incluir.addEventListener('change', function () {
actualizarFilaIncluida(fila, catalogo);
});
actualizarVencimiento(fila, catalogo);
actualizarFilaIncluida(fila, catalogo);
return fila;
}
function mostrarError(root, mensaje) {
var contenedor = root.querySelector(
'[data-sig-carga-masiva-error]'
);
if (!contenedor) {
return;
}
if (!mensaje) {
contenedor.style.display = 'none';
contenedor.textContent = '';
return;
}
contenedor.textContent = mensaje;
contenedor.style.display = '';
}
function mostrarEstadoListado(root, mensaje) {
var cuerpo = root.querySelector(
'[data-sig-carga-masiva-filas]'
);
if (!cuerpo) {
return;
}
cuerpo.innerHTML = '';
var vacio = document.createElement('tr');
vacio.className = 'sig-carga-masiva-vacio';
var celda = document.createElement('td');
celda.colSpan = 5;
celda.textContent =
mensaje || 'Seleccione archivos para continuar.';
vacio.appendChild(celda);
cuerpo.appendChild(vacio);
}
function actualizarResumenSeleccion(root) {
var archivos = obtenerArchivos(root);
var resumen = root.querySelector(
'[data-sig-carga-masiva-resumen]'
);
var enlistar = root.querySelector(
'[data-sig-carga-masiva-enlistar]'
);
var cantidad = archivos.length || 0;
if (enlistar) {
enlistar.disabled = cantidad === 0;
}
if (!resumen) {
return;
}
if (cantidad === 0) {
resumen.textContent =
'Seleccione o arrastre archivos para continuar.';
return;
}
resumen.textContent =
cantidad === 1
? '1 archivo seleccionado. Presione "Enlistar archivos".'
: cantidad +
' archivos seleccionados. Presione "Enlistar archivos".';
}
function seleccionCambiada(root) {
var archivos = obtenerArchivos(root);
var cantidad = archivos.length || 0;
mostrarError(root, '');
actualizarResumenSeleccion(root);
if (cantidad === 0) {
mostrarEstadoListado(
root,
'Seleccione archivos para continuar.'
);
return;
}
mostrarEstadoListado(
root,
cantidad === 1
? '1 archivo seleccionado. Presione "Enlistar archivos" para prepararlo.'
: cantidad +
' archivos seleccionados. Presione "Enlistar archivos" para prepararlos.'
);
}
function renderizar(root) {
var archivos = obtenerArchivos(root);
var cuerpo = root.querySelector(
'[data-sig-carga-masiva-filas]'
);
var catalogo = leerCatalogo(root);
if (!cuerpo) {
return;
}
cuerpo.innerHTML = '';
mostrarError(root, '');
if (!archivos || archivos.length === 0) {
mostrarEstadoListado(
root,
'Seleccione archivos para continuar.'
);
actualizarResumenSeleccion(root);
return;
}
var indice;
for (indice = 0; indice < archivos.length; indice++) {
cuerpo.appendChild(
crearFila(
root,
archivos[indice],
indice,
catalogo
)
);
}
actualizarResumenSeleccion(root);
}
function serializar(root) {
var archivos = obtenerArchivos(root);
var hidden = root.querySelector(
'input[id$="MetadataCargaMasiva"]'
);
var catalogo = leerCatalogo(root);
var filas = root.querySelectorAll(
'[data-sig-carga-fila]'
);
var datos = [];
var errores = [];
var destinos = {};
var incluidos = 0;
var indice;
if (!archivos || archivos.length === 0) {
errores.push('Seleccione al menos un archivo.');
}
else if (filas.length !== archivos.length) {
errores.push(
'Presione "Enlistar archivos" antes de guardar.'
);
}
for (indice = 0; indice < filas.length; indice++) {
var fila = filas[indice];
var indexArchivo = parseInt(
fila.getAttribute('data-index'),
10
);
var archivo =
!isNaN(indexArchivo) &&
indexArchivo >= 0 &&
indexArchivo < archivos.length
? archivos[indexArchivo]
: null;
if (!archivo) {
errores.push(
'La selección de archivos cambió. Presione "Enlistar archivos" nuevamente.'
);
continue;
}
var incluir = fila.querySelector(
'[data-sig-carga-incluir]'
);
var estaIncluido = !incluir || incluir.checked;
if (!estaIncluido) {
datos.push({
Index: indexArchivo,
Incluir: false
});
continue;
}
incluidos++;
var select = fila.querySelector(
'[data-sig-carga-tipo]'
);
var fecha = fila.querySelector(
'[data-sig-carga-fecha]'
);
var vencimiento = fila.querySelector(
'[data-sig-carga-vencimiento]'
);
if (!select || select.value === '') {
errores.push(
'Seleccione el tipo de documento para ' +
archivo.name +
'.'
);
continue;
}
var indiceCatalogo = parseInt(
select.value,
10
);
var opcion = catalogo[indiceCatalogo];
if (!opcion) {
errores.push(
'El tipo de documento seleccionado ya no está disponible.'
);
continue;
}
if (!fecha || !fecha.value) {
errores.push(
'Capture la fecha del documento para ' +
archivo.name +
'.'
);
continue;
}
if (
opcion.TieneVigencia === true &&
(!vencimiento || !vencimiento.value)
) {
errores.push(
'Capture el vencimiento para ' +
archivo.name +
'.'
);
continue;
}
if (
vencimiento &&
vencimiento.value &&
vencimiento.value < fecha.value
) {
errores.push(
'El vencimiento no puede ser anterior a la fecha del documento en ' +
archivo.name +
'.'
);
continue;
}
var destino =
opcion.CveFraccionamientoEtapa +
'|' +
opcion.CveTipoDocumento;
if (destinos[destino]) {
errores.push(
'Hay más de un archivo asignado al mismo documento: ' +
opcion.TipoDocumento +
'.'
);
continue;
}
destinos[destino] = true;
datos.push({
Index: indexArchivo,
Incluir: true,
CveFraccionamientoEtapa:
opcion.CveFraccionamientoEtapa,
CveTipoDocumento:
opcion.CveTipoDocumento,
FechaDocumento:
fecha.value,
FechaVencimiento:
opcion.TieneVigencia === true &&
vencimiento
? vencimiento.value
: ''
});
}
if (incluidos === 0 && errores.length === 0) {
errores.push(
'Marque al menos un archivo para guardar.'
);
}
if (errores.length) {
mostrarError(root, errores.join('\n'));
return false;
}
if (hidden) {
hidden.value = JSON.stringify(datos);
}
mostrarError(root, '');
return true;
}
function ancestroConAtributo(elemento, atributo, limite) {
var actual = elemento;
while (actual && actual !== limite) {
if (
actual.getAttribute &&
actual.getAttribute(atributo) !== null
) {
return actual;
}
actual = actual.parentNode;
}
if (
limite &&
limite.getAttribute &&
limite.getAttribute(atributo) !== null
) {
return limite;
}
return null;
}
function ancestroConClase(elemento, clase, limite) {
var actual = elemento;
while (actual && actual !== limite) {
if (
actual.classList &&
actual.classList.contains(clase)
) {
return actual;
}
actual = actual.parentNode;
}
return null;
}
function esInputDelDropzone(root, elemento) {
if (
!elemento ||
elemento.tagName !== 'INPUT' ||
texto(elemento.type).toLowerCase() !== 'file'
) {
return false;
}
var contenedor = ancestroConClase(
elemento,
'ADSdropzoneLocalContainer',
root
);
return !!contenedor;
}
function sincronizarDespuesDeQuitar(root) {
window.setTimeout(function () {
seleccionCambiada(root);
}, 0);
}
/*
* El ADSdropzoneLocal agrega/quita previews de forma dinámica y puede
* reemplazar su input type=file. Observamos únicamente el contenedor
* del dropzone para mantener el botón "Enlistar archivos" sincronizado
* aunque el change del input interno no llegue hasta este script.
*/
function observarDropzone(root) {
var contenedor = obtenerContenedorDropzone(root);
if (
!contenedor ||
!window.MutationObserver ||
contenedor.getAttribute(
'data-sig-carga-masiva-observado'
) === '1'
) {
return;
}
contenedor.setAttribute(
'data-sig-carga-masiva-observado',
'1'
);
var pendiente = false;
var observador = new MutationObserver(function () {
if (pendiente) {
return;
}
pendiente = true;
window.setTimeout(function () {
pendiente = false;
seleccionCambiada(root);
}, 0);
});
observador.observe(contenedor, {
childList: true,
subtree: true
});
}
function inicializar(root) {
if (!root) {
return;
}
/*
* El modal contiene un UpdatePanel. Los eventos se delegan
* desde el contenedor exterior para que sigan funcionando
* aunque ASP.NET reemplace el control ADSdropzoneLocal,
* el botón o la tabla.
*/
if (
root.getAttribute(
'data-sig-carga-masiva-inicializado'
) !== '1'
) {
root.setAttribute(
'data-sig-carga-masiva-inicializado',
'1'
);
root.addEventListener(
'change',
function (evento) {
var objetivo =
evento.target ||
evento.srcElement;
if (
esInputDelDropzone(
root,
objetivo
)
) {
seleccionCambiada(root);
}
}
);
root.addEventListener(
'click',
function (evento) {
var objetivo =
evento.target ||
evento.srcElement;
var enlistar =
ancestroConAtributo(
objetivo,
'data-sig-carga-masiva-enlistar',
root
);
if (enlistar) {
evento.preventDefault();
renderizar(root);
return;
}
/*
* El propio ADSdropzoneLocal muestra "Quitar archivo".
* Cuando se quite uno, invalidamos el grid para que
* el usuario vuelva a enlistar la selección actual.
*/
var quitarPreview =
ancestroConClase(
objetivo,
'dz-remove',
root
);
if (quitarPreview) {
sincronizarDespuesDeQuitar(root);
return;
}
var guardar =
ancestroConAtributo(
objetivo,
'data-sig-carga-masiva-guardar',
root
);
if (guardar) {
if (!serializar(root)) {
evento.preventDefault();
evento.stopPropagation();
return false;
}
/*
* No deshabilitamos el submit.
*
* Si existe el loader global de custom.js,
* lo mostramos después de validar/serializar
* y dejamos continuar el click/postback normal.
*/
if (
typeof window.loaderinit === 'function'
) {
window.loaderinit();
}
return true;
}
return true;
}
);
}
/*
* El control genérico puede terminar de crear su input real
* durante el load de Microsoft Ajax. Esperamos un ciclo para
* sincronizar el resumen sin interferir con su inicialización.
*/
window.setTimeout(function () {
observarDropzone(root);
actualizarResumenSeleccion(root);
var archivos = obtenerArchivos(root);
if (!archivos || archivos.length === 0) {
mostrarEstadoListado(
root,
'Seleccione archivos para continuar.'
);
}
}, 0);
}
function inicializarTodos() {
var controles = document.querySelectorAll(
'[data-sig-carga-masiva]'
);
var indice;
for (
indice = 0;
indice < controles.length;
indice++
) {
inicializar(controles[indice]);
}
}
if (window.Sys && window.Sys.Application) {
window.Sys.Application.add_load(
inicializarTodos
);
}
else if (document.readyState === 'loading') {
document.addEventListener(
'DOMContentLoaded',
inicializarTodos
);
}
else {
inicializarTodos();
}
window.SIGDocumentosEtapaCargaMasiva = {
inicializar: inicializarTodos,
enlistar: renderizar
};
}(window, document));