🔒 Panel de Administración

📄 Carga de documentos para análisis

Adjunte los 3 documentos obligatorios. Legal Check y TYBA son opcionales pero mejoran el análisis.

① Datos del cliente
② Ingresos de libranza
Suma total del desprendible: ley + libranzas + otros
③ Cuotas de créditos actuales (opcional — para análisis con compra de cartera)
Ingrese las cuotas de créditos que el cliente tiene con otras entidades y que se incluirán en el análisis como compra de cartera. Se calculará la capacidad con y sin estos valores.
④ Datos de la agencia
⑤ Línea de crédito
Cargue los 3 documentos obligatorios para continuar
Obligatorios
Opcionales

📈 Informes de gestión comercial

Colocación por zona, agencia y línea — Productividad de analistas — Tendencias mensuales

Filtrar por:
🌎 Colocación por zona regional
Zona Regional Aprobados Con cond. Negados Monto aprobado Total
📅 Tendencia mensual — Monto aprobado
🏆 Ranking de agencias — Colocación
📄 Colocación por línea de crédito
👨‍💼 Informe de gestión — Productividad por analista
Analista Usuario Total Aprobados Cond. Negados Monto total Prom/día % Aprob. Rendimiento

📋 Repositorio de solicitudes

Historial completo de análisis procesados — estadísticas por analista y agencia

# Fecha Cédula Agencia Regional Línea Monto Capacidad Margen CIFIN Legal Analista Decisión PDF
📈 Informe de productividad por analista
Analista Usuario Total Aprobados Con condiciones Negados % Aprobación Último proceso

⚙ Panel de administración

Gestión de regionales, agencias, departamentos/ciudades y usuarios del sistema

🗺 Regionales
#RegionalAcciones
🏢 Agencias
NombreNITRegionalDepartamentoCiudad
Agregar nueva agencia
📍 Departamentos y ciudades de Colombia
🔗 Configuración Google Drive & Sheets
💡 Cómo configurar:
1. Crea un proyecto en script.google.com y pega el código Apps Script
2. Despliega como Aplicación web y copia la URL del webhook
3. Crea una carpeta en Google Drive y copia su ID desde la URL
4. Crea un Google Sheets y copia su ID desde la URL
👤 Usuarios del sistema
#NombreUsuarioRolAcciones
Agregar usuario
'); } // ── DOCUMENTOS ─────────────────────────────────────────────── var archivos = {cedula:null,cifin:null,desprendible:null,legalcheck:null,tyba:null}; function cargarDoc(tipo, input){ if(!input.files||!input.files[0]) return; var f = input.files[0]; archivos[tipo] = f; document.getElementById('box-'+tipo).classList.add('cargado'); document.getElementById('nom-'+tipo).textContent = f.name; actualizarEstadoDocs(); } function actualizarEstadoDocs(){ var ok = archivos.cedula && archivos.cifin && archivos.desprendible; var n = Object.values(archivos).filter(Boolean).length; var barra = document.getElementById('edo-docs'); var txt = document.getElementById('edo-txt'); var btn = document.getElementById('btn-analizar'); if(ok){ barra.className = 'estado-docs ok'; txt.textContent = '✓ '+n+'/5 documentos cargados — listo para analizar'; btn.disabled = false; btn.className = 'btn-analizar on'; } else { var f=[]; if(!archivos.cedula) f.push('Cédula'); if(!archivos.cifin) f.push('CIFIN'); if(!archivos.desprendible) f.push('Desprendible'); barra.className = 'estado-docs warn'; txt.textContent = 'Faltan: '+f.join(', '); btn.disabled = true; btn.className = 'btn-analizar'; } } // ── CUOTAS DE CRÉDITOS ─────────────────────────────────────── var cuotas = []; // [{entidad, valor}] function agregarCuota(){ var ent = document.getElementById('cq-entidad').value.trim(); var val = parseMonto('cq-valor'); if(!ent || !val){ alert('Ingrese entidad y valor de la cuota'); return; } cuotas.push({entidad: ent, valor: val}); document.getElementById('cq-entidad').value = ''; document.getElementById('cq-valor').value = ''; renderCuotas(); } function eliminarCuota(i){ cuotas.splice(i,1); renderCuotas(); } function renderCuotas(){ var total = cuotas.reduce(function(s,c){ return s+c.valor; }, 0); var lista = document.getElementById('lista-cuotas'); if(!cuotas.length){ lista.innerHTML = '
Sin cuotas agregadas
'; document.getElementById('total-cuotas-bar').style.display = 'none'; return; } lista.innerHTML = cuotas.map(function(c,i){ return '
' + '
' + c.entidad + '
' + '
' + fmtPeso(c.valor) + '
' + '' + '
'; }).join(''); var bar = document.getElementById('total-cuotas-bar'); bar.style.display = 'flex'; document.getElementById('total-cuotas-val').textContent = fmtPeso(total); } function calcularEscenarios(capacidad, descActuales){ if(!cuotas.length){ document.getElementById('bloque-escenarios').style.display = 'none'; return; } var totalCuotas = cuotas.reduce(function(s,c){ return s+c.valor; }, 0); var margenConCartera = capacidad - descActuales + totalCuotas; var html = ''; // Escenario 1: SIN compra de cartera var m1 = capacidad - descActuales; html += crearEscenario( 'Sin compra de cartera', 'Capacidad disponible sin incluir los créditos a unificar', m1, '#1565C0','#E3F2FD', null ); // Escenario por cada cuota individual cuotas.forEach(function(c){ var m = capacidad - descActuales + c.valor; html += crearEscenario( 'Con compra de cartera: ' + c.entidad, 'Incluye la cuota de ' + fmtPeso(c.valor) + ' que queda liberada', m, '#6A1B9A','#F3E5F5', c ); }); // Escenario final: con TODAS las cuotas if(cuotas.length > 1){ html += crearEscenario( 'Con compra de cartera total (' + cuotas.length + ' créditos)', 'Incluye todas las cuotas: ' + cuotas.map(function(c){ return c.entidad; }).join(', '), margenConCartera, '#00695C','#E0F2F1', null ); } document.getElementById('lista-escenarios').innerHTML = html; document.getElementById('bloque-escenarios').style.display = 'block'; } function crearEscenario(titulo, sub, margen, color, bg, cuota){ var icono = margen > 0 ? '✓' : '⚠'; var colorM = margen > 0 ? '#2E7D32' : '#CC0000'; return '
' + '
' + '
' + icono + ' ' + titulo + '
' + '
' + sub + '
' + '
' + '
' + '
' + fmtPeso(margen) + '
' + '
margen disponible
' + '
' + '
'; } function parseMonto(id){ var v = document.getElementById(id)?.value||''; return parseInt(v.replace(/[^0-9]/g,''))||0; } function fmtPeso(n){ return '$' + Math.round(n).toLocaleString('es-CO'); } function iniciarAnalisis(){ // Validar cédulas cruzadas primero if(!validarCedulasCruzadas()){ return; // El modal ya se abrió dentro de validarCedulasCruzadas } if(!verificarFormSolicitud()){ alert('Complete todos los campos obligatorios antes de iniciar el análisis.'); return; } // ── Leer valores ── var salario = parseMonto('lib-salario'); var descLey = parseMonto('lib-descuentos'); var descTotal = parseMonto('lib-total'); var monto = parseMonto('monto-sol'); // ── FÓRMULA: ((Salario - Desc.Ley) / 2) - 10000 ── var baseNeta = salario - descLey; var mitad = baseNeta / 2; var capacidad = mitad - 10000; var margen = capacidad - descTotal; // ── Mostrar desglose ── document.getElementById('res-salario').textContent = fmtPeso(salario); document.getElementById('res-descley').textContent = fmtPeso(descLey); document.getElementById('res-base').textContent = fmtPeso(baseNeta); document.getElementById('res-mitad').textContent = fmtPeso(mitad); document.getElementById('res-capacidad').textContent = fmtPeso(capacidad); document.getElementById('res-desc-total').textContent= fmtPeso(descTotal); document.getElementById('res-margen').textContent = fmtPeso(margen); // ── Color del margen ── var bm = document.getElementById('bloque-margen'); var ml = document.getElementById('margen-label'); var mr = document.getElementById('res-margen'); if(margen > 0){ bm.style.background = '#E8F5E9'; ml.style.color = '#2E7D32'; mr.style.color = '#2E7D32'; } else { bm.style.background = '#FFEBEE'; ml.style.color = '#CC0000'; mr.style.color = '#CC0000'; } // ── Viabilidad del monto solicitado ── var bv = document.getElementById('bloque-viabilidad'); bv.style.display = 'block'; if(margen <= 0){ bv.style.background = '#FFEBEE'; bv.style.color = '#CC0000'; bv.innerHTML = '⛔ El cliente NO tiene margen disponible. Los descuentos actuales superan la capacidad de pago.'; } else if(monto > 0){ // Cuota estimada a 48 meses (sin interés — solo referencia) var cuotaRef = Math.round(monto / 48); if(cuotaRef <= margen){ bv.style.background = '#E8F5E9'; bv.style.color = '#2E7D32'; bv.innerHTML = '✓ Monto solicitado ' + fmtPeso(monto) + ' viable. ' + 'Cuota estimada referencial a 48 meses: ' + fmtPeso(cuotaRef) + ' ' + '(margen disponible: ' + fmtPeso(margen) + ')'; } else { bv.style.background = '#FFF3E0'; bv.style.color = '#E65100'; bv.innerHTML = '⚠ Cuota estimada referencial (' + fmtPeso(cuotaRef) + ') ' + 'supera el margen disponible (' + fmtPeso(margen) + '). ' + 'Revisar monto o plazo.'; } } // ── Libre inversión ── var deducidos = parseMonto('lib-deducidos') || (descTotal + descLey); var descFin = deducidos - descLey; var capLibre = capacidad - descFin; document.getElementById('li-cap-libranza').textContent = fmtPeso(capacidad); document.getElementById('li-desc-fin').textContent = fmtPeso(descFin); var liEl = document.getElementById('li-capacidad'); liEl.textContent = fmtPeso(capLibre); liEl.style.color = capLibre > 0 ? '#1565C0' : '#CC0000'; document.getElementById('li-viabilidad').textContent = capLibre > 0 ? '\u2713 VIABLE' : '\u2717 NO VIABLE'; document.getElementById('li-viabilidad').style.color = capLibre > 0 ? '#2E7D32' : '#CC0000'; document.getElementById('li-viabilidad-desc').textContent = capLibre > 0 ? 'Cuota m\u00e1x: ' + fmtPeso(capLibre) : 'Sin margen para libre inversi\u00f3n'; // ── Mostrar bloque ── document.getElementById('bloque-analisis').style.display = 'block'; document.getElementById('bloque-analisis').scrollIntoView({behavior:'smooth'}); calcularEscenarios(capacidad, descTotal); renderCapPago(salario, descLey, descTotal, monto); renderAlertasIniciales(); renderCifin(); renderLegalInicial(); abrirInforme(); } function llenarAgenciasSelect(){ var sel = document.getElementById('ag-sel'); if(!sel) return; var val = sel.value; sel.innerHTML = '' + datos.agencias.slice().sort((a,b)=>a.nombre.localeCompare(b.nombre)) .map(a=>``).join(''); if(val) sel.value = val; } function seleccionarAgencia(){ var sel = document.getElementById('ag-sel'); var opt = sel.options[sel.selectedIndex]; var regional = opt?.dataset?.regional || ''; document.getElementById('ag-regional-info').value = regional; } function fmtMonto(el){ var v = el.value.replace(/[^0-9]/g,''); el.value = v ? parseInt(v).toLocaleString('es-CO') : ''; } // ── VALIDACIÓN CRUZADA DE CÉDULAS ──────────────────────────── // Fuentes de cédula en el sistema // 1. Formulario: cli-cedula (ingresada por analista) // 2. Cédula ingresada en CIFIN: cf-cedula (campo val-cifin-cedula) // 3. Cédula en informe Legal Check: lc-doc (campo val-lc-cedula) // Todas deben coincidir var cedulasValidas = false; function validarCedulasCruzadas(){ var cedForm = (document.getElementById('cli-cedula')?.value||'').replace(/[^0-9]/g,'').trim(); var cedCifin = (document.getElementById('val-cifin-cedula')?.value||'').replace(/[^0-9]/g,'').trim(); var cedLegal = (document.getElementById('val-lc-cedula')?.value||'').replace(/[^0-9]/g,'').trim(); var fuentes = []; if(cedForm) fuentes.push({nombre:'Formulario', valor:cedForm}); if(cedCifin) fuentes.push({nombre:'CIFIN', valor:cedCifin}); if(cedLegal) fuentes.push({nombre:'Legal Check', valor:cedLegal}); // Si solo hay 1 fuente no hay inconsistencia if(fuentes.length <= 1){ ocultarAlertaCedulas(); cedulasValidas = true; return true; } // Verificar que todas sean iguales var ref = fuentes[0].valor; var distintas = fuentes.filter(function(f){ return f.valor !== ref; }); if(distintas.length === 0){ ocultarAlertaCedulas(); cedulasValidas = true; return true; } // Hay inconsistencia — mostrar alerta var detalle = 'Se encontraron números de cédula diferentes entre los documentos:

' + fuentes.map(function(f){ var ok = f.valor === ref; return '' +(ok?'✓':'✗')+' '+f.nombre+': ' +''+f.valor+''; }).join('
'); // Mostrar modal document.getElementById('modal-cedulas-detalle').innerHTML = detalle; document.getElementById('modal-cedulas').style.display = 'flex'; // Bloquear botón var btn = document.getElementById('btn-analizar'); if(btn){ btn.disabled = true; btn.className = 'btn-analizar'; btn.title = 'Los números de cédula no coinciden'; } cedulasValidas = false; return false; } function ocultarAlertaCedulas(){ cedulasValidas = true; } function cerrarModalCedulas(){ document.getElementById('modal-cedulas').style.display = 'none'; } function verificarFormSolicitud(){ var cedula = document.getElementById('cli-cedula')?.value.trim(); var salario = document.getElementById('lib-salario')?.value.trim(); var descLey = document.getElementById('lib-descuentos')?.value.trim(); var descTot = document.getElementById('lib-total')?.value.trim(); var email = document.getElementById('cli-email')?.value.trim(); var tel = document.getElementById('cli-tel')?.value.trim(); var agencia = document.getElementById('ag-sel')?.value; var gesN = document.getElementById('ges-nombre')?.value.trim(); var gesE = document.getElementById('ges-email')?.value.trim(); var gesC = document.getElementById('ges-cel')?.value.trim(); var linea = document.getElementById('linea-credito')?.value; var monto = document.getElementById('monto-sol')?.value.trim(); return cedula && email && tel && salario && descLey && descTot && agencia && gesN && gesE && gesC && linea && monto; } // ── CAPACIDAD DE PAGO — INFORME ───────────────────────────── function renderCapPago(salario, descLey, descTotal, monto){ var baseNeta = salario - descLey; var mitad = baseNeta / 2; var capacidad = mitad - 10000; var margen = capacidad - descTotal; var limite50 = salario * 0.5; var descFin = descTotal; var cobAd = 10000; var totalNuevo= descFin + cobAd; var margenLey = limite50 - totalNuevo; var neto = salario - descLey - descTotal; var cedula = document.getElementById('cli-cedula')?.value || ''; // Nombre document.getElementById('cp-nombre').textContent = 'CLIENTE CC ' + cedula; document.getElementById('cp-cc').textContent = 'C.C. ' + cedula; document.getElementById('cp-salario-bruto').textContent = fmtPeso(salario); document.getElementById('cp-total-ingresos').textContent= fmtPeso(salario); // Descuentos tabla var filas = [ {concepto:'Descuentos de ley (Salud/Pensión/Rtf.)', valor: descLey}, {concepto:'Libranzas y créditos actuales', valor: descTotal} ]; document.getElementById('cp-desc-tabla').innerHTML = filas.map(function(f){ return ''+f.concepto+'' +''+fmtPeso(f.valor)+''; }).join(''); document.getElementById('cp-total-desc').textContent = fmtPeso(descLey + descTotal); document.getElementById('cp-neto').textContent = fmtPeso(neto); // Evaluación Ley 1527 document.getElementById('cp-limite').textContent = fmtPeso(limite50); document.getElementById('cp-desc-fin').textContent = fmtPeso(descFin); document.getElementById('cp-cob-adicional').textContent = fmtPeso(cobAd); document.getElementById('cp-total-nuevo').textContent = fmtPeso(totalNuevo); document.getElementById('cp-margen-formula').textContent= fmtPeso(limite50)+' − '+fmtPeso(totalNuevo); document.getElementById('cp-margen-ley').textContent = fmtPeso(margenLey); // Concepto var viable = margenLey > 0 && margen > 0; var conceptoBox = document.getElementById('cp-concepto-box'); conceptoBox.style.background = viable ? '#E8F5E9' : '#FFEBEE'; document.getElementById('cp-concepto-ico').textContent = viable ? '✓' : '✗'; document.getElementById('cp-concepto-ico').style.color = viable ? '#2E7D32' : '#CC0000'; document.getElementById('cp-concepto-val').textContent = viable ? 'VIABLE' : 'NO VIABLE'; document.getElementById('cp-concepto-val').style.color = viable ? '#2E7D32' : '#CC0000'; var pct = Math.round((totalNuevo / salario) * 100); document.getElementById('cp-concepto-desc').textContent = 'Con la cobertura adicional de '+fmtPeso(cobAd)+', el total de descuentos financieros representa el ' +pct+'% de la mesada, '+(margenLey>0?'por debajo':'por encima')+' del límite del 50% establecido por política y Ley 1527.'; document.getElementById('cp-obs-lista').innerHTML = [ 'Ingresos '+((salario>2000000)?'estables y suficientes':'a verificar')+'.', 'Descuentos actuales representan el '+Math.round((descTotal/salario)*100)+'% del salario.', 'Neto actual recibido '+((neto>1300000)?'por encima':'por debajo')+' de 1 SMMLV.' ].map(function(o){ return '
  • '+o+'
  • '; }).join(''); // Perfil var perfil = margenLey > 500000 ? 'BAJO' : margenLey > 0 ? 'MEDIO' : 'ALTO'; var colorP = perfil==='BAJO'?'#2E7D32': perfil==='MEDIO'?'#E65100':'#CC0000'; document.getElementById('cp-perfil-val').textContent = perfil; document.getElementById('cp-perfil-val').style.color = colorP; // Libre inversión var deducidosInf = parseMonto('lib-deducidos') || (descTotal + descLey); var descFinInf = deducidosInf - descLey; var capLibreInf = capacidad - descFinInf; var elCL = document.getElementById('cpinf-cap-lib'); var elDF = document.getElementById('cpinf-desc-fin'); var elCLI= document.getElementById('cpinf-cap-libre'); if(elCL) elCL.textContent = fmtPeso(capacidad); if(elDF) elDF.textContent = fmtPeso(descFinInf); if(elCLI){ elCLI.textContent=fmtPeso(capLibreInf); elCLI.style.color=capLibreInf>0?'#1565C0':'#CC0000'; } // Escenarios compra de cartera if(cuotas.length > 0){ var listaEsc = document.getElementById('cp-escenarios-lista'); var html = ''; // Escenario base (sin compra) html += cpEscenario('Sin compra de cartera', capacidad, descTotal, 'Capacidad: '+fmtPeso(capacidad)+' — Descuentos: '+fmtPeso(descTotal), '#1565C0','#E3F2FD'); // Por cada cuota cuotas.forEach(function(c){ var descNuevo = descTotal - c.valor; var margenNuevo= capacidad - descNuevo; html += cpEscenario( 'Con compra de cartera: '+c.entidad, capacidad, descNuevo, 'Liberando cuota de '+fmtPeso(c.valor)+' → Nuevo descuento: '+fmtPeso(descNuevo), '#6A1B9A','#F3E5F5'); }); // Todas if(cuotas.length > 1){ var totalLib = cuotas.reduce(function(s,c){ return s+c.valor; },0); var descTodas = descTotal - totalLib; html += cpEscenario( 'Con compra de cartera TOTAL ('+cuotas.length+' créditos)', capacidad, descTodas, 'Liberando '+fmtPeso(totalLib)+' en cuotas → Nuevo descuento: '+fmtPeso(descTodas), '#00695C','#E0F2F1'); } listaEsc.innerHTML = html; document.getElementById('cp-escenarios-bloque').style.display = 'block'; } else { document.getElementById('cp-escenarios-bloque').style.display = 'none'; } document.getElementById('bloque-cap-informe').style.display = 'block'; } function cpEscenario(titulo, capacidad, descNuevo, sub, color, bg){ var margen = capacidad - descNuevo; var ico = margen > 0 ? '✓' : '⚠'; var colorM = margen > 0 ? '#2E7D32' : '#CC0000'; return '
    ' +'
    ' +'
    '+ico+' '+titulo+'
    ' +'
    '+sub+'
    ' +'
    ' +'
    ' +'
    '+fmtPeso(margen)+'
    ' +'
    margen disponible
    ' +'
    ' +'
    '; } // ── VALIDACIONES CRUZADAS — ALERTAS ───────────────────────── function ejecutarValidaciones(){ var alertas = []; // Leer valores var salario = parseMonto('lib-salario'); var descTotal = parseMonto('lib-total'); var cuotaCifin = parseMonto('val-cuota-cifin'); var libSinNomina = document.getElementById('val-lib-sin-nomina')?.value === 'si'; var entSinNomina = document.getElementById('val-entidad-sin-nomina')?.value.trim() || 'entidad no especificada'; // ══════════════════════════════════════════════════════════ // ALERTA 1 — Libranza en CIFIN que NO opera por nómina // ══════════════════════════════════════════════════════════ if(libSinNomina){ alertas.push({ nivel: 'CRÍTICA', codigo: 'ALT-001', titulo: '⛔ Libranza en CIFIN sin descuento por nómina', desc: 'Se encontró obligación de libranza reportada en CIFIN a nombre de "' + entSinNomina + '" que NO aparece descontada en el desprendible de pago. ' + 'Esto indica que la cuota puede estar en mora, haber sido cedida a otra entidad ' + 'o que el cliente tiene una deuda oculta no declarada.', accion: [ 'Solicitar al cliente el extracto o paz y salvo de "' + entSinNomina + '".', 'Verificar si la libranza fue cedida o se está cobrando por otro medio.', 'NO desembolsar hasta clarificar el origen y estado de esta obligación.', 'Si no hay explicación, considerarlo como causal de NEGACIÓN.' ], bg: '#FFEBEE', borde: '#CC0000', colorTitulo: '#CC0000' }); } // ══════════════════════════════════════════════════════════ // ALERTA 2 — Cuota CIFIN menor que la reportada en nómina // ══════════════════════════════════════════════════════════ if(cuotaCifin > 0 && descTotal > 0 && cuotaCifin < descTotal * 0.85){ var diferencia = descTotal - cuotaCifin; var pctDif = Math.round((diferencia / descTotal) * 100); alertas.push({ nivel: 'CRÍTICA', codigo: 'ALT-002', titulo: '⛔ Cuota CIFIN menor que la reportada en desprendible', desc: 'La cuota de libranza reportada en CIFIN (' + fmtPeso(cuotaCifin) + ') ' + 'es ' + pctDif + '% menor que el total de descuentos en el desprendible (' + fmtPeso(descTotal) + '). Diferencia: ' + fmtPeso(diferencia) + '. ' + 'Esto indica que hay obligaciones en el desprendible que no están reportadas en la central de riesgos, ' + 'lo cual puede significar descuentos irregulares, deuda informal o información desactualizada.', accion: [ 'Solicitar certificado de descuentos actualizado de la entidad pagadora.', 'Verificar con el empleador o fondo de pensiones cada concepto descontado.', 'Identificar la(s) entidad(es) que descuentan sin reporte en CIFIN.', 'Considerar el descuento real del desprendible, NO el de CIFIN, para calcular capacidad de pago.' ], bg: '#FFEBEE', borde: '#CC0000', colorTitulo: '#CC0000' }); } // ══════════════════════════════════════════════════════════ // ALERTA 3 — Cuota CIFIN MAYOR que la del desprendible // ══════════════════════════════════════════════════════════ if(cuotaCifin > 0 && descTotal > 0 && cuotaCifin > descTotal * 1.15){ var difE = cuotaCifin - descTotal; alertas.push({ nivel: 'MEDIA', codigo: 'ALT-003', titulo: '⚠ Cuota CIFIN mayor que el desprendible', desc: 'La cuota reportada en CIFIN (' + fmtPeso(cuotaCifin) + ') ' + 'supera el total descontado en nómina (' + fmtPeso(descTotal) + '). ' + 'Diferencia de ' + fmtPeso(difE) + '. ' + 'Puede indicar que el CIFIN está desactualizado, que hay pagos voluntarios adicionales ' + 'o que la libranza tiene cuotas extras (seguros, mora).', accion: [ 'Verificar si el CIFIN tiene fecha reciente de actualización.', 'Confirmar si existen pagos adicionales fuera de nómina.', 'Usar el valor del desprendible como referencia principal.' ], bg: '#FFF3E0', borde: '#E65100', colorTitulo: '#E65100' }); } // ══════════════════════════════════════════════════════════ // ALERTA 4 — Descuentos superan el 50% del salario // ══════════════════════════════════════════════════════════ if(salario > 0 && descTotal > 0){ var pct50 = descTotal / salario; if(pct50 > 0.5){ alertas.push({ nivel: 'CRÍTICA', codigo: 'ALT-004', titulo: '⛔ Descuentos superan el 50% del salario (Ley 1527)', desc: 'Los descuentos actuales (' + fmtPeso(descTotal) + ') representan el ' + Math.round(pct50*100) + '% del salario bruto (' + fmtPeso(salario) + '). ' + 'La Ley 1527 establece que los descuentos por libranza NO pueden superar el 50% del ingreso. ' + 'El cliente NO tiene capacidad disponible para nuevas obligaciones.', accion: [ 'Verificar si alguna libranza actual puede ser liquidada o reestructurada.', 'Evaluar compra de cartera para liberar margen.', 'Si no hay margen, negar el crédito por incumplimiento de la Ley 1527.' ], bg: '#FFEBEE', borde: '#CC0000', colorTitulo: '#CC0000' }); } else if(pct50 > 0.40){ alertas.push({ nivel: 'MEDIA', codigo: 'ALT-005', titulo: '⚠ Descuentos entre el 40% y 50% del salario', desc: 'Los descuentos actuales representan el ' + Math.round(pct50*100) + '% del salario. ' + 'Aunque no supera el límite legal, el margen disponible es reducido. ' + 'Una nueva cuota podría llevar al cliente al límite de la Ley 1527.', accion: [ 'Evaluar monto conservador para no superar el 50%.', 'Considerar plazos más largos para reducir la cuota mensual.', 'Informar al asesor sobre el margen limitado.' ], bg: '#FFF3E0', borde: '#E65100', colorTitulo: '#E65100' }); } } // ── Renderizar alertas ────────────────────────────────── var lista = document.getElementById('alertas-lista'); var count = alertas.length; document.getElementById('alerta-count').textContent = count; if(count === 0){ lista.innerHTML = ''; document.getElementById('alertas-ninguna').style.display = 'block'; document.getElementById('alertas-instrucciones').textContent = '✓ No se detectaron inconsistencias entre la información del CIFIN, el desprendible y los datos ingresados.'; } else { document.getElementById('alertas-ninguna').style.display = 'none'; lista.innerHTML = alertas.map(function(a){ var nivelBg = a.nivel==='CRÍTICA' ? '#CC0000' : '#E65100'; return '
    ' // Header alerta +'
    ' +'
    '+a.titulo+'
    ' +''+a.nivel+'' +'
    ' // Descripción +'
    ' +'
    '+a.desc+'
    ' // Acciones +'
    ' +'
    📌 Acciones requeridas
    ' +'
      ' + a.accion.map(function(ac){ return '
    • '+ac+'
    • '; }).join('') +'
    ' +'
    Código: '+a.codigo+'
    ' +'
    ' +'
    '; }).join(''); var instrTxt = alertas.filter(function(a){ return a.nivel==='CRÍTICA'; }).length > 0 ? '⛔ Se detectaron alertas CRÍTICAS. El proceso de crédito debe detenerse hasta resolver las inconsistencias. No continuar con el desembolso.' : '⚠ Se detectaron alertas de nivel medio. Revisar y aclarar con el cliente antes de tomar una decisión.'; document.getElementById('alertas-instrucciones').textContent = instrTxt; } document.getElementById('bloque-alertas').style.display = 'block'; } function renderAlertasIniciales(){ document.getElementById('bloque-alertas').style.display = 'block'; } // ── CIFIN ──────────────────────────────────────────────────── var datosCifin = { score: 686, scoreLabel: 'ACEPTABLE', scoreSub: 'Clientes que hayan tenido al menos 1 cuenta castigada', nombre: '', cedula: '', fecha: '', cuota: 3958, riesgo: 'MEDIO', riesgoDesc: 'Perfil con buen comportamiento reciente, pero con antecedentes de castigos, mora activa y nivel de endeudamiento relativamente alto.', endeudamiento: [ {sector:'Financiero', cant:3, saldo:123867}, {sector:'Solidario', cant:1, saldo:5232}, {sector:'Tarjetas crédito', cant:1, saldo:4221}, {sector:'Sector real', cant:4, saldo:40} ], antiguedad: '+40 años en el sistema financiero', historial: [ 'Amplia trayectoria crediticia desde 1985.', 'Múltiples obligaciones canceladas satisfactoriamente.', 'Relación con entidades financieras reconocidas.', 'La mayoría de las cuentas vigentes se encuentran al día.' ], obligaciones: [ {entidad:'Banco Unión', saldo:'$75,857 mil', cuota:'$1,735 mil', estado:'VIGENTE NORMAL'}, {entidad:'Crezcamos', saldo:'$48,010 mil', cuota:'$936 mil', estado:'VIGENTE NORMAL'}, {entidad:'Serfinanza', saldo:'$4,221 mil', cuota:'$306 mil', estado:'VIGENTE AL DÍA'} ], hallazgos: [ {tipo:'MORA ACTIVA', desc:'Colombia Móvil ESP – $40 mil. Deuda insoluta. Permanencia negativa hasta 2033.'}, {tipo:'ANTECEDENTES DE CASTIGOS', desc:'La subpoblación del score indica historial de cuentas castigadas.'}, {tipo:'EDAD PROBABLE', desc:'Más de 75 años. Aumenta el riesgo actuarial y de estabilidad de ingresos.'} ], consultas: [ {fecha:'10/02/2026', entidad:'Banco Unión S.A.'}, {fecha:'20/01/2026', entidad:'Banco de Occidente'}, {fecha:'07/01/2026', entidad:'Kala Colombia SAS'}, {fecha:'12/12/2025', entidad:'Banco de Occidente'} ], fortalezas: ['Amplia trayectoria financiera.','Varias obligaciones canceladas correctamente.','Créditos vigentes mayormente al día.','Baja mora activa.','Experiencia con banca tradicional.'], riesgos: ['Score medio (686).','Antecedentes de cartera castigada.','Mora activa en telecomunicaciones.','Endeudamiento relativamente alto.','Edad avanzada para obligaciones de largo plazo.'], recTitulo: 'APROBABLE CON MITIGANTES', recomendaciones: ['Verificar y soportar ingresos.','Evaluar capacidad de pago actual.','Evitar plazos excesivamente largos.','Solicitar respaldo patrimonial o codeudor si el monto es alto.','Normalizar la mora en telecomunicaciones antes del desembolso.'], evalFinal: 'Perfil con capacidad de pago actual aceptable, pero con factores de riesgo que requieren mitigación y seguimiento.' }; function renderCifin(){ var d = datosCifin; var cedula = document.getElementById('cli-cedula')?.value || ''; var nombre = 'CLIENTE ' + (cedula || '—'); // Header document.getElementById('cf-nombre').textContent = d.nombre || nombre; document.getElementById('cf-cedula').textContent = 'CC ' + (d.cedula || cedula || '—'); document.getElementById('cf-fecha').textContent = d.fecha || new Date().toLocaleDateString('es-CO'); // Score document.getElementById('cf-score-val').textContent = d.score; document.getElementById('cf-score-label').textContent = d.scoreLabel; document.getElementById('cf-score-sub').textContent = '"' + d.scoreSub + '"'; var pct = ((d.score - 150) / (950 - 150)) * 100; document.getElementById('cf-score-ptr').style.left = Math.min(Math.max(pct,2),98) + '%'; var sc = document.getElementById('cf-score-val'); sc.style.color = d.score >= 750 ? '#2E7D32' : d.score >= 650 ? '#E65100' : '#CC0000'; // Cuota y riesgo document.getElementById('cf-cuota-val').textContent = '$' + d.cuota.toLocaleString('es-CO'); document.getElementById('cf-riesgo-val').textContent = d.riesgo; document.getElementById('cf-riesgo-desc').textContent = d.riesgoDesc; var rb = document.getElementById('cf-riesgo-box'); rb.style.background = d.riesgo==='BAJO'?'#E8F5E9': d.riesgo==='ALTO'?'#FFEBEE':'#FFF3E0'; document.getElementById('cf-riesgo-val').style.color = d.riesgo==='BAJO'?'#2E7D32': d.riesgo==='ALTO'?'#CC0000':'#E65100'; // Endeudamiento var totC=0, totS=0; document.getElementById('cf-tabla-end').innerHTML = d.endeudamiento.map(function(r){ totC += r.cant; totS += r.saldo; return ''+r.sector+'' +''+r.cant+'' +''+r.saldo.toLocaleString('es-CO')+''; }).join(''); document.getElementById('cf-total-cant').textContent = totC; document.getElementById('cf-total-saldo').textContent = totS.toLocaleString('es-CO'); // Historial document.getElementById('cf-antiguedad').textContent = d.antiguedad; document.getElementById('cf-historial-list').innerHTML = d.historial.map(function(h){ return '
  • '+h+'
  • '; }).join(''); // Obligaciones document.getElementById('cf-obligaciones').innerHTML = d.obligaciones.map(function(o){ var bg = o.estado.includes('NORMAL') ? '#E3F2FD' : '#E8F5E9'; var co = o.estado.includes('NORMAL') ? '#1565C0' : '#2E7D32'; return '
    ' +'
    '+o.entidad+'
    ' +'
    Saldo: '+o.saldo+' • Cuota: '+o.cuota+'
    ' +''+o.estado+'' +'
    '; }).join(''); // Hallazgos var icons = {MORA:'⚠', ANTE:'📋', EDAD:'👤'}; document.getElementById('cf-hallazgos').innerHTML = d.hallazgos.map(function(h){ var k = Object.keys(icons).find(function(k){ return h.tipo.includes(k); }) || 'MORA'; return '
    '+(icons[k]||'⚠')+' '+h.tipo+'
    ' +'
    '+h.desc+'
    '; }).join(''); // Consultas document.getElementById('cf-consultas').innerHTML = d.consultas.map(function(c){ return '
    ' +''+c.fecha+''+c.entidad+'
    '; }).join('') + '
    Total consultas: '+d.consultas.length+'
    '; // Fortalezas, riesgos, recomendaciones document.getElementById('cf-fortalezas').innerHTML = d.fortalezas.map(function(f){ return '
  • '+f+'
  • '; }).join(''); document.getElementById('cf-riesgos-list').innerHTML = d.riesgos.map(function(r){ return '
  • '+r+'
  • '; }).join(''); document.getElementById('cf-rec-titulo').textContent = d.recTitulo; document.getElementById('cf-rec-lista').innerHTML = d.recomendaciones.map(function(r){ return '
  • '+r+'
  • '; }).join(''); document.getElementById('cf-eval-final').textContent = d.evalFinal; document.getElementById('bloque-cifin').style.display = 'block'; } function abrirEditorCifin(){ var d = datosCifin; var score = prompt('Score CIFIN (Ej: 686):', d.score); if(score===null) return; var label = prompt('Clasificación (EXCELENTE/BUENO/ACEPTABLE/REGULAR/MALO):', d.scoreLabel); var riesgo = prompt('Riesgo global (BAJO/MEDIO/ALTO):', d.riesgo); var cuota = prompt('Cuota mensual aproximada en miles (Ej: 3958):', d.cuota); var nombre = prompt('Nombre del cliente:', d.nombre); var cedula = prompt('Cédula:', d.cedula || document.getElementById('cli-cedula')?.value || ''); var eval_ = prompt('Evaluación final:', d.evalFinal); datosCifin.score = parseInt(score)||d.score; datosCifin.scoreLabel = (label||d.scoreLabel).toUpperCase(); datosCifin.riesgo = (riesgo||d.riesgo).toUpperCase(); datosCifin.cuota = parseInt(cuota)||d.cuota; datosCifin.nombre = nombre||d.nombre; datosCifin.cedula = cedula||d.cedula; datosCifin.evalFinal = eval_||d.evalFinal; renderCifin(); } // ── LEGAL CHECK + TYBA ─────────────────────────────────────── var PLATAFORMAS = [ {id:'legalcheck', nombre:'Legal Check Plus', icon:'⚖️', color:'#1565C0'}, {id:'tyba', nombre:'TYBA Justicia XXI', icon:'🏛️', color:'#4A148C'}, {id:'procuraduria',nombre:'Procuraduría', icon:'🏛', color:'#B71C1C'}, {id:'contraloria', nombre:'Contraloría', icon:'📋', color:'#E65100'} ]; var lcDatos = { nacCoincidencias: 0, intCoincidencias: 0, procesos: 0, insolvencia: false, cooperativa: false, embargo: false, perfil: 'MEDIO', concepto: 'VIABLE_MITIGANTES', plataformas: {legalcheck:'pendiente', tyba:'pendiente', procuraduria:'pendiente', contraloria:'pendiente'} }; function aplicarLegalCheck2(){ lcDatos.nacCoincidencias = parseInt(document.getElementById('lci-nac')?.value)||0; lcDatos.intCoincidencias = parseInt(document.getElementById('lci-int')?.value)||0; lcDatos.procesos = parseInt(document.getElementById('lci-proc')?.value)||0; lcDatos.insolvencia = document.getElementById('lci-insolvencia')?.value === 'si'; lcDatos.cooperativa = document.getElementById('lci-cooperativa')?.value === 'si'; lcDatos.embargo = document.getElementById('lci-embargo')?.value === 'si'; lcDatos.perfil = document.getElementById('lci-perfil')?.value || 'MEDIO'; lcDatos.concepto = document.getElementById('lci-concepto')?.value || 'VIABLE_MITIGANTES'; renderLegalCheck2(); } function renderLegalCheck2(){ var d = lcDatos; var cedula = document.getElementById('cli-cedula')?.value || '—'; var fecha = new Date().toLocaleDateString('es-CO'); var codigo = Math.random().toString(36).substr(2,16); // Header document.getElementById('lc-fecha').textContent = fecha; document.getElementById('lc-fecha2').textContent = fecha; document.getElementById('lc-codigo').textContent = codigo; document.getElementById('lc-nombre').textContent = 'CLIENTE CC ' + cedula; document.getElementById('lc-doc').textContent = 'CC ' + cedula; document.getElementById('lc-edad').textContent = '—'; // Resumen numérico document.getElementById('rs-nac-num').textContent = d.nacCoincidencias; document.getElementById('rs-nac-num').style.color = d.nacCoincidencias>0?'#E65100':'#2E7D32'; document.getElementById('rs-nac-lbl').textContent = d.nacCoincidencias>0?'con coincidencias':'sin coincidencias'; document.getElementById('rs-int-num').textContent = d.intCoincidencias; document.getElementById('rs-int-num').style.color = d.intCoincidencias>0?'#CC0000':'#2E7D32'; document.getElementById('rs-int-lbl').textContent = d.intCoincidencias>0?'con coincidencias':'sin coincidencias'; document.getElementById('rs-proc-num').textContent = d.procesos; document.getElementById('rs-proc-num').style.color = d.procesos>0?'#E65100':'#2E7D32'; document.getElementById('rs-proc-det').textContent = 'coincidencias procesales'; // Procesos judiciales var procHtml = ''; if(d.insolvencia) procHtml += '
    ⛔ Proceso de INSOLVENCIA detectado
    '; if(d.cooperativa) procHtml += '
    ⛔ Proceso con COOPERATIVA detectado
    '; if(d.embargo) procHtml += '
    ⛔ EMBARGO DE NÓMINA detectado
    '; if(d.procesos>0 && !d.insolvencia && !d.cooperativa && !d.embargo) procHtml += '
    ⚠ '+d.procesos+' proceso(s) judicial(es) encontrado(s)
    '; if(!procHtml) procHtml = '
    ✓ Sin procesos judiciales críticos
    '; document.getElementById('lc-procesos-lista').innerHTML = procHtml; document.getElementById('lc-otros-procesos').innerHTML = d.procesos>1 ? '
    '+d.procesos+' coincidencias encontradas — verificar estado actualizado
    ' : ''; // Listas document.getElementById('lc-listas-nac').innerHTML = d.nacCoincidencias>0 ? '
    ⚠ '+d.nacCoincidencias+' lista(s) con coincidencias detectadas
    ' : '
    ✓ Sin coincidencias en listas nacionales
    '; document.getElementById('lc-listas-int').innerHTML = d.intCoincidencias>0 ? '
    ⛔ '+d.intCoincidencias+' lista(s) internacional(es) con coincidencias
    ' : '
    ✓ Sin coincidencias en listas internacionales
    '; document.getElementById('lc-peps').innerHTML = '
    ✓ Sin coincidencias en PEPs nacionales e internacionales
    ' + '
    ✓ Sin aparición en noticias negativas
    '; // Evaluación de riesgo — tabla factores var negado = d.insolvencia || d.cooperativa || d.embargo || d.intCoincidencias > 0; var factores = [ {factor:'Listas restrictivas', resultado: d.nacCoincidencias>0?d.nacCoincidencias+' coincidencias':'Sin coincidencias', nivel: d.nacCoincidencias>0?'Medio':'Bajo'}, {factor:'PEPs', resultado:'Sin coincidencias', nivel:'Bajo'}, {factor:'Noticias negativas', resultado:'No registra', nivel:'Bajo'}, {factor:'Procesos judiciales', resultado: d.procesos>0?d.procesos+' coincidencias':'Sin coincidencias', nivel: d.procesos>5?'Alto': d.procesos>0?'Medio':'Bajo'}, {factor:'Insolvencia', resultado: d.insolvencia?'DETECTADA':'No registra', nivel: d.insolvencia?'Alto':'Bajo'}, {factor:'Cooperativas', resultado: d.cooperativa?'PROCESO ACTIVO':'No registra', nivel: d.cooperativa?'Alto':'Bajo'}, {factor:'Embargo nómina', resultado: d.embargo?'DETECTADO':'No registra', nivel: d.embargo?'Alto':'Bajo'}, {factor:'Riesgo LA/FT', resultado:'Sin alertas', nivel:'Bajo'} ]; var nivelBg = {Alto:'#FFEBEE',Medio:'#FFF3E0',Bajo:'#E8F5E9'}; var nivelColor = {Alto:'#CC0000',Medio:'#E65100',Bajo:'#2E7D32'}; document.getElementById('lc-eval-tbody').innerHTML = factores.map(function(f){ return '' +''+f.factor+'' +''+f.resultado+'' +'' +''+f.nivel+'' +''; }).join(''); // RUAF document.getElementById('lc-ruaf').innerHTML = '
    💚 Salud (EPS)
    ' +'
    Verificar afiliación activa al sistema de salud
    ' +'
    💰 Pensiones
    ' +'
    Verificar entidad pagadora en desprendible adjunto
    '; // Concepto final var CONCEPTOS = { VIABLE: {txt:'VIABLE', ico:'✅', color:'#2E7D32', bg:'#E8F5E9', desc:'El titular no presenta alertas críticas. Perfil adecuado para evaluación crediticia.', recs:['Verificar ingresos y soportes.','Evaluar capacidad de pago actual.']}, VIABLE_MITIGANTES: {txt:'VIABLE CON MITIGANTES', ico:'✔', color:'#1565C0', bg:'#E3F2FD', desc:'Viable para evaluación comercial o crediticia con condiciones. Se recomienda validar hallazgos antes del desembolso.', recs:['Verificar estado actual de los procesos.','Evaluar capacidad de pago.','Solicitar soportes de ingresos / pensión.']}, REVISAR: {txt:'REVISAR', ico:'⚠', color:'#E65100', bg:'#FFF3E0', desc:'Se encontraron hallazgos que requieren verificación adicional antes de aprobar.', recs:['Solicitar aclaraciones al cliente.','Verificar estado actualizado de los procesos.','Consultar otras plataformas de búsqueda.']}, NEGADO: {txt:'NEGADO', ico:'⛔', color:'#CC0000', bg:'#FFEBEE', desc:'El perfil presenta alertas críticas que impiden el otorgamiento del crédito: insolvencia, cooperativas o embargo de nómina.', recs:['Crédito negado por política interna.','Informar al asesor y al cliente.','Conservar registro del análisis.']}, }; var concepto = negado ? CONCEPTOS.NEGADO : (CONCEPTOS[d.concepto] || CONCEPTOS.VIABLE_MITIGANTES); document.getElementById('lc-concepto-box').style.borderColor = concepto.color; document.getElementById('lc-concepto-ico').textContent = concepto.ico; document.getElementById('lc-concepto-txt').textContent = concepto.txt; document.getElementById('lc-concepto-txt').style.color = concepto.color; document.getElementById('lc-concepto-desc').textContent = concepto.desc; document.getElementById('lc-rec-lista').innerHTML = concepto.recs.map(function(r){ return '
  • ✔ '+r+'
  • '; }).join(''); // Badge header var badge = document.getElementById('lc-concepto-badge'); badge.textContent = concepto.txt; badge.style.background = concepto.color; badge.style.color = '#fff'; // Plataformas var plat = document.getElementById('lc-plataformas'); var resultPlat = { legalcheck: negado?'NEGADO': d.nacCoincidencias>0||d.procesos>0?'CON HALLAZGOS':'LIMPIO', tyba: d.insolvencia||d.cooperativa?'NEGADO': d.procesos>0?'CON PROCESOS':'LIMPIO', procuraduria:'PENDIENTE', contraloria: 'PENDIENTE' }; var platBg = {NEGADO:'#FFEBEE',LIMPIO:'#E8F5E9','CON HALLAZGOS':'#FFF3E0','CON PROCESOS':'#FFF3E0',PENDIENTE:'#f5f5f5'}; var platColor = {NEGADO:'#CC0000',LIMPIO:'#2E7D32','CON HALLAZGOS':'#E65100','CON PROCESOS':'#E65100',PENDIENTE:'#888'}; plat.innerHTML = PLATAFORMAS.map(function(p){ var res = resultPlat[p.id] || 'PENDIENTE'; return '
    ' +'
    '+p.icon+'
    ' +'
    '+p.nombre+'
    ' +'
    '+res+'
    ' +'
    '; }).join(''); document.getElementById('bloque-legal').style.display = 'block'; } function renderLegalInicial(){ document.getElementById('bloque-legal').style.display = 'block'; } // ── VALIDACIONES CRUZADAS — ALERTAS ───────────────────────── function ejecutarValidaciones(){ var alertas = []; // Leer valores var salario = parseMonto('lib-salario'); var descTotal = parseMonto('lib-total'); var cuotaCifin = parseMonto('val-cuota-cifin'); var libSinNomina = document.getElementById('val-lib-sin-nomina')?.value === 'si'; var entSinNomina = document.getElementById('val-entidad-sin-nomina')?.value.trim() || 'entidad no especificada'; // ══════════════════════════════════════════════════════════ // ALERTA 1 — Libranza en CIFIN que NO opera por nómina // ══════════════════════════════════════════════════════════ if(libSinNomina){ alertas.push({ nivel: 'CRÍTICA', codigo: 'ALT-001', titulo: '⛔ Libranza en CIFIN sin descuento por nómina', desc: 'Se encontró obligación de libranza reportada en CIFIN a nombre de "' + entSinNomina + '" que NO aparece descontada en el desprendible de pago. ' + 'Esto indica que la cuota puede estar en mora, haber sido cedida a otra entidad ' + 'o que el cliente tiene una deuda oculta no declarada.', accion: [ 'Solicitar al cliente el extracto o paz y salvo de "' + entSinNomina + '".', 'Verificar si la libranza fue cedida o se está cobrando por otro medio.', 'NO desembolsar hasta clarificar el origen y estado de esta obligación.', 'Si no hay explicación, considerarlo como causal de NEGACIÓN.' ], bg: '#FFEBEE', borde: '#CC0000', colorTitulo: '#CC0000' }); } // ══════════════════════════════════════════════════════════ // ALERTA 2 — Cuota CIFIN menor que la reportada en nómina // ══════════════════════════════════════════════════════════ if(cuotaCifin > 0 && descTotal > 0 && cuotaCifin < descTotal * 0.85){ var diferencia = descTotal - cuotaCifin; var pctDif = Math.round((diferencia / descTotal) * 100); alertas.push({ nivel: 'CRÍTICA', codigo: 'ALT-002', titulo: '⛔ Cuota CIFIN menor que la reportada en desprendible', desc: 'La cuota de libranza reportada en CIFIN (' + fmtPeso(cuotaCifin) + ') ' + 'es ' + pctDif + '% menor que el total de descuentos en el desprendible (' + fmtPeso(descTotal) + '). Diferencia: ' + fmtPeso(diferencia) + '. ' + 'Esto indica que hay obligaciones en el desprendible que no están reportadas en la central de riesgos, ' + 'lo cual puede significar descuentos irregulares, deuda informal o información desactualizada.', accion: [ 'Solicitar certificado de descuentos actualizado de la entidad pagadora.', 'Verificar con el empleador o fondo de pensiones cada concepto descontado.', 'Identificar la(s) entidad(es) que descuentan sin reporte en CIFIN.', 'Considerar el descuento real del desprendible, NO el de CIFIN, para calcular capacidad de pago.' ], bg: '#FFEBEE', borde: '#CC0000', colorTitulo: '#CC0000' }); } // ══════════════════════════════════════════════════════════ // ALERTA 3 — Cuota CIFIN MAYOR que la del desprendible // ══════════════════════════════════════════════════════════ if(cuotaCifin > 0 && descTotal > 0 && cuotaCifin > descTotal * 1.15){ var difE = cuotaCifin - descTotal; alertas.push({ nivel: 'MEDIA', codigo: 'ALT-003', titulo: '⚠ Cuota CIFIN mayor que el desprendible', desc: 'La cuota reportada en CIFIN (' + fmtPeso(cuotaCifin) + ') ' + 'supera el total descontado en nómina (' + fmtPeso(descTotal) + '). ' + 'Diferencia de ' + fmtPeso(difE) + '. ' + 'Puede indicar que el CIFIN está desactualizado, que hay pagos voluntarios adicionales ' + 'o que la libranza tiene cuotas extras (seguros, mora).', accion: [ 'Verificar si el CIFIN tiene fecha reciente de actualización.', 'Confirmar si existen pagos adicionales fuera de nómina.', 'Usar el valor del desprendible como referencia principal.' ], bg: '#FFF3E0', borde: '#E65100', colorTitulo: '#E65100' }); } // ══════════════════════════════════════════════════════════ // ALERTA 4 — Descuentos superan el 50% del salario // ══════════════════════════════════════════════════════════ if(salario > 0 && descTotal > 0){ var pct50 = descTotal / salario; if(pct50 > 0.5){ alertas.push({ nivel: 'CRÍTICA', codigo: 'ALT-004', titulo: '⛔ Descuentos superan el 50% del salario (Ley 1527)', desc: 'Los descuentos actuales (' + fmtPeso(descTotal) + ') representan el ' + Math.round(pct50*100) + '% del salario bruto (' + fmtPeso(salario) + '). ' + 'La Ley 1527 establece que los descuentos por libranza NO pueden superar el 50% del ingreso. ' + 'El cliente NO tiene capacidad disponible para nuevas obligaciones.', accion: [ 'Verificar si alguna libranza actual puede ser liquidada o reestructurada.', 'Evaluar compra de cartera para liberar margen.', 'Si no hay margen, negar el crédito por incumplimiento de la Ley 1527.' ], bg: '#FFEBEE', borde: '#CC0000', colorTitulo: '#CC0000' }); } else if(pct50 > 0.40){ alertas.push({ nivel: 'MEDIA', codigo: 'ALT-005', titulo: '⚠ Descuentos entre el 40% y 50% del salario', desc: 'Los descuentos actuales representan el ' + Math.round(pct50*100) + '% del salario. ' + 'Aunque no supera el límite legal, el margen disponible es reducido. ' + 'Una nueva cuota podría llevar al cliente al límite de la Ley 1527.', accion: [ 'Evaluar monto conservador para no superar el 50%.', 'Considerar plazos más largos para reducir la cuota mensual.', 'Informar al asesor sobre el margen limitado.' ], bg: '#FFF3E0', borde: '#E65100', colorTitulo: '#E65100' }); } } // ── Renderizar alertas ────────────────────────────────── var lista = document.getElementById('alertas-lista'); var count = alertas.length; document.getElementById('alerta-count').textContent = count; if(count === 0){ lista.innerHTML = ''; document.getElementById('alertas-ninguna').style.display = 'block'; document.getElementById('alertas-instrucciones').textContent = '✓ No se detectaron inconsistencias entre la información del CIFIN, el desprendible y los datos ingresados.'; } else { document.getElementById('alertas-ninguna').style.display = 'none'; lista.innerHTML = alertas.map(function(a){ var nivelBg = a.nivel==='CRÍTICA' ? '#CC0000' : '#E65100'; return '
    ' // Header alerta +'
    ' +'
    '+a.titulo+'
    ' +''+a.nivel+'' +'
    ' // Descripción +'
    ' +'
    '+a.desc+'
    ' // Acciones +'
    ' +'
    📌 Acciones requeridas
    ' +'
      ' + a.accion.map(function(ac){ return '
    • '+ac+'
    • '; }).join('') +'
    ' +'
    Código: '+a.codigo+'
    ' +'
    ' +'
    '; }).join(''); var instrTxt = alertas.filter(function(a){ return a.nivel==='CRÍTICA'; }).length > 0 ? '⛔ Se detectaron alertas CRÍTICAS. El proceso de crédito debe detenerse hasta resolver las inconsistencias. No continuar con el desembolso.' : '⚠ Se detectaron alertas de nivel medio. Revisar y aclarar con el cliente antes de tomar una decisión.'; document.getElementById('alertas-instrucciones').textContent = instrTxt; } document.getElementById('bloque-alertas').style.display = 'block'; } function renderAlertasIniciales(){ document.getElementById('bloque-alertas').style.display = 'block'; } // ── CIFIN ──────────────────────────────────────────────────── // ── INFORME FINAL ──────────────────────────────────────────── var decisionSeleccionada = null; function abrirInforme(){ var salario = parseMonto('lib-salario'); var descLey = parseMonto('lib-descuentos'); var descTotal = parseMonto('lib-total'); var monto = parseMonto('monto-sol'); var cedula = document.getElementById('cli-cedula')?.value||'—'; var emailCli = document.getElementById('cli-email')?.value||'—'; var agencia = document.getElementById('ag-sel')?.value||'—'; var gestor = document.getElementById('ges-nombre')?.value||'—'; var linea = document.getElementById('linea-credito')?.value||'—'; var baseNeta = salario - descLey; var capacidad = (baseNeta/2) - 10000; var margen = capacidad - descTotal; // ── Llenar datos básicos ── var hoy = new Date().toLocaleDateString('es-CO',{day:'2-digit',month:'long',year:'numeric'}); document.getElementById('inf-fecha').textContent = hoy; document.getElementById('inf-cedula').textContent = 'CC ' + cedula; document.getElementById('inf-email-cli').textContent= emailCli; document.getElementById('inf-agencia').textContent = agencia; document.getElementById('inf-gestor').textContent = gestor; document.getElementById('inf-linea').textContent = linea; document.getElementById('inf-monto').textContent = fmtPeso(monto); // ── Capacidad de pago ── var deducidos = parseMonto('lib-deducidos') || (descTotal + descLey); document.getElementById('inf-salario').textContent = fmtPeso(salario); document.getElementById('inf-descley').textContent = fmtPeso(descLey); document.getElementById('inf-desctotal').textContent = fmtPeso(descTotal); document.getElementById('inf-capacidad').textContent = fmtPeso(capacidad); var mEl = document.getElementById('inf-margen'); mEl.textContent = fmtPeso(margen); mEl.style.color = margen > 0 ? '#2E7D32' : '#CC0000'; // ── CIFIN ── document.getElementById('inf-score').textContent = datosCifin.score||'—'; document.getElementById('inf-score-lbl').textContent = datosCifin.scoreLabel||'—'; document.getElementById('inf-score').style.color = (datosCifin.score||0)>=750?'#2E7D32':(datosCifin.score||0)>=650?'#E65100':'#CC0000'; document.getElementById('inf-riesgo-cifin').textContent = datosCifin.riesgo||'—'; document.getElementById('inf-cuota-cifin').textContent = fmtPeso((datosCifin.cuota||0)*1000); // ── Legal Check ── var lcRes = lcDatos.insolvencia||lcDatos.cooperativa||lcDatos.embargo ? 'NEGADO' : lcDatos.nacCoincidencias>0||lcDatos.procesos>0 ? 'CON HALLAZGOS' : 'LIMPIO'; var lcColor = lcRes==='NEGADO'?'#CC0000': lcRes==='CON HALLAZGOS'?'#E65100':'#2E7D32'; document.getElementById('inf-lc-resultado').textContent = lcRes; document.getElementById('inf-lc-resultado').style.color = lcColor; var lcAlertas = []; if(lcDatos.insolvencia) lcAlertas.push('⛔ Proceso de insolvencia'); if(lcDatos.cooperativa) lcAlertas.push('⛔ Proceso con cooperativa'); if(lcDatos.embargo) lcAlertas.push('⛔ Embargo de nómina'); if(lcDatos.procesos>0) lcAlertas.push('⚠ '+lcDatos.procesos+' proceso(s) judicial(es)'); document.getElementById('inf-lc-alertas').innerHTML = lcAlertas.length ? lcAlertas.join('
    ') : '✓ Sin alertas críticas'; // ── Alertas de inconsistencia ── var alertasCriticas = []; var cuotaCifin = parseMonto('val-cuota-cifin'); var libSinNom = document.getElementById('val-lib-sin-nomina')?.value==='si'; var entSinNom = document.getElementById('val-entidad-sin-nomina')?.value||'—'; if(libSinNom) alertasCriticas.push('⛔ Libranza "'+entSinNom+'" en CIFIN sin descuento por nómina'); if(cuotaCifin>0 && descTotal>0 && cuotaCifin < descTotal*0.85) alertasCriticas.push('⛔ Cuota CIFIN ('+fmtPeso(cuotaCifin)+') menor que desprendible ('+fmtPeso(descTotal)+')'); if(descTotal/salario > 0.5) alertasCriticas.push('⛔ Descuentos superan el 50% del salario — Ley 1527'); var bAlerta = document.getElementById('inf-alertas-bloque'); if(alertasCriticas.length){ document.getElementById('inf-alertas-lista').innerHTML = alertasCriticas.map(function(a){ return '
    '+a+'
    '; }).join(''); bAlerta.style.display = 'block'; } else { bAlerta.style.display = 'none'; } // ── Escenarios compra de cartera ── var bEsc = document.getElementById('inf-escenarios-bloque'); if(cuotas.length){ var escHtml = cuotas.map(function(c){ var m = capacidad - descTotal + c.valor; var co = m>0?'#2E7D32':'#CC0000'; return '
    ' +'
    '+c.entidad+'' +' cuota: '+fmtPeso(c.valor)+'
    ' +'
    Margen: '+fmtPeso(m)+'
    ' +'
    '; }).join(''); document.getElementById('inf-escenarios-lista').innerHTML = escHtml; bEsc.style.display = 'block'; } else { bEsc.style.display = 'none'; } // Limpiar decisión y observaciones decisionSeleccionada = null; document.getElementById('inf-observaciones').value = ''; document.getElementById('inf-obs-count').textContent = '0 caracteres'; ['dec-aprobado','dec-cond','dec-negado'].forEach(function(id){ var el = document.getElementById(id); if(el){ el.style.borderColor='#ccc'; el.style.background='#fff'; } }); document.getElementById('modal-informe').style.display = 'flex'; } function nuevaSolicitud(){ // Cerrar modales document.getElementById('modal-confirmacion').style.display = 'none'; document.getElementById('modal-informe').style.display = 'none'; document.getElementById('bloque-analisis').style.display = 'none'; document.getElementById('bloque-cap-informe').style.display = 'none'; document.getElementById('bloque-alertas').style.display = 'none'; document.getElementById('bloque-cifin').style.display = 'none'; document.getElementById('bloque-legal').style.display = 'none'; // Limpiar campos del formulario var campos = [ 'cli-cedula','cli-email','cli-tel', 'lib-salario','lib-descuentos','lib-total','lib-deducidos', 'ges-nombre','ges-email','ges-cel', 'monto-sol','val-cifin-cedula','val-cuota-cifin', 'val-lc-cedula','val-lc-codigo', 'val-entidad-sin-nomina', 'lci-nac','lci-int','lci-proc' ]; campos.forEach(function(id){ var el = document.getElementById(id); if(el) el.value = ''; }); // Limpiar selects var selects = ['ag-sel','linea-credito','val-lib-sin-nomina','lci-insolvencia','lci-cooperativa','lci-embargo','lci-perfil','lci-concepto']; selects.forEach(function(id){ var el = document.getElementById(id); if(el) el.selectedIndex = 0; }); // Limpiar campo regional var reg = document.getElementById('ag-regional-info'); if(reg) reg.value = ''; // Limpiar cuotas cuotas = []; renderCuotas(); // Limpiar documentos archivos = {}; document.querySelectorAll('.doc-box').forEach(function(box){ box.className = box.className.replace(' cargado',''); var lbl = box.querySelector('.doc-lbl'); if(lbl){ lbl.style.display = 'block'; } var chk = box.querySelector('.doc-check'); if(chk){ chk.style.display = 'none'; } var inp = box.querySelector('input[type=file]'); if(inp) inp.value = ''; }); // Resetear botón analizar var btn = document.getElementById('btn-analizar'); if(btn){ btn.disabled = true; btn.className = 'btn-analizar'; } // Resetear decisión decisionSeleccionada = null; // Resetear estado docs actualizarEstadoDocs(); // Ir a nueva solicitud var tabDocs = document.getElementById('tab-docs'); irVista('docs', tabDocs); // Scroll arriba window.scrollTo({top:0, behavior:'smooth'}); // Mostrar botón nuevo crédito en nav (desaparece al limpiar) var btnNav = document.getElementById('btn-nuevo-credito'); if(btnNav) btnNav.style.display = 'none'; } function cerrarInforme(){ document.getElementById('modal-informe').style.display = 'none'; } function validarObservacion(){ var txt = document.getElementById('inf-observaciones').value; document.getElementById('inf-obs-count').textContent = txt.length + ' caracteres'; document.getElementById('inf-obs-error').style.display = txt.trim() ? 'none' : 'block'; } function selDecision(radio){ decisionSeleccionada = radio.value; var map = {APROBADO:'dec-aprobado', APROBADO_CONDICIONES:'dec-cond', NEGADO:'dec-negado'}; var colors = {APROBADO:'#2E7D32', APROBADO_CONDICIONES:'#E65100', NEGADO:'#CC0000'}; var bgs = {APROBADO:'#E8F5E9', APROBADO_CONDICIONES:'#FFF3E0', NEGADO:'#FFEBEE'}; ['dec-aprobado','dec-cond','dec-negado'].forEach(function(id){ var el = document.getElementById(id); if(el){ el.style.borderColor='#ccc'; el.style.background='#fff'; } }); var sel = document.getElementById(map[radio.value]); if(sel){ sel.style.borderColor = colors[radio.value]; sel.style.background = bgs[radio.value]; } } function guardarYEnviar(){ var obs = document.getElementById('inf-observaciones').value.trim(); if(!obs){ document.getElementById('inf-obs-error').style.display = 'block'; document.getElementById('inf-observaciones').focus(); return; } if(!decisionSeleccionada){ alert('Seleccione una decisión antes de guardar.'); return; } // Construir registro completo var cedula = document.getElementById('cli-cedula')?.value||''; var salario = parseMonto('lib-salario'); var descLey = parseMonto('lib-descuentos'); var descTot = parseMonto('lib-total'); var monto = parseMonto('monto-sol'); var cap = ((salario-descLey)/2)-10000; var margen = cap - descTot; var hoy = new Date().toLocaleDateString('es-CO'); // Capturar analista activo (sesión) var analistaNombre = sesion ? sesion.nombre : 'Sin sesión'; var analistaUser = sesion ? sesion.usuario : '—'; var registro = { fecha: hoy, analista: analistaNombre, analistaUser:analistaUser, cedula: cedula, emailCliente:document.getElementById('cli-email')?.value||'', telCliente: document.getElementById('cli-tel')?.value||'', agencia: document.getElementById('ag-sel')?.value||'', regional: document.getElementById('ag-regional-info')?.value||'', gestor: document.getElementById('ges-nombre')?.value||'', gesEmail: document.getElementById('ges-email')?.value||'', gesCel: document.getElementById('ges-cel')?.value||'', linea: document.getElementById('linea-credito')?.value||'', monto: monto, salario: salario, descLey: descLey, descTotal: descTot, deducidos: parseMonto('lib-deducidos') || 0, capacidad: cap, margen: margen, scoreCifin: datosCifin.score||0, labelCifin: datosCifin.scoreLabel||'', riesgoCifin: datosCifin.riesgo||'', cuotaCifin: datosCifin.cuota||0, lcResultado: lcDatos.concepto||'', insolvencia: lcDatos.insolvencia||false, cooperativa: lcDatos.cooperativa||false, embargo: lcDatos.embargo||false, procesos: lcDatos.procesos||0, cuotas: cuotas, observacion: obs, decision: decisionSeleccionada }; // Guardar local var db = JSON.parse(localStorage.getItem('c2_solicitudes')||'[]'); db.push(registro); localStorage.setItem('c2_solicitudes', JSON.stringify(db)); // Siempre mostrar confirmación local primero var WEBHOOK = getCfg().sheetsWebhook || ''; if(!WEBHOOK){ // Sin webhook: confirmar solo con guardado local mostrarConfirmacion(registro, {ok:true, linkPDF:'', offline:true}); return; } // Con webhook: intentar enviar a Google Drive/Sheets mostrarProgreso('Guardando en Google Drive...', 0); var payload = { accion: 'guardarSolicitud', registro: registro, pdfContent: generarHTMLParaPDF(registro) }; // Usar URL con parámetros para evitar CORS (GET request) var payloadStr = encodeURIComponent(JSON.stringify(payload)); fetch(WEBHOOK, { method: 'POST', headers: {'Content-Type':'text/plain'}, body: JSON.stringify(payload), mode: 'no-cors' }) .then(function(){ mostrarProgreso('', -1); // no-cors no devuelve respuesta — asumir éxito mostrarConfirmacion(registro, {ok:true, linkPDF:'', nocors:true}); }) .catch(function(err){ mostrarProgreso('', -1); mostrarConfirmacion(registro, {ok:true, linkPDF:'', offline:true}); }); } function mostrarProgreso(msg, estado){ var m = document.getElementById('modal-progreso'); if(!m) return; if(estado === -1){ m.style.display='none'; return; } document.getElementById('prog-msg').textContent = msg; m.style.display = 'flex'; } function mostrarConfirmacion(registro, res){ var dec = { APROBADO: '✅ APROBADO', APROBADO_CONDICIONES:'⚠️ APROBADO CON CONDICIONES', NEGADO: '⛔ NEGADO' }; // Actualizar link PDF en el informe si existe if(res.linkPDF){ var lnk = document.getElementById('inf-link-pdf'); if(lnk){ lnk.href = res.linkPDF; lnk.style.display = 'inline-flex'; } } // Mostrar modal de confirmación document.getElementById('conf-cedula').textContent = 'CC ' + registro.cedula; document.getElementById('conf-decision').textContent = dec[registro.decision] || registro.decision; document.getElementById('conf-fecha').textContent = registro.fecha; // Checks dinámicos según si hay webhook var chkSheets = document.getElementById('conf-check-sheets'); var chkPDF = document.getElementById('conf-check-pdf'); if(res.offline){ if(chkSheets){ chkSheets.style.background='#FFF3E0'; chkSheets.style.color='#E65100'; chkSheets.innerHTML=' Sin conexión al servidor'; } if(chkPDF){ chkPDF.style.background='#FFF3E0'; chkPDF.style.color='#E65100'; chkPDF.innerHTML=' PDF pendiente de conexión'; } } else if(res.nocors){ if(chkSheets){ chkSheets.style.background='#E8F5E9'; chkSheets.style.color='#2E7D32'; chkSheets.innerHTML=' Enviado a Google Sheets'; } if(chkPDF){ chkPDF.style.background='#E8F5E9'; chkPDF.style.color='#2E7D32'; chkPDF.innerHTML=' PDF en proceso en Google Drive'; } } else { if(chkSheets){ chkSheets.style.background='#E8F5E9'; chkSheets.style.color='#2E7D32'; chkSheets.innerHTML=' Guardado en Google Sheets'; } if(chkPDF){ chkPDF.style.background='#E8F5E9'; chkPDF.style.color='#2E7D32'; chkPDF.innerHTML=' PDF generado en Google Drive'; } } var pdfDiv = document.getElementById('conf-pdf-link'); if(res.linkPDF && pdfDiv){ pdfDiv.innerHTML = '' +'📄 Abrir PDF en Google Drive'; } else if(pdfDiv){ pdfDiv.innerHTML = res.offline ? 'PDF pendiente — sin conexión al servidor' : 'PDF no disponible'; } document.getElementById('modal-confirmacion').style.display = 'flex'; // Mostrar botón nuevo crédito en el nav var btnNav = document.getElementById('btn-nuevo-credito'); if(btnNav) btnNav.style.display = 'inline-flex'; } function generarHTMLParaPDF(r){ var dec = {APROBADO:'APROBADO',APROBADO_CONDICIONES:'APROBADO CON CONDICIONES',NEGADO:'NEGADO'}; var decColor = {APROBADO:'#2E7D32',APROBADO_CONDICIONES:'#E65100',NEGADO:'#CC0000'}; var d = dec[r.decision]||r.decision; var dc = decColor[r.decision]||'#1a1a1a'; return '' +'' +'

    CREDITO2 SAS — Informe de Pre-Aprobación de Crédito

    ' +'
    Fecha: '+r.fecha+' | CC: '+r.cedula+'
    ' +'' +'' +'' +'' +'' +'' +'
    Datos del cliente y solicitud
    Cédula'+r.cedula+'
    Email'+r.emailCliente+'
    Agencia'+r.agencia+' ('+r.regional+')
    Gestor'+r.gestor+' | '+r.gesEmail+'
    Línea'+r.linea+'
    Monto$'+r.monto.toLocaleString('es-CO')+'
    ' +'' +'' +'' +'' +''+''+''+'' +'
    Capacidad de pago — Ley 1527
    Salario bruto$'+r.salario.toLocaleString('es-CO')+'
    Descuentos de ley$'+r.descLey.toLocaleString('es-CO')+'
    Descuentos libranza$'+r.descTotal.toLocaleString('es-CO')+'
    Capacidad de pago (libranza)$'+Math.round(r.capacidad).toLocaleString('es-CO')+'
    Total deducidos nómina$'+(r.deducidos||0).toLocaleString('es-CO')+'
    Desc. financieros (Total - Ley)$'+Math.round((r.deducidos||0)-r.descLey).toLocaleString('es-CO')+'
    Cap. libre inversión$'+Math.round(r.capacidad-((r.deducidos||0)-r.descLey)).toLocaleString('es-CO')+'
    Margen disponible$'+Math.round(r.margen).toLocaleString('es-CO')+'
    ' +'' +'' +'' +'
    CIFIN — TransUnion
    Score'+r.scoreCifin+' ('+r.labelCifin+')
    Riesgo global'+r.riesgoCifin+'
    Cuota mensual aprox.$'+(r.cuotaCifin*1000).toLocaleString('es-CO')+'
    ' +'' +'' +'' +'' +'' +'
    Legal Check / TYBA
    Resultado'+r.lcResultado+'
    Insolvencia'+(r.insolvencia?'⛔ DETECTADA':'✓ No')+'
    Cooperativas'+(r.cooperativa?'⛔ DETECTADO':'✓ No')+'
    Embargo nómina'+(r.embargo?'⛔ DETECTADO':'✓ No')+'
    Procesos judiciales'+r.procesos+'
    ' +(r.cuotas&&r.cuotas.length?'' +r.cuotas.map(function(c){return '';}).join('') +'
    Cuotas a comprar
    '+c.entidad+'$'+c.valor.toLocaleString('es-CO')+'
    ':'') +'
    Observaciones del analista:' +'
    '+r.observacion+'
    ' +'
    ' +'
    DECISIÓN FINAL
    ' +'
    '+d+'
    ' +'
    ' +'CREDITO2 SAS — Sistema de Pre-Aprobación — '+r.fecha+'
    ' +''; } // ── REPOSITORIO ────────────────────────────────────────────── var COLORES_DEC = { APROBADO: {bg:'#E8F5E9', color:'#2E7D32', txt:'Aprobado'}, APROBADO_CONDICIONES:{bg:'#FFF3E0', color:'#E65100', txt:'Cond.'}, NEGADO: {bg:'#FFEBEE', color:'#CC0000', txt:'Negado'} }; function getSolicitudes(){ try{ return JSON.parse(localStorage.getItem('c2_solicitudes')||'[]'); } catch(e){ return []; } } function renderRepositorio(){ var db = getSolicitudes(); var buscar = (document.getElementById('repo-buscar')?.value||'').toLowerCase(); var filtDec = document.getElementById('repo-decision')?.value||''; var filtAna = document.getElementById('repo-analista')?.value||''; var filtMes = document.getElementById('repo-mes')?.value||''; // Llenar filtro analistas var analistas = [...new Set(db.map(function(r){ return r.analistaUser||'—'; }))]; var selAna = document.getElementById('repo-analista'); if(selAna && selAna.options.length <= 1){ analistas.forEach(function(a){ var op = document.createElement('option'); op.value = a; op.textContent = a; selAna.appendChild(op); }); } // Llenar filtro meses var meses = [...new Set(db.map(function(r){ var p = (r.fecha||'').split('/'); return p.length===3 ? p[1]+'/'+p[2] : ''; }).filter(Boolean))]; var selMes = document.getElementById('repo-mes'); if(selMes && selMes.options.length <= 1){ meses.sort().forEach(function(m){ var op = document.createElement('option'); op.value = m; op.textContent = m; selMes.appendChild(op); }); } // Filtrar var filtrado = db.filter(function(r){ var mesR = ''; var p = (r.fecha||'').split('/'); if(p.length===3) mesR = p[1]+'/'+p[2]; return (!buscar || (r.cedula||'').includes(buscar) || (r.agencia||'').toLowerCase().includes(buscar) || (r.gestor||'').toLowerCase().includes(buscar) || (r.analistaUser||'').toLowerCase().includes(buscar)) && (!filtDec || r.decision===filtDec) && (!filtAna || r.analistaUser===filtAna) && (!filtMes || mesR===filtMes); }); // KPIs var total = filtrado.length; var aprobados = filtrado.filter(function(r){ return r.decision==='APROBADO'; }).length; var condic = filtrado.filter(function(r){ return r.decision==='APROBADO_CONDICIONES'; }).length; var negados = filtrado.filter(function(r){ return r.decision==='NEGADO'; }).length; var montoTotal= filtrado.reduce(function(s,r){ return s+(r.monto||0); }, 0); document.getElementById('repo-kpis').innerHTML = [ {ico:'📋', val:total, lbl:'Total', bg:'#1a1a2e', co:'#fff'}, {ico:'✓', val:aprobados,lbl:'Aprobados', bg:'#E8F5E9', co:'#2E7D32'}, {ico:'⚠', val:condic, lbl:'Con condiciones',bg:'#FFF3E0',co:'#E65100'}, {ico:'⛔', val:negados, lbl:'Negados', bg:'#FFEBEE', co:'#CC0000'}, {ico:'$', val:'$'+Math.round(montoTotal/1e6)+'M', lbl:'Monto total', bg:'#E3F2FD', co:'#1565C0'} ].map(function(k){ return '
    ' +'
    '+k.ico+'
    ' +'
    '+k.val+'
    ' +'
    '+k.lbl+'
    ' +'
    '; }).join(''); // Tabla principal var tbody = document.getElementById('repo-tbody'); var vacio = document.getElementById('repo-vacio'); if(!filtrado.length){ tbody.innerHTML = ''; vacio.style.display = 'block'; } else { vacio.style.display = 'none'; tbody.innerHTML = filtrado.slice().reverse().map(function(r,i){ var dec = COLORES_DEC[r.decision] || {bg:'#f5f5f5',color:'#888',txt:r.decision||'—'}; var margenColor = (r.margen||0)>0 ? '#2E7D32' : '#CC0000'; var scoreColor = (r.scoreCifin||0)>=750?'#2E7D32':(r.scoreCifin||0)>=650?'#E65100':'#CC0000'; var lcColor = r.lcResultado==='NEGADO'?'#CC0000':r.lcResultado==='LIMPIO'?'#2E7D32':'#E65100'; return '' +''+(filtrado.length-i)+'' +''+r.fecha+'' +''+r.cedula+'' +''+r.agencia+'' +''+r.regional+'' +''+r.linea+'' +'$'+(r.monto||0).toLocaleString('es-CO')+'' +'$'+Math.round(r.capacidad||0).toLocaleString('es-CO')+'' +'$'+Math.round(r.margen||0).toLocaleString('es-CO')+'' +''+(r.scoreCifin||'—')+'' +''+(r.lcResultado||'—')+'' +'' +'
    '+(r.analista||'—')+'
    ' +'
    '+(r.analistaUser||'')+'
    ' +'' +''+dec.txt+'' +'' +(r.linkPDF ?'' +'📄 PDF' :'') +'' +''; }).join(''); } // Informe por analista var porAnalista = {}; db.forEach(function(r){ var key = r.analistaUser||'desconocido'; if(!porAnalista[key]) porAnalista[key] = { nombre:r.analista||key, usuario:key, total:0, aprobados:0, condiciones:0, negados:0, ultimo:r.fecha }; var a = porAnalista[key]; a.total++; if(r.decision==='APROBADO') a.aprobados++; else if(r.decision==='APROBADO_CONDICIONES') a.condiciones++; else if(r.decision==='NEGADO') a.negados++; if(r.fecha > a.ultimo) a.ultimo = r.fecha; }); var tbAna = document.getElementById('repo-analistas-tbody'); var lista = Object.values(porAnalista).sort(function(a,b){ return b.total-a.total; }); tbAna.innerHTML = lista.length ? lista.map(function(a){ var pct = a.total>0 ? Math.round(((a.aprobados+a.condiciones)/a.total)*100) : 0; var barColor = pct>=70?'#2E7D32':pct>=50?'#E65100':'#CC0000'; return '' +''+a.nombre+'' +''+a.usuario+'' +''+a.total+'' +''+a.aprobados+'' +''+a.condiciones+'' +''+a.negados+'' +'' +'
    ' +'
    ' +'
    ' +''+pct+'%' +'
    ' +'' +''+a.ultimo+'' +''; }).join('') : 'Sin datos'; } function limpiarFiltrosRepo(){ ['repo-buscar','repo-decision','repo-analista','repo-mes'].forEach(function(id){ var el = document.getElementById(id); if(el) el.value=''; }); renderRepositorio(); } function exportarCSV(){ var db = getSolicitudes(); if(!db.length){ alert('No hay solicitudes para exportar.'); return; } var cols = ['fecha','cedula','emailCliente','agencia','regional','gestor','linea','monto','salario','descLey','descTotal','capacidad','margen','scoreCifin','riesgoCifin','lcResultado','analista','analistaUser','decision','observacion']; var SEP = String.fromCharCode(10); var csv = cols.join(',') + SEP + db.map(function(r){ return cols.map(function(col){ var v = (r[col]===undefined||r[col]===null?'':String(r[col])).replace(/,/g,' ').replace(/[\r\n]/g,' '); return '\"'+v+'\"'; }).join(','); }).join(SEP); var blob = new Blob([csv],{type:'text/csv;charset=utf-8;'}); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'CREDITO2_solicitudes_'+new Date().toLocaleDateString('es-CO').replace(/\//g,'-')+'.csv'; a.click(); URL.revokeObjectURL(url); } // ── INFORMES ───────────────────────────────────────────────── var periodoInforme = 'mes'; var chartMensual = null, chartLineas = null, chartAnalistas = null; function setPeriodo(p){ periodoInforme = p; ['dia','sem','mes2','todo'].forEach(function(id){ var el = document.getElementById('btn-'+id); if(el){ el.className = 'btn btn-gris'; el.style.fontSize='12px'; } }); var activo = document.getElementById('btn-'+p); if(activo){ activo.className = 'btn btn-rojo'; } renderInformes(); } function filtrarPorPeriodo(db){ var hoy = new Date(); var hoyStr= hoy.toLocaleDateString('es-CO'); return db.filter(function(r){ if(periodoInforme==='todo') return true; var f = (r.fecha||'').split('/'); if(f.length!==3) return false; var rd = new Date(parseInt(f[2]), parseInt(f[1])-1, parseInt(f[0])); if(periodoInforme==='dia'){ return r.fecha===hoyStr; } else if(periodoInforme==='sem'){ var lunes = new Date(hoy); lunes.setDate(hoy.getDate()-hoy.getDay()+1); return rd >= lunes; } else if(periodoInforme==='mes'){ return rd.getMonth()===hoy.getMonth() && rd.getFullYear()===hoy.getFullYear(); } return true; }); } function renderInformes(){ var dbFull = getSolicitudes(); var filMes = document.getElementById('inf-fil-mes')?.value||''; var filZona = document.getElementById('inf-fil-zona')?.value||''; // Llenar filtros var meses = [...new Set(dbFull.map(function(r){ var p=(r.fecha||'').split('/'); return p.length===3?p[1]+'/'+p[2]:''; }).filter(Boolean))].sort(); var selM = document.getElementById('inf-fil-mes'); if(selM && selM.options.length<=1) meses.forEach(function(m){ var o=document.createElement('option'); o.value=m; o.textContent=m; selM.appendChild(o); }); var zonas = [...new Set(dbFull.map(function(r){ return r.regional||''; }).filter(Boolean))].sort(); var selZ = document.getElementById('inf-fil-zona'); if(selZ && selZ.options.length<=1) zonas.forEach(function(z){ var o=document.createElement('option'); o.value=z; o.textContent=z; selZ.appendChild(o); }); // Aplicar filtros globales var db = dbFull.filter(function(r){ var mesR=''; var p=(r.fecha||'').split('/'); if(p.length===3) mesR=p[1]+'/'+p[2]; return (!filMes||mesR===filMes) && (!filZona||(r.regional||'')===filZona); }); document.getElementById('inf-total-registros').textContent = db.length + ' registros' + (filMes||filZona?' (filtrado)':''); if(!db.length){ document.getElementById('inf-sin-datos').style.display='block'; return; } document.getElementById('inf-sin-datos').style.display='none'; var aprobados = db.filter(function(r){return r.decision==='APROBADO';}); var condic = db.filter(function(r){return r.decision==='APROBADO_CONDICIONES';}); var negados = db.filter(function(r){return r.decision==='NEGADO';}); var montoAprob= aprobados.reduce(function(s,r){return s+(r.monto||0);},0); var montoTotal= db.reduce(function(s,r){return s+(r.monto||0);},0); var pctAprob = db.length?Math.round(((aprobados.length+condic.length)/db.length)*100):0; // ── KPIs ── document.getElementById('inf-kpis').innerHTML = [ {ico:'📋',val:db.length, lbl:'Total solicitudes', bg:'#1a1a2e',co:'#fff'}, {ico:'✓', val:aprobados.length, lbl:'Aprobados', bg:'#E8F5E9',co:'#2E7D32'}, {ico:'⚠', val:condic.length, lbl:'Con condiciones', bg:'#FFF3E0',co:'#E65100'}, {ico:'⛔', val:negados.length, lbl:'Negados', bg:'#FFEBEE',co:'#CC0000'}, {ico:'$', val:'$'+Math.round(montoAprob/1e6)+'M', lbl:'Monto aprobado', bg:'#E8F5E9',co:'#2E7D32'}, {ico:'%', val:pctAprob+'%', lbl:'Tasa aprobación', bg: pctAprob>=70?'#E8F5E9':pctAprob>=50?'#FFF3E0':'#FFEBEE', co: pctAprob>=70?'#2E7D32':pctAprob>=50?'#E65100':'#CC0000'} ].map(function(k){ return '
    ' +'
    '+k.ico+'
    ' +'
    '+k.val+'
    ' +'
    '+k.lbl+'
    ' +'
    '; }).join(''); // ── COLOCACIÓN POR ZONA ── var porZona = {}; db.forEach(function(r){ var z = r.regional||'Sin zona'; if(!porZona[z]) porZona[z]={zona:z,aprob:0,cond:0,neg:0,monto:0,total:0}; porZona[z].total++; porZona[z].monto += (r.monto||0); if(r.decision==='APROBADO') porZona[z].aprob++; else if(r.decision==='APROBADO_CONDICIONES') porZona[z].cond++; else if(r.decision==='NEGADO') porZona[z].neg++; }); var zonasList = Object.values(porZona).sort(function(a,b){return b.monto-a.monto;}); var maxMontoZ = zonasList.length ? zonasList[0].monto : 1; document.getElementById('inf-tbody-zona').innerHTML = zonasList.map(function(z){ var pct = Math.round(z.monto/maxMontoZ*100); return '' +''+z.zona +'
    ' +'
    ' +'' +''+z.aprob+'' +''+z.cond+'' +''+z.neg+'' +'$'+Math.round(z.monto/1e6*10)/10+'M' +''+z.total+'' +''; }).join(''); // ── TENDENCIA MENSUAL ── var porMes = {}; db.forEach(function(r){ var p=(r.fecha||'').split('/'); if(p.length!==3) return; var key = p[1].padStart(2,'0')+'/'+p[2]; if(!porMes[key]) porMes[key]={aprob:0,cond:0,neg:0,monto:0}; porMes[key].monto += (r.monto||0); if(r.decision==='APROBADO') porMes[key].aprob++; else if(r.decision==='APROBADO_CONDICIONES') porMes[key].cond++; else if(r.decision==='NEGADO') porMes[key].neg++; }); var mesesKeys = Object.keys(porMes).sort(); var ctxM = document.getElementById('chart-mensual')?.getContext('2d'); if(ctxM){ if(chartMensual) chartMensual.destroy(); chartMensual = new Chart(ctxM,{ type:'bar', data:{ labels: mesesKeys, datasets:[ {label:'Aprobados', data:mesesKeys.map(function(k){return porMes[k].aprob;}), backgroundColor:'#2E7D32',borderRadius:4}, {label:'Con cond.', data:mesesKeys.map(function(k){return porMes[k].cond;}), backgroundColor:'#E65100',borderRadius:4}, {label:'Negados', data:mesesKeys.map(function(k){return porMes[k].neg;}), backgroundColor:'#CC0000',borderRadius:4} ] }, options:{responsive:true,plugins:{legend:{position:'top'}}, scales:{x:{stacked:true},y:{stacked:true,beginAtZero:true,ticks:{precision:0}}}} }); } // ── RANKING AGENCIAS ── var porAgencia = {}; db.forEach(function(r){ var a = r.agencia||'Sin agencia'; if(!porAgencia[a]) porAgencia[a]={agencia:a,zona:r.regional||'',total:0,aprob:0,monto:0}; porAgencia[a].total++; porAgencia[a].monto += (r.monto||0); if(r.decision==='APROBADO'||r.decision==='APROBADO_CONDICIONES') porAgencia[a].aprob++; }); var agList = Object.values(porAgencia).sort(function(a,b){return b.monto-a.monto;}); var maxAg = agList.length ? agList[0].monto : 1; document.getElementById('inf-ranking-agencias').innerHTML = agList.slice(0,12).map(function(a,i){ var pct = Math.round(a.monto/maxAg*100); var medal = i===0?'🏋':i===1?'🥈':i===2?'🥉':''; return '
    ' +'
    ' +''+medal+' '+(i+1)+'. ' +''+a.agencia.substring(0,28)+(a.agencia.length>28?'…':'')+'' +' ('+a.zona+')' +'$'+Math.round(a.monto/1e6*10)/10+'M • '+a.total+' créd.' +'
    ' +'
    ' +'
    ' +'
    '; }).join(''); // ── LÍNEAS DE CRÉDITO ── var porLinea = {}; db.forEach(function(r){ var l=r.linea||'Otra'; if(!porLinea[l]) porLinea[l]={linea:l,total:0,monto:0,aprob:0}; porLinea[l].total++; porLinea[l].monto+=(r.monto||0); if(r.decision==='APROBADO'||r.decision==='APROBADO_CONDICIONES') porLinea[l].aprob++; }); var lineaList = Object.values(porLinea).sort(function(a,b){return b.total-a.total;}); var COLORES_CHART = ['#CC0000','#1565C0','#2E7D32','#E65100','#4A148C','#00695C','#F57F17']; var ctxL = document.getElementById('chart-lineas')?.getContext('2d'); if(ctxL){ if(chartLineas) chartLineas.destroy(); chartLineas = new Chart(ctxL,{ type:'doughnut', data:{ labels: lineaList.map(function(l){return l.linea;}), datasets:[{ data: lineaList.map(function(l){return l.total;}), backgroundColor: COLORES_CHART.slice(0,lineaList.length), borderWidth:2, borderColor:'#fff' }] }, options:{responsive:true,plugins:{legend:{position:'right',labels:{font:{size:11}}}}} }); } document.getElementById('inf-tabla-lineas').innerHTML = '' +'' +'' +'' +''+lineaList.map(function(l){ return '' +'' +''; }).join('')+'
    LíneaSolicitudesMonto
    '+l.linea+''+l.total+'$'+Math.round(l.monto/1e6*10)/10+'M
    '; // ── GESTIÓN ANALISTAS ── var dbPer = filtrarPorPeriodo(db); var periodos= dbPer; var diasPer = periodoInforme==='dia'?1:periodoInforme==='sem'?7:periodoInforme==='mes'?30:365; var porAna = {}; periodos.forEach(function(r){ var k=r.analistaUser||'—'; if(!porAna[k]) porAna[k]={nombre:r.analista||k,usuario:k,total:0,aprob:0,cond:0,neg:0,monto:0}; porAna[k].total++; porAna[k].monto+=(r.monto||0); if(r.decision==='APROBADO') porAna[k].aprob++; else if(r.decision==='APROBADO_CONDICIONES') porAna[k].cond++; else if(r.decision==='NEGADO') porAna[k].neg++; }); var anaList = Object.values(porAna).sort(function(a,b){return b.total-a.total;}); document.getElementById('inf-tbody-analistas').innerHTML = anaList.length ? anaList.map(function(a){ var pctA = a.total?Math.round(((a.aprob+a.cond)/a.total)*100):0; var pDay = (a.total/diasPer).toFixed(1); var rend = pctA>=70?{txt:'ALTO',bg:'#E8F5E9',co:'#2E7D32'} : pctA>=50?{txt:'MEDIO',bg:'#FFF3E0',co:'#E65100'} : {txt:'BAJO', bg:'#FFEBEE',co:'#CC0000'}; return '' +''+a.nombre+'' +''+a.usuario+'' +''+a.total+'' +''+a.aprob+'' +''+a.cond+'' +''+a.neg+'' +'$'+Math.round(a.monto/1e6*10)/10+'M' +''+pDay+'' +'' +'
    ' +'
    ' +'
    ' +''+pctA+'%' +'
    ' +'' +''+rend.txt+'' +''; }).join('') : 'Sin datos para el período seleccionado'; // Gráfica analistas — barras horizontales var ctxA = document.getElementById('chart-analistas')?.getContext('2d'); if(ctxA && anaList.length){ if(chartAnalistas) chartAnalistas.destroy(); chartAnalistas = new Chart(ctxA,{ type:'bar', data:{ labels: anaList.map(function(a){return a.nombre;}), datasets:[ {label:'Aprobados', data:anaList.map(function(a){return a.aprob;}), backgroundColor:'#2E7D32',borderRadius:4}, {label:'Con cond.', data:anaList.map(function(a){return a.cond;}), backgroundColor:'#E65100',borderRadius:4}, {label:'Negados', data:anaList.map(function(a){return a.neg;}), backgroundColor:'#CC0000',borderRadius:4} ] }, options:{ indexAxis:'y', responsive:true, plugins:{legend:{position:'top'}}, scales:{x:{stacked:true,ticks:{precision:0}},y:{stacked:true}} } }); } } // ── INIT ───────────────────────────────────────────────────── document.addEventListener('DOMContentLoaded', function(){ // Configurar Google si no está configurado aún var cfg = getCfg(); if(!cfg.sheetsWebhook){ cfg.sheetsWebhook = 'https://script.google.com/macros/s/AKfycbynHNzbam3tDxB7PQ6fqiGt0v6UMcm9ydv6nK0ffE9b2vpxMyQqmBvbqqlteCP8J2xB/exec'; cfg.sheetId = '1pWSr2knqcYwGRu3dP17IU3GuQGmEnh2p9WowI9MNHjE'; cfg.driveFolder = '12tqVSUOEbmlpZf_VMWSvd-7xdyAHAHAI'; saveCfg(cfg); } llenarDeptosVer(); llenarSelectsAdmin(); llenarDeptoSelect('ag-depto', llenarCiudadesAgencia); llenarAgenciasSelect(); cargarConfigGoogle(); });