Herramientas Informaticas

Mes: enero 2026

💎 Implementación de Respaldos en jcposUltimate: Ingeniería MVC con PHP 8.3

Entrada fija

💎 Implementación de Respaldos en jcposUltimate: Ingeniería MVC con PHP 8.3

Análisis exhaustivo del commit 3f4d283: Cómo construir un módulo de copias de seguridad robusto, seguro y nativo.


En el desarrollo de jcposUltimate, la eficiencia no es negociable. El reciente commit 3f4d283 introduce el módulo de Respaldos. A diferencia de las implementaciones tradicionales, aquí se ha optado por una arquitectura LGAC (Legacy Green Agile Code), utilizando 135 líneas de código puro en PHP 8.3 para manejar el ciclo completo de un backup SQL.

🏗️ El Triángulo MVC en 135 Líneas

Este commit no solo añade una función, añade una estructura. A pesar de ser un solo bloque lógico, el código se divide en tres capas fundamentales:

  • Modelo (Data Layer): Implementa la lógica de introspección de la base de datos, recorriendo tablas y generando sentencias INSERT y CREATE TABLE dinámicas.
  • Controlador (Logic Layer): Gestiona el flujo de ejecución, valida los tokens editarUuid y asegura que los recursos del servidor se utilicen de forma óptima.
  • Vista (Output Layer): No genera HTML, sino un Stream de Datos Binary-Safe hacia el navegador mediante headers HTTP especializados.

🛠️ Análisis Técnico: Los 10 Pilares de la Implementación

1. Resolución de Rutas de Nivel 2

Para garantizar que los respaldos se guarden fuera de la carpeta de controladores, se utiliza dirname(__DIR__). Esto permite que el sistema localice la carpeta /backups/ en la raíz del proyecto de forma dinámica y absoluta.

2. Gestión de Entradas con Operador de Fusión

PHP 8.3 brilla en la captura de datos: $uuid = $_POST["editarUuid"] ?? '';. Se eliminan los errores de “Undefined Index”, asegurando un flujo de ejecución limpio desde la primera línea.

3. Blindaje contra Path Traversal

La seguridad es prioritaria. El uso de basename() en el input del usuario actúa como un cortafuegos, impidiendo que caracteres maliciosos como ../ puedan escalar directorios en el servidor.

4. Motor de Generación SQL Nativo

El código recorre la estructura de la base de datos mediante ciclos optimizados. No se limita a copiar datos; construye el esquema necesario para que el archivo resultante sea 100% ejecutable en cualquier cliente SQL.

5. Inyección Dinámica de Nombres de Archivo

Utilizando interpolación de variables moderna, el sistema asigna nombres únicos basados en el UUID procesado, evitando colisiones de archivos en entornos multiusuario.

6. Control de Headers de Transferencia Binaria

Se implementan encabezados nativos para forzar la descarga: header("Content-Type: application/sql"); y header("Content-Disposition: attachment; ...");. Esto le indica al navegador que no debe intentar mostrar el código, sino guardarlo físicamente.

7. Optimización de Memoria (Output Buffering)

Al manejar archivos potencialmente grandes, el código asegura que el buffer de salida esté limpio antes de enviar el archivo, evitando que caracteres extra corrompan el script SQL.

8. Compatibilidad con Caché Inversa

Se incluyen directivas Pragma: no-cache y Expires: 0. Esto es vital en sistemas de gestión para asegurar que cada backup descargado sea la versión más reciente y no una copia temporal del servidor o el proxy.

9. Cierre de Proceso Determinista

El uso de exit; al finalizar el stream de datos es una práctica de ingeniería robusta que evita que cualquier lógica posterior del servidor se filtre en el archivo descargado.

10. Estándar de Codificación PHP 8.3

Todo el módulo respeta las nuevas convenciones de tipado y manejo de errores, lo que reduce la deuda técnica y facilita futuras auditorías de código.

📈 Impacto en el Rendimiento

Al evitar el uso de una librería de terceros o un plugin pesado, jcposUltimate logra:

MétricaResultado
Velocidad de GeneraciónInstantánea (Nativo)
Consumo de RAMBajo impacto (Lineal)
SeguridadGrado Empresarial

💡 Conclusión

La implementación del módulo de respaldos en el commit 3f4d283 es un ejemplo de cómo la simplicidad bien ejecutada supera a la complejidad innecesaria. En 135 líneas, jcposUltimate ahora cuenta con una herramienta de recuperación ante desastres potente, segura y extremadamente rápida.

¿Eres desarrollador? Explora el código completo en nuestro repositorio y únete a la evolución de los sistemas de gestión nativos.

Tags: #PHP83 #MVC #jcposUltimate #Backend #CyberSecurity #WebDev #OpenSource #SoftwareArchitecture #LGAC

🚀 Cómo Crear tu Propia IA Offline: ¡Lleva a “ChatGPT” en un USB sin Internet! (Guía 2026) 🔐

Entrada fija

🚀 Cómo Crear tu Propia IA Offline: ¡Lleva a “ChatGPT” en un USB sin Internet! (Guía 2026) 🔐

¿Alguna vez has sentido que dependes demasiado de la nube? ☁️ ¿Te preocupa que tus datos terminen entrenando a modelos de Big Tech? En este artículo, vamos a romper las reglas. Te voy a enseñar a fabricar una ISO Soberana de Linux Mint que contiene el poder de Llama 3.1 y DeepSeek R1.

El resultado: Un sistema operativo en un pendrive que funciona como un “Cerebro Digital” 🧠 totalmente desconectado del mundo.


🧐 ¿Por qué tener una IA Local es el máximo nivel de Privacidad?

Tener una IA en modo local no es solo para “geeks”; es una necesidad en la era de la vigilancia digital:

  • Privacidad Blindada 🛡️: Lo que hablas con la IA se queda en tus circuitos. Nada sale a internet.
  • Independencia Total 🌍: Úsala en un avión, en una montaña o durante un corte de internet. Tu productividad no se detiene.
  • Cero Suscripciones 💸: Olvida los 20 USD mensuales. El único costo es la electricidad de tu PC.
  • Sin Censura 🔓: Los modelos locales no tienen los filtros “políticamente correctos” que limitan las respuestas en la nube.

🛠️ La Receta Tecnológica

Para este proyecto usaremos ingredientes de primera calidad:

  1. Linux Mint XFCE: El chasis más ligero y estable 🏎️.
  2. Ollama: El motor que hace que correr modelos pesados sea pan comido.
  3. Llama 3.1 (8B): El modelo de Meta, inteligente y versátil.
  4. DeepSeek R1: El nuevo rey de la lógica y la programación 💻.

📜 El Script Maestro: “IA Soberana Final”

Este script es una obra de ingeniería que automatiza todo: descarga la base, inyecta 15GB de inteligencia, soluciona problemas de permisos y te regala una interfaz web oscura súper elegante.

⚠️ Requisito: Necesitas 60GB de espacio en disco y un USB de al menos 32GB.

#!/bin/bash
# =================================================================
# 🚀 SCRIPT DE CREACIÓN DE ISO SOBERANA - IA OFFLINE
# 🦾 Linux Mint 22 + Ollama + Interfaz Web Visual
# =================================================================

echo "🔥 Iniciando la construcción de tu Estación IA..."
sudo apt update || echo "Saltando errores de repos..."
sudo apt install -y binutils wget squashfs-tools xorriso isolinux libisoburn1

set -e

# 1. DESCARGA DE LA BASE
if [ ! -f mint_xfce.iso ]; then
    echo "📥 Descargando Linux Mint XFCE..."
    wget -O mint_xfce.iso https://mirrors.layeronline.com/linuxmint/stable/22/linuxmint-22-xfce-64bit.iso
fi

# 2. LIMPIEZA DE ENTORNO
sudo umount -l squashfs-root/dev || true
sudo umount -l mnt || true
sudo rm -rf squashfs-root extract-cd mnt
mkdir -p mnt extract-cd squashfs-root

# 3. EXTRACCIÓN DEL ADN LINUX
sudo mount -o loop mint_xfce.iso mnt
cp -a mnt/. extract-cd
sudo umount mnt
sudo unsquashfs -d squashfs-root extract-cd/casper/filesystem.squashfs

# 4. CONFIGURACIÓN E INYECCIÓN DE IA (CHROOT)
sudo cp /etc/resolv.conf squashfs-root/etc/resolv.conf 
sudo mount --bind /dev squashfs-root/dev

sudo chroot squashfs-root /bin/bash < /opt/ia_interface/index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Cerebro IA Offline</title>
    <style>
        body { font-family: sans-serif; background: #0f0f0f; color: #e0e0e0; display: flex; flex-direction: column; height: 100vh; margin: 0; }
        header { background: #1a1a1a; padding: 15px; border-bottom: 2px solid #25d366; text-align: center; font-weight: bold; color: #25d366; }
        #chat { flex: 1; overflow-y: auto; padding: 20px; }
        .msg { margin-bottom: 15px; padding: 12px; border-radius: 12px; max-width: 85%; line-height: 1.5; }
        .user { background: #005c4b; align-self: flex-end; margin-left: auto; }
        .ai { background: #262626; border-left: 4px solid #25d366; }
        #input-area { padding: 20px; background: #1a1a1a; display: flex; gap: 10px; }
        input { flex: 1; padding: 12px; border-radius: 8px; border: none; background: #2a2a2a; color: white; outline: none; }
        button { padding: 12px 25px; background: #25d366; color: black; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; }
    </style>
</head>
<body>
    <header>🤖 IA SOBERANA - SELECTOR: 
        <select id="m" style="background:#2a2a2a; color:white; border:none; padding:5px; border-radius:4px;">
            <option value="llama3.1:8b">Llama 3.1 (Creatividad)</option>
            <option value="deepseek-r1:14b">DeepSeek R1 (Lógica)</option>
        </select>
    </header>
    <div id="chat"></div>
    <div id="input-area">
        <input type="text" id="p" placeholder="Escribe tu consulta soberana..." onkeypress="if(event.key=='Enter')send()">
        <button onclick="send()">ENVIAR ⚡</button>
    </div>
    <script>
        async function send() {
            const p = document.getElementById('p');
            const m = document.getElementById('m').value;
            const chat = document.getElementById('chat');
            const val = p.value;
            if(!val) return;
            chat.innerHTML += '<div class="msg user">'+val+'</div>';
            p.value = 'IA Pensando...'; p.disabled = true;
            try {
                const r = await fetch('http://127.0.0.1:11434/api/generate', {
                    method: 'POST',
                    body: JSON.stringify({ model: m, prompt: val, stream: false })
                });
                const d = await r.json();
                chat.innerHTML += '<div class="msg ai">'+d.response.replace(/\n/g,'<br>')+'</div>';
            } catch (e) { 
                chat.innerHTML += '<div class="msg ai" style="color:#ff5555">⚠️ Error: El motor está despertando. Espera 10 segundos.</div>'; 
            }
            p.value = ''; p.disabled = false; p.focus();
            chat.scrollTop = chat.scrollHeight;
        }
    </script>
</body>
</html>
HTML

# LANZADOR AL ESCRITORIO
DESKTOP_FILE="[Desktop Entry]
Name=Cerebro IA (OFFLINE)
Comment=Iniciar IA Visual
Exec=bash -c 'export OLLAMA_MODELS=/usr/share/ollama/.ollama; export OLLAMA_ORIGINS=\"*\"; sudo systemctl stop ollama; ollama serve & sleep 10; firefox --new-window /opt/ia_interface/index.html'
Icon=firefox
Type=Application
Terminal=false"

mkdir -p /etc/skel/Desktop
echo "$DESKTOP_FILE" > /etc/skel/Desktop/IA.desktop
chmod +x /etc/skel/Desktop/IA.desktop

umount /proc
umount /sys
exit
EOF

sudo umount squashfs-root/dev

# 5. EMPAQUETADO FINAL 📦
echo "📦 Comprimiendo sistema... ¡Casi listo!"
sudo rm -f extract-cd/casper/filesystem.squashfs
sudo mksquashfs squashfs-root extract-cd/casper/filesystem.squashfs -comp xz
sudo xorriso -as mkisofs -iso-level 3 -full-iso9660-filenames -volid "IA_SOBERANA" -eltorito-boot isolinux/isolinux.bin -eltorito-catalog isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat -output IA_SOBERANA_2026.iso extract-cd

echo "✅ ¡HECHO! Tu ISO pesa unos 20GB. Grábala y desconéctate del mundo."
    

🤔 Preguntas Frecuentes (FAQ)

⚡ ¿Qué tan rápido responde la IA?

Depende de tu hardware. En un procesador i7 con 16GB de RAM, responde de forma fluida. Si tienes una tarjeta NVIDIA, ¡volará! 🚀

📁 ¿Puedo usarla para analizar mis archivos?

¡Sí! Como el sistema es local, puedes copiar tus documentos al escritorio de Linux Mint y pedirle a la IA que te ayude, con la seguridad de que ningún dato saldrá de tu USB.

🛠️ ¿Cómo grabo la ISO?

Usa BalenaEtcher o Ventoy. Ventoy es genial porque te permite tener varias ISOs en el mismo USB.


💡 Conclusión: La Soberanía Digital empieza aquí

En un futuro donde la IA será el filtro de toda la información, poseer tu propio modelo sin censura y sin conexión es un superpoder. No dejes tu privacidad en manos de terceros. ¡Construye tu propio Cerebro Offline hoy mismo! 🧠🔥

¿Te gustó este tutorial? ¡Compártelo con otros entusiastas de Linux y la Privacidad! 📢

🚀 Boilerplate CFDI v1.1.2: DataTables server-side preciso, filtros reales y control total 📊⚙️

Entrada fija

🚀 Boilerplate CFDI v1.1.2: DataTables server-side preciso, filtros reales y control total 📊⚙️

Cuando se trabaja con CFDI, XML, PHP, MySQL / MariaDB y DataTables, hay una verdad que todo desarrollador aprende con la experiencia:

👉 si el backend no entiende exactamente lo que el frontend pide, el sistema termina dando resultados incorrectos 😬

Búsquedas que no coinciden, columnas que se ordenan cuando no deberían, filtros que parecen funcionar “a medias”… y lo peor: sin errores visibles.

Por eso llega Boilerplate CFDI v1.1.2, una actualización enfocada en precisión, coherencia y rendimiento real, especialmente en el manejo de DataTables en modo server-side 🚀


🧩 El reto real al usar DataTables en sistemas CFDI

DataTables es una herramienta poderosa, pero cuando se usa con:

serverSide: true

ocurre algo muy importante:

  • 📤 El frontend envía reglas claras
  • 📥 El backend debe interpretarlas correctamente

Ejemplo típico:

columnDefs: [{
    targets: [0, 19],
    searchable: false,
    orderable: false
}]

Esto indica que ciertas columnas:

  • ❌ No deben buscarse
  • ❌ No deben ordenarse

Si el backend ignora esta información, el comportamiento del sistema se vuelve impredecible ⚠️


❌ Síntomas clásicos de una implementación incompleta

Antes de esta mejora, era común encontrar situaciones como:

  • ❌ Búsquedas aplicadas a columnas visuales
  • ❌ Ordenamientos en botones o acciones
  • ❌ Filtros que no respetan la columna
  • ❌ Resultados distintos a lo que ve el usuario
  • ❌ Consultas SQL más pesadas de lo necesario

Todo parecía “funcionar”… hasta que la tabla empezaba a crecer 📈


✅ El enfoque aplicado en Boilerplate CFDI v1.1.2

En esta versión, el manejo de DataTables server-side fue refinado desde la base, siguiendo una regla simple pero poderosa:

El backend solo hace lo que el frontend permite.

Ahora el servidor interpreta directamente los parámetros enviados por DataTables:

  • columns[]
  • search
  • order
  • start
  • length

Y valida cada uno antes de construir la consulta SQL.


🔍 Búsqueda global inteligente

La búsqueda global ahora funciona de forma precisa y eficiente:

  • ✔ Solo se aplica a columnas marcadas como buscables
  • ✔ Ignora columnas visuales o auxiliares
  • ✔ Reduce condiciones LIKE innecesarias
  • ✔ Mejora tiempos de respuesta en MySQL / MariaDB

Esto es clave en sistemas CFDI, donde se manejan:

  • 📄 UUID
  • 📄 RFC
  • 📄 Razones sociales
  • 📄 Fechas
  • 📄 Métodos de pago
  • 📄 Tipos de comprobante

Buscar en todo ya no es una opción escalable 🚫


🔎 Filtros por columna confiables

Además de la búsqueda global, los filtros individuales por columna ahora:

  • ✔ Respetan la configuración del frontend
  • ✔ Se aplican solo cuando corresponde
  • ✔ No interfieren con otros filtros
  • ✔ Producen resultados coherentes

🎯 El resultado es un filtrado exacto y predecible.


🔃 Ordenamiento exacto y sin sorpresas

El ordenamiento fue otro punto clave corregido en v1.1.2.

Ahora el backend:

  • ✔ Valida el índice de la columna
  • ✔ Obtiene el campo real
  • ✔ Verifica que sea ordenable
  • ✔ Aplica ORDER BY solo cuando corresponde

Esto evita:

  • 🚫 Ordenamientos incorrectos
  • 🚫 Errores SQL
  • 🚫 Resultados inconsistentes

⚡ Rendimiento optimizado en MySQL y MariaDB

Gracias a estos ajustes, las consultas SQL ahora:

  • ✔ Son más limpias
  • ✔ Tienen menos condiciones innecesarias
  • ✔ Aprovechan mejor los índices
  • ✔ Responden mejor en tablas grandes

Esto se nota especialmente cuando se manejan:

  • 📄 Miles de CFDI
  • 🏢 Múltiples empresas
  • 📅 Rangos amplios de fechas

🧠 Menos magia, más control

Uno de los mayores beneficios de esta versión es la claridad del código.

Ahora el backend:

  • ✔ Valida todo explícitamente
  • ✔ Evita comportamientos implícitos
  • ✔ Es más fácil de leer y mantener
  • ✔ Permite escalar sin miedo

Para los equipos de desarrollo esto se traduce en:

  • 👨‍💻 Menos tiempo depurando
  • 👨‍💻 Más tiempo construyendo

🚀 Impacto directo en sistemas CFDI

Con Boilerplate CFDI v1.1.2 obtienes:

  • ✨ Tablas más rápidas
  • ✨ Filtros confiables
  • ✨ Ordenamientos correctos
  • ✨ Menor carga en el servidor
  • ✨ Mejor experiencia de usuario

Todo esto es fundamental cuando se trabaja con información fiscal sensible 📄🔒


🧪 Cambios técnicos destacados

  • 🔧 Refactor completo del server-side de DataTables
  • 🔧 Validación de columnas buscables y ordenables
  • 🔧 Mejor control de filtros globales e individuales
  • 🔧 Código compatible con MySQL y MariaDB
  • 🔧 Listo para producción y escalabilidad

🏁 Conclusión

Boilerplate CFDI v1.1.2 no agrega “features vistosas”. Agrega estabilidad, coherencia y control real 💎

Si trabajas con:

  • CFDI
  • XML
  • PHP
  • MySQL / MariaDB
  • DataTables

👉 esta versión eleva la calidad de tu sistema 🚀🔥

🔥 Grandes cambios y mejoras en el Boilerplate CodeIgniter: Multilenguaje, UX y Seguridad

Entrada fija


El desarrollo de aplicaciones modernas exige algo más que código funcional. Requiere una experiencia de usuario sólida, escalabilidad, seguridad y, sobre todo, una correcta internacionalización 🌍. En este artículo te explico todos los cambios y ajustes recientes aplicados al boilerplate, enfocados en mejorar la gestión de idiomas, autenticación, usabilidad y compatibilidad multiplataforma.


🚀 Internacionalización real: sistema multilenguaje mejorado

Uno de los cambios más importantes fue la optimización del sistema de traducciones. Ahora el boilerplate permite trabajar fácilmente con múltiples idiomas sin duplicar lógica ni romper vistas.

Se agregaron y ajustaron traducciones completas para:

  • 🇬🇧 Inglés
  • 🇩🇪 Alemán
  • 🇮🇹 Italiano
  • 🇮🇩 Indonesio
  • 🌍 Esperanto

Esto permite que el sistema pueda escalar internacionalmente sin modificar el core de la aplicación.

📌 Ejemplo de uso correcto del sistema de idiomas

<?= lang('Auth.showPassword') ?>

Este enfoque mantiene el código limpio, reutilizable y compatible con futuras extensiones.


🔐 Mostrar contraseña: mejora UX sin comprometer seguridad

Se agregó la opción de mostrar u ocultar la contraseña mediante un checkbox, algo esencial hoy en día para mejorar la experiencia del usuario, especialmente en dispositivos móviles 📱.

El texto del checkbox también es traducible:

<?= lang('Auth.showPassword') ?? 'Show password' ?>

De esta forma, si una traducción no existe, el sistema usa un fallback automáticamente.


🎨 Interfaz adaptable: PC, tablet y móvil

Todos los formularios fueron ajustados para verse correctamente en:

  • 🖥️ Computadoras de escritorio
  • 📱 Teléfonos móviles
  • 📲 Tablets

Se cuidaron márgenes, tamaños de fuente y alineación para garantizar una experiencia consistente sin importar el dispositivo.


🧠 Organización de idiomas: estructura clara y mantenible

Los archivos de idioma ahora están mejor organizados por contexto:

  • Global
  • Permisos
  • Roles
  • Menús
  • Usuarios
  • Autenticación

Esto facilita:

  • ✔️ Mantenimiento
  • ✔️ Escalabilidad
  • ✔️ Nuevos idiomas

🛠️ Compatibilidad total con WordPress (documentación)

Un punto clave fue documentar correctamente el código para que pueda mostrarse en WordPress sin que el CMS intente interpretar PHP.

Ejemplo correcto de código documentado:

<?= lang('Admin.Auth.showPassword') ?>

Esto permite compartir documentación técnica sin errores visuales ni de seguridad.


📈 SEO técnico y legibilidad

Se mejoraron:

  • 📌 Encabezados jerárquicos (H1, H2, H3)
  • 📌 Palabras clave relacionadas con CodeIgniter y boilerplates
  • 📌 Lectura natural para humanos 🤝 buscadores

Esto hace que el proyecto no solo sea bueno técnicamente, sino también visible en buscadores 🔍.


🔥 Conclusión

Estos cambios convierten al boilerplate en una base mucho más sólida, moderna y preparada para proyectos reales. No se trata solo de agregar funciones, sino de hacerlo bien: con orden, traducciones, UX y documentación clara.

Si estás construyendo un proyecto serio con CodeIgniter, estos ajustes marcan la diferencia entre un sistema básico y uno profesional 💼.

🚀 Seguiremos mejorando y optimizando el proyecto paso a paso.

Ordenamiento Descendente Correcto en Administrar Ventas con DataTables Server-Side en JCPOS Ultimate

Entrada fija

Ordenamiento Descendente Correcto en Administrar Ventas con DataTables Server-Side en JCPOS Ultimate

Uno de los puntos que más confusión genera al trabajar con DataTables en modo server-side es el ordenamiento por defecto. En el módulo Administrar Ventas de JCPOS Ultimate Punto de Venta, este tema cobra especial importancia, ya que el usuario espera ver siempre las ventas más recientes primero.

En esta publicación explicamos:

  • Por qué el ordenamiento descendente no parecía funcionar
  • Cómo maneja DataTables el orden en server-side
  • Dónde ocurre realmente el ordenamiento
  • Qué se ajustó para que el orden sea correcto
  • Recomendaciones para evitar este problema en el futuro

📁 Archivos involucrados

El flujo completo del listado de ventas utiliza dos archivos clave:

  • Vista:
    vistas/modulos/ventas.php
  • Backend DataTables:
    ajax/datatable-administrarVentas.ajax.php

Importante: No se modificó la vista ventas.php. El comportamiento correcto se logra entendiendo y respetando la lógica de DataTables server-side.


📊 El problema: “el orden no cambia”

Desde el lado del usuario, el síntoma era claro:

  • Se esperaba que la tabla se ordenara por la primera columna (ID)
  • En forma descendente (ventas más recientes arriba)
  • Pero el orden parecía no cambiar

Esto suele llevar a pensar erróneamente que:

  • El parámetro order no funciona
  • DataTables ignora la configuración
  • Existe un error en JavaScript

La realidad es otra.


⚠️ La clave: server-side cambia las reglas

Cuando DataTables funciona en modo normal (frontend), el ordenamiento se hace completamente en el navegador.

Pero cuando se activa:

serverSide: true

ocurre algo fundamental:

  • DataTables NO ordena los datos
  • Solo envía la instrucción de orden al servidor
  • El backend es quien decide el orden final

Por eso, aunque en JavaScript se indique:

order: [[0, "desc"]]

si el backend no respeta esa instrucción, el orden visual nunca cambiará.


📡 Qué envía realmente DataTables al backend

En cada petición AJAX, DataTables envía parámetros como:

  • order[0][column] → índice de la columna
  • order[0][dir] → asc / desc

En este caso:

  • Columna 0 → ID
  • Dirección → desc

Esto significa: “ordena por ID de forma descendente”


🔍 Por qué antes no se reflejaba el orden

El archivo datatable-administrarVentas.ajax.php recibe los parámetros de DataTables, pero:

  • El método ctrRangoFechasVentas() ya devuelve resultados
  • El orden puede venir definido internamente
  • Si no se aplica el ORDER BY dinámico, el resultado será siempre el mismo

Esto genera la sensación de que:

  • El DataTable “ignora” el orden

Cuando en realidad:

  • El backend está devolviendo los datos sin respetar la instrucción

✅ Qué se hizo para que el orden funcione correctamente

El cambio no fue visual ni estético, fue conceptual y estructural.

✔ Se respetó el flujo server-side

DataTables:

  • Envía la columna y dirección
  • No ordena nada por sí mismo

Backend:

  • Recibe los parámetros
  • Aplica el orden correcto
  • Devuelve los datos ya ordenados

✔ La primera columna (ID) quedó como referencia

Al usar el ID como columna principal:

  • Mayor ID = venta más reciente
  • Orden descendente = últimas ventas primero

Esto coincide con la expectativa natural del usuario.


📈 Beneficios del ordenamiento correcto

  • Mejor experiencia de usuario
  • Acceso inmediato a ventas recientes
  • Menos clics para buscar información
  • Coherencia con sistemas POS profesionales

💡 Recomendaciones técnicas

1️⃣ Siempre pensar en backend cuando uses server-side

Si no se ordena en el servidor, no se ordenará nunca.

2️⃣ Usar IDs autoincrementales como referencia

Facilitan ordenamiento y paginación.

3️⃣ No confiar solo en JavaScript

El JS solo indica la intención, no ejecuta el orden.

4️⃣ Mantener coherencia entre columnas

El índice de columna debe coincidir exactamente con el JSON.

5️⃣ Documentar estos cambios

Evita confusiones futuras al mantener el sistema.


📌 Conclusión

El ordenamiento descendente en Administrar Ventas no fue un simple ajuste visual, sino una correcta aplicación del flujo DataTables server-side.

Entender que el orden se decide en el backend marca la diferencia entre una tabla que “parece no funcionar” y un sistema profesional, escalable y confiable.

Documentado por Julio Leyva

JCPOS Ultimate: Corrección profesional de descuentos en tickets y facturas PDF (TCPDF

Entrada fija

JCPOS Ultimate: Corrección profesional de descuentos en tickets y facturas PDF (TCPDF)

En JCPOS Ultimate Punto de Venta seguimos afinando cada detalle para ofrecer un sistema sólido, preciso y visualmente profesional. En este nuevo ajuste técnico se corrigió y optimizó el manejo de descuentos tanto en facturas PDF (TCPDF) como en la impresión de tickets, evitando errores visuales, desbordes de columnas y cálculos inconsistentes.

🔧 ¿Qué problema se resolvió?

Al agregar la columna de descuento en los documentos de venta, surgieron dos problemas comunes en sistemas POS:

  • 📄 El encabezado del PDF se desalineaba y aumentaba el alto del renglón.
  • 🧾 El ticket no reflejaba correctamente el total de descuentos aplicados por producto.

Esto ocurre principalmente por saltos de línea automáticos, padding excesivo y falta de normalización visual en TCPDF y HTML de impresión.

✅ Solución aplicada en JCPOS Ultimate

El commit implementa una solución limpia, compatible y optimizada:

  • ✔ Redistribución exacta de anchos sin romper el layout
  • ✔ Eliminación de saltos de línea forzados (white-space: nowrap)
  • ✔ Ajuste de line-height y padding para encabezados compactos
  • ✔ Cálculo acumulado real de descuentos por producto

📄 Factura PDF (TCPDF)

Se ajustó el encabezado de la tabla para que la nueva columna de descuento:

  • No incremente el alto del renglón
  • No rompa el ancho total del documento
  • Mantenga una lectura clara incluso en impresoras térmicas

Esto es especialmente importante en PDFs generados con TCPDF, donde el control de pixeles es crítico.

Archivo modificado:
extensiones/tcpdf/pdf/factura.php

🧾 Ticket de venta: descuentos correctamente reflejados

En la impresión del ticket se implementó:

  • 📌 Acumulación correcta de descuentos por producto
  • 📌 Visualización clara del subtotal, descuento, IVA y total
  • 📌 Compatibilidad total con impresión automática

Ahora el cliente puede ver claramente cuánto se le descontó en su compra, aumentando transparencia y confianza.

Archivos involucrados:

  • extensiones/impresionTicket/impresionTicket.php
  • Controladores y modelos de ventas, productos y empresa

💡 ¿Por qué este cambio es importante en un POS?

  • 📊 Evita errores contables por descuentos mal aplicados
  • 🖨 Mejora la impresión en tickets térmicos
  • 🧠 Facilita auditorías y revisiones
  • 🎯 Ofrece una imagen más profesional al cliente final

🔗 Commit del cambio

Puedes revisar el cambio completo aquí:
Ver commit en GitHub

🚀 JCPOS Ultimate sigue evolucionando

Cada ajuste en JCPOS Ultimate está pensado para resolver problemas reales del día a día en puntos de venta: desde el cálculo correcto de descuentos hasta la impresión perfecta en PDF y ticket.

Capturas de pantalla

Aqui seleccionamos el descuento por producto

Podemos elegir diferentes descuentos

En configuraciones->Descuentos podemos capturar los diferentes descuentos

Podemos darle acceso a los descuentos por usuario

Si usas JCPOS Ultimate, este cambio mejora directamente la precisión y presentación de tus ventas.

Adaptado y desarrollado por Julio Leyva.


#JCPOSUltimate #PuntoDeVenta #TCPDF #DescuentosPOS #PHP #OpenSource #SistemasDeVenta #Tickets #FacturasPDF

Creado con WordPress & Tema de Anders Norén