Herramientas Informaticas

Mes: junio 2026

🚀 Implementa tu propio sistema de autorización de comprobantes SAP con CodeIgniter 4 y ODBC (Guía práctica + beneficios)

Entrada fija

📝 Por: julio 101290
Especialista en integraciones SAP – PHP – CodeIgniter

¿Cansado de los atascos en la aprobación de gastos, viáticos y facturas? ¿Los comprobantes se pierden en correos electrónicos o en papeles que nunca llegan? En este artículo te muestro cómo construí un módulo de autorización de comprobantes conectado directamente a SAP HANA usando CodeIgniter 4, ODBC y tecnologías web modernas. Además, te cuento los beneficios reales que obtendrás.


📌 Índice

  1. El problema de siempre: autorización manual
  2. La solución técnica: arquitectura limpia y directa
  3. Beneficios clave (✅ productividad, ✅ seguridad, ✅ movilidad)
  4. Código y componentes principales
  5. Lecciones aprendidas (y errores que evitar)
  6. Mejoras futuras y recomendaciones
  7. Conclusión: ¿por qué deberías implementarlo ya?

1. El problema de siempre: autorización manual 📄❌

Imaginemos el día a día:

  • Un empleado genera un comprobante de gasto en SAP (ej. CV__300000366).
  • El comprobante queda con estado U_Status = 2 (pendiente de autorización).
  • El autorizador recibe un correo, imprime, firma, escanea… o peor, usa un Excel compartido.
  • Contabilidad tarda días en enterarse de la aprobación.
  • No hay trazabilidad: ¿quién aprobó? ¿cuándo? ¿por qué?

🔴 Resultado: retrasos, errores, falta de control y equipos frustrados.


2. La solución técnica: arquitectura limpia y directa 🏗️

Decidí construir una interfaz web que se conecte directamente a las tablas de usuario de SAP (@QSYS_GLO_VOUC y @QSYS_GLO_VODE) usando ODBC. No necesitamos API intermedias ni modificar el núcleo de SAP.

🧱 Stack tecnológico elegido

Componente¿Por qué?
CodeIgniter 4Ligero, rápido, con excelente soporte para ODBC y JSON
ODBCConexión nativa a SAP HANA; sin controladores adicionales complicados
DataTablesTablas dinámicas con búsqueda, ordenamiento y paginación server-side
SweetAlert2Diálogos modernos para confirmar autorizaciones
Bootstrap 4Diseño responsivo (funciona en móvil, tableta y escritorio)
jQuery + AJAXComunicación asíncrona sin recargar la página

🔄 Flujo de trabajo

  1. El autorizador ingresa a la URL /admin/refundsauth.
  2. Se carga una DataTable con todos los comprobantes pendientes (U_Status = 2).
  3. Por cada fila, botones: Autorizar y Detalle.
  4. Al hacer clic en Detalle, se abre un modal responsivo que muestra las líneas del comprobante (tabla VODE).
  5. Al hacer clic en Autorizar, se confirma con SweetAlert y se envía una petición AJAX que actualiza U_Status = 4 directamente en SAP.
  6. La fila desaparece de la tabla y queda registrado en una bitácora local.

Todo en tiempo real, sin papeles, sin correos, sin pérdidas.


3. Beneficios clave (✅ productividad, ✅ seguridad, ✅ movilidad)

🚀 Velocidad y productividad

  • De días a segundos: la autorización se hace con un clic.
  • El autorizador ve solo lo que le corresponde (filtros, búsqueda, orden).
  • Autorización masiva (se puede implementar fácilmente).

🔒 Trazabilidad y auditoría

  • Cada autorización queda registrada en la tabla log con usuario, fecha y detalles.
  • SAP conserva el histórico de U_Status (si se audita, se sabe cuándo cambió a 4).
  • Evita fraudes o aprobaciones sin conocimiento.

📱 Acceso móvil y responsivo

  • El diseño con Bootstrap y DataTables se adapta a cualquier dispositivo.
  • El modal de detalle tiene scroll horizontal (ideal para ver muchas columnas).
  • Los gerentes pueden aprobar gastos desde el móvil mientras viajan.

🧩 Integración sin fricción con SAP

  • No se necesita licencia adicional de Service Layer.
  • Las tablas @... son de usuario → se pueden leer/escribir con ODBC con permisos estándar.
  • La actualización es inmediata y consistente.

🛡️ Seguridad basada en roles

  • Cada usuario del sistema se vincula con un sapuser autorizador (tabla user_sap_link).
  • Solo los usuarios con el permiso adecuado pueden ver y autorizar.

💰 Bajo costo y rápido desarrollo

  • Solución implementada en menos de una semana.
  • Código 100% reutilizable y extensible.
  • No requiere comprar add‑ons caros de terceros.

4. Código y componentes principales (fragmentos clave) 🧩

Aquí te muestro las partes más importantes del controlador RefundsAuthController.php.

📡 Conexión ODBC y consulta de pendientes

$conn = odbc_connect($dataConect['nameODBC'], $dataConect['userODBC'], $dataConect['passwordODBC']);
$tableName = '"' . $dataConect['companyDB'] . '"."@QSYS_GLO_VOUC"';
$sql = "SELECT * FROM $tableName WHERE \"U_Status\" = 2 ORDER BY \"U_Date\" DESC";
$result = odbc_exec($conn, $sql);

✏️ Autorización (update directo)

$sqlUpdate = "UPDATE $tableName SET \"U_Status\" = 4 WHERE \"Code\" = $code";
odbc_exec($conn, $sqlUpdate);

📋 Detalle responsivo (modal con scroll)

$.ajax({
    url: base_url + '/admin/servicelayer/refundsauth/showVoucherDetails',
    data: JSON.stringify({ code: code }),
    success: function(resp) {
        var html = '<div class="table-responsive">' +
                   '<table class="table table-sm table-bordered">' +
                   // ... cabeceras y filas ...
                   '</table></div>';
        $('#modalVoucherDetailsBody').html(html);
    }
});

💡 El código completo lo puedes encontrar en mi repositorio de GitHub (link al final).


5. Lecciones aprendidas (y errores que evitar) 🧠

❌ Error 1: Nombres de tablas con @ sin comillas dobles

Solución: Siempre usar "DBNAME"."@TABLA". HANA es sensible a mayúsculas/minúsculas.

❌ Error 2: Usar LIMIT en ODBC sin ROW_NUMBER()

Solución: Emplear la función analítica ROW_NUMBER() OVER (ORDER BY ...) y filtrar por rn.

❌ Error 3: El modal se quedaba en “Cargando…”

Causa: Los IDs del contenedor en el modal no coincidían con los del JavaScript.
Solución: Sincronizar id="modalVoucherDetailsBody" en el HTML y en el JS.

❌ Error 4: La tabla no hacía scroll en móvil

Solución: Envolver la tabla en <div class="table-responsive"> y dar a la tabla un min-width: 700px mediante CSS.

❌ Error 5: Caracteres extraños en JSON

Solución: Aplicar utf8ize() recursivo a cada fila antes de enviarla.


6. Mejoras futuras y recomendaciones 🧪

Si quieres llevar el sistema al siguiente nivel, considera:

  • Autorización por lotes (seleccionar varios y aprobar con un botón).
  • Notificaciones por correo o WhatsApp cuando llegue un nuevo comprobante.
  • Rechazo con motivo y notificación al empleado.
  • Dashboard de indicadores (tiempo promedio de autorización, montos aprobados, etc.).
  • Firma digital para cumplir normativas.
  • Integración con SAP Business Workflow (si la empresa lo requiere).

7. Conclusión: ¿por qué deberías implementarlo ya? 🎯

La combinación CodeIgniter 4 + ODBC + DataTables permite construir en pocos días un sistema de autorización de comprobantes que:

  • Reduce drásticamente los tiempos de espera.
  • Elimina el papeleo y los correos perdidos.
  • Brinda movilidad a los aprobadores.
  • Aporta transparencia y auditoría.
  • Se integra de forma nativa con SAP sin costes adicionales.

¿Tu empresa sigue aprobando gastos con firmas manuales y hojas de cálculo?
Es hora de dar el salto a la automatización web. Yo ya lo hice para varios clientes, y los resultados han sido increíbles: +80% de agilidad, 0 errores de registro y aprobaciones desde el móvil.


🔗 Recursos adicionales


📢 Comparte este artículo si te ha resultado útil
♻️ ¿Conoces a alguien que todavía autoriza comprobantes en papel? Etiquétalo en los comentarios.

#SAP #CodeIgniter #ODBC #AutorizaciónDeGastos #PHP #DesarrolloWeb #TransformaciónDigital

Tutorial: Instalación del High‑Performance Backend para Nextcloud Talk

Entrada fija

¡Excelente! A continuación encontrarás un tutorial completo desde cero para instalar y configurar el High‑Performance Backend (HPB) de Nextcloud Talk en un servidor Ubuntu, basado en la solución que aplicamos paso a paso.

Requisitos previos

  • Un servidor con Ubuntu 22.04 o 24.04 (mínimo 2 GB RAM, 2 vCPU).
  • Nextcloud ya instalado y funcionando (puede estar en el mismo servidor o en otro).
  • La aplicación Talk habilitada en Nextcloud.
  • Un nombre de dominio (por ejemplo, tudominio.com) apuntando al servidor.
    Necesitarás dos subdominios (o uno con puertos distintos):
    • nextcloud.tudominio.com → donde está Nextcloud (si no usas el mismo servidor, omítelo).
    • signal.tudominio.com → para el backend de alto rendimiento.
  • Puertos abiertos en el firewall del servidor:
    • 80/tcp y 443/tcp (web + WebSocket).
    • 3478/udp y 3478/tcp (STUN/TURN sin TLS).
    • 5349/udp y 5349/tcp (STUN/TURN con TLS).
  • Una cuenta de correo para recibir avisos de Certbot (Let’s Encrypt).

Nota: Este tutorial asume que Nextcloud está en el mismo servidor. Si está en otro, ajusta las IPs y dominios.

1. Instalación del script HPB

El script oficial de sunweaver automatiza la instalación de coturn, janus, nats y el servidor de señalización.

# Actualizar sistema e instalar dependencias básicas
sudo apt update && sudo apt upgrade -y
sudo apt install -y wget git curl

# Descargar la última versión del script (ejemplo con la 1.3.5)
cd ~
wget https://github.com/sunweaver/nextcloud-high-performance-backend-setup/archive/refs/tags/1.3.5.tar.gz
tar xzf 1.3.5.tar.gz
cd nextcloud-high-performance-backend-setup-1.3.5

# Ejecutar el instalador (responde a las preguntas)
sudo ./install.sh

Durante la instalación te pedirá:

  • Nextcloud base URL: https://nextcloud.tudominio.com (o la IP/puerto de tu Nextcloud). Si Nextcloud usa un puerto no estándar (ej. :444), indícalo.
  • Signaling domain: signal.tudominio.com (el subdominio para el HPB).
  • Correo para certificados SSL: tu email.
  • Secreto TURN: se generará automáticamente (guárdalo).
  • Secreto del backend: también se generará (guárdalo).

Al final verás un resumen similar a este (los valores cambiarán):

STUN server = signal.tudominio.com:5349
TURN server:
 - turn and turns
 - turnserver+port: signal.tudominio.com:5349
 - secret: a33e1511842947b2f2c514fba9e01dced4ed6d274bd8424d8c6bc8bcb00f60bb
 - udp & tcp
High-performance backend:
 - https://signal.tudominio.com/standalone-signaling
 - signal.tudominio.com -> 437929b3e7012e0d4a9e7a4564945d3a

Guarda estos datos, los necesitarás en Nextcloud.

2. Solución de problemas post‑instalación

El script a veces deja servicios sin arrancar o con configuraciones incorrectas. Aplica los siguientes arreglos si ves errores (como coturn o janus caídos, WebSocket fallando, invalid_backend, etc.).

2.1 Arreglar coturn (servidor TURN)

Edita /etc/turnserver.conf:

sudo nano /etc/turnserver.conf

Asegura estas líneas:

listening-ip=0.0.0.0
listening-port=3478
tls-listening-port=5349

# Comenta o elimina cualquier línea que ponga external-ip o relay-ip
# (a menos que estés detrás de NAT, en cuyo caso consulta la nota al final)
# external-ip=
# relay-ip=

Si tu servidor tiene IP pública directa, no necesitas external-ip. Si está detrás de NAT (IP privada), añade:

relay-ip=192.168.x.x   # IP privada del servidor
# external-ip = (no poner, el router hará NAT)

Reinicia coturn:

sudo systemctl restart coturn
sudo systemctl enable coturn

2.2 Arreglar el servidor de señalización (nextcloud‑spreed‑signaling)

Edita /etc/nextcloud-spreed-signaling/server.conf:

sudo nano /etc/nextcloud-spreed-signaling/server.conf

Corrige la sección [backend] (debe coincidir con la URL de tu Nextcloud, incluyendo puerto si es necesario):

[backend]

allowed = https://nextcloud.tudominio.com:444 # o sin puerto si es 443 secret = 437929b3e7012e0d4a9e7a4564945d3a # el secreto que te dio el script

Añade o corrige la sección [turn] (asegúrate de usar el puerto 5349, no 9991):

[turn]

secret = a33e1511842947b2f2c514fba9e01dced4ed6d274bd8424d8c6bc8bcb00f60bb # el secreto TURN servers = turn:signal.tudominio.com:5349?transport=udp, turn:signal.tudominio.com:5349?transport=tcp

Si existe una sección [nextcloud-backend-0], asegura que tenga:

[nextcloud-backend-0]

url = https://nextcloud.tudominio.com:444 secret = 437929b3e7012e0d4a9e7a4564945d3a

Reinicia el servicio:

sudo systemctl restart nextcloud-spreed-signaling

2.3 Configurar el proxy inverso (Nginx)

El script ya debería haber creado un archivo para signal.tudominio.com. Verifica que incluya las cabeceras WebSocket:

sudo nginx -T | grep -A20 "server_name signal.tudominio.com"

Debe aparecer algo como:

location /standalone-signaling/ {
    proxy_pass http://127.0.0.1:8080/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    # ... otras cabeceras
}

Si falta, edita el archivo correspondiente (por ejemplo, /etc/nginx/sites-available/signal.tudominio.com) y añade esas líneas. Luego recarga Nginx:

sudo nginx -t && sudo systemctl reload nginx

2.4 Firewall

Abre los puertos necesarios:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 3478/udp
sudo ufw allow 3478/tcp
sudo ufw allow 5349/udp
sudo ufw allow 5349/tcp

2.5 Reiniciar todos los servicios en orden

sudo systemctl restart coturn
sudo systemctl restart janus
sudo systemctl restart nats-server
sudo systemctl restart nextcloud-spreed-signaling

Comprueba que todos estén activos:

sudo systemctl status coturn janus nats-server nextcloud-spreed-signaling

3. Configuración en Nextcloud (interfaz web)

Accede a tu Nextcloud con una cuenta de administrador.

  1. Ve a Ajustes → Administración → Talk.
  2. En la sección High‑performance backend:
    • URL: https://signal.tudominio.com/standalone-signaling
    • Secreto compartido: 437929b3e7012e0d4a9e7a4564945d3a (el que generó el script).
  3. En STUN & TURN servers:
    • STUN: signal.tudominio.com:5349
    • TURN (dos líneas):
      • turn:signal.tudominio.com:5349?transport=udp
      • turn:signal.tudominio.com:5349?transport=tcp
    • Secreto TURN: a33e1511842947b2f2c514fba9e01dced4ed6d274bd8424d8c6bc8bcb00f60bb (el otro secreto).
  4. Guarda los cambios.

4. Prueba de funcionamiento

4.1 Prueba básica del signaling server

curl -k https://signal.tudominio.com/standalone-signaling/api/v1/welcome

Debe responder un JSON con "nextcloud-spreed-signaling":"Welcome".

4.2 Prueba del WebSocket con wscat

sudo apt install -y node-ws
wscat -c wss://signal.tudominio.com/standalone-signaling/spreed

Si ves Connected, presiona Ctrl+C.

4.3 Prueba en el navegador

  • Abre la consola de desarrollador (F12) en tu Nextcloud.
  • Ejecuta: window.OCA.Talk.SignalingStandaloneTest?.testConnection()
  • Deberías ver mensajes de éxito (no invalid_backend).
  • Inicia una llamada entre dos usuarios diferentes. El audio y vídeo deben fluir.

5. Solución de errores comunes

invalid_backend en la consola del navegador
Causa: la URL del backend en el signaling no coincide con la de Nextcloud.
Solución: edita /etc/nextcloud-spreed-signaling/server.conf y asegura que allowed y url tengan exactamente la URL que usas para acceder a Nextcloud (incluyendo https:// y el puerto si no es 443). Luego reinicia el signaling.

❌ WebSocket no conecta (Error en la consola: WebSocket connection failed)
Causa: Nginx no envía las cabeceras Upgrade.
Solución: agrega las líneas proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; en el bloque location /standalone-signaling/ de Nginx.

❌ Coturn no arranca o se cae (Cannot bind)
Causa: el servidor intenta escuchar en una IP que ya no existe o está mal configurada.
Solución: pon listening-ip=0.0.0.0 y comenta cualquier external-ip o relay-ip a menos que estés detrás de NAT.

❌ La llamada conecta pero no hay audio/vídeo (candidatos ICE vacíos)
Causa: el TURN no está accesible o el signaling anuncia un puerto incorrecto.
Solución: 1) Verifica que turnserver.conf tenga tls-listening-port=5349. 2) En server.conf del signaling, asegura que los servidores TURN usen el puerto 5349. 3) Abre los puertos en el firewall (UDP y TCP). 4) Si estás detrás de NAT, configura relay-ip y redirección de puertos en el router.

❌ Un usuario no administrador no puede unirse a la sala (user not invited)
Causa: la conversación no es pública o el usuario no fue invitado.
Solución: crea una nueva conversación, invita explícitamente al usuario, o convierte la sala en pública (enlace compartido). Asegúrate de que el usuario tenga habilitada la app Talk.

6. Nota final sobre IPs dinámicas y NAT

Si tu servidor tiene IP pública dinámica (cambia periódicamente) y usas DDNS (ej. dyndns.org):

  • No pongas external-ip en /etc/turnserver.conf. Deja que coturn use 0.0.0.0.
  • En el router, redirige los puertos 3478/5349 (UDP/TCP) a la IP privada del servidor.
  • Usa el nombre DDNS en todas las configuraciones (signal.tudominio.com).
  • El signaling server ya usará el nombre, y el navegador resolverá la IP actual.

✅ ¡Listo!

Ahora tienes un backend de alto rendimiento para Talk funcionando con WebSocket y TURN correctamente. Disfruta de llamadas de vídeo escalables y con baja latencia.

Si encuentras algún problema no cubierto aquí, revisa los logs:

sudo journalctl -u coturn -f
sudo journalctl -u janus -f
sudo journalctl -u nextcloud-spreed-signaling -f
sudo tail -f /var/log/nginx/error.log

Creado con WordPress & Tema de Anders Norén