Created
May 23, 2026 10:44
-
-
Save iDavidMorales/46b9436eb8f3d00507ca14fdb3680dcb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const Firebird = require('node-firebird'); | |
| const axios = require('axios'); | |
| // Configuración de conexión (ruta absoluta en macOS) | |
| const options = { | |
| host: '127.0.0.1', | |
| port: 3050, | |
| database: '/Library/Frameworks/Firebird.framework/Resources/data/PDVDATA.fdb', | |
| user: 'SYSDBA', | |
| password: 'masterkey' | |
| }; | |
| // URL de tu API en Routicket | |
| const API_ENDPOINT = 'https://routicket.com/api/eleventa/recibir-tickets.php'; | |
| const API_KEY = 'rt_live_8f7d6e5c4b3a2'; | |
| const COMERCIO_ID = 'com_00192'; | |
| async function sincronizar() { | |
| console.log(`[${new Date().toLocaleString()}] Buscando tickets nuevos...`); | |
| Firebird.attach(options, (err, db) => { | |
| if (err) { | |
| console.error("Error al conectar a Firebird:", err.message); | |
| return; | |
| } | |
| // 1. Buscamos ventas que aún no se han subido | |
| db.query('SELECT * FROM VENTAS WHERE SINCRONIZADO = 0', async (err, ventasResult) => { | |
| if (err) { | |
| console.error("Error en la consulta de ventas:", err.message); | |
| db.detach(); | |
| return; | |
| } | |
| if (ventasResult.length === 0) { | |
| db.detach(); | |
| return; // Silencioso si no hay ventas para no llenar los logs de PM2 | |
| } | |
| console.log(`Procesando ${ventasResult.length} ticket(s)...`); | |
| let ventasProcesadas = []; | |
| // 2. Extraemos las partidas de cada venta usando un bucle asíncrono | |
| for (let v of ventasResult) { | |
| const queryPartidas = `SELECT * FROM PARTIDAS WHERE ID_VENTA = ${v.ID_VENTA}`; | |
| // Esperamos a que Firebird nos devuelva los productos de este ticket | |
| const partidas = await new Promise((resolve) => { | |
| db.query(queryPartidas, (err, items) => { | |
| if (err || !items) { | |
| resolve([]); | |
| } else { | |
| resolve(items.map(p => ({ | |
| codigo_barras: p.CODIGO || '', | |
| descripcion: p.DESCRIPCION || 'Sin descripción', | |
| cantidad: parseFloat(p.CANTIDAD) || 1, | |
| precio_unitario: parseFloat(p.PRECIO) || 0, | |
| importe: (parseFloat(p.CANTIDAD) || 1) * (parseFloat(p.PRECIO) || 0) | |
| }))); | |
| } | |
| }); | |
| }); | |
| // 3. Armamos el objeto final del ticket | |
| ventasProcesadas.push({ | |
| id_venta_local: v.ID_VENTA, | |
| folio_ticket: v.FOLIO, | |
| fecha_venta: new Date(v.FECHA).toISOString(), | |
| cajero: "Caja 1", // Ajustar si agregas la columna cajero en Firebird | |
| total_vendido: parseFloat(v.TOTAL) || 0, | |
| partidas: partidas // Ahora lleva todo el detalle de productos | |
| }); | |
| } | |
| // 4. Preparamos el paquete JSON para el servidor | |
| const payload = { | |
| auth: { api_key: API_KEY, comercio_id: COMERCIO_ID }, | |
| ventas: ventasProcesadas | |
| }; | |
| // 5. Enviamos a Routicket | |
| axios.post(API_ENDPOINT, payload) | |
| .then(response => { | |
| console.log("Respuesta del servidor:", response.data); | |
| // Si el servidor confirma éxito, marcamos en la BD local | |
| if (response.data.status === 'success' || response.data.status === 200) { | |
| db.query('UPDATE VENTAS SET SINCRONIZADO = 1 WHERE SINCRONIZADO = 0', (err) => { | |
| if (!err) console.log("BD Local: Tickets marcados como sincronizados."); | |
| db.detach(); | |
| }); | |
| } else { | |
| db.detach(); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error("Error HTTP al enviar a Routicket:", error.message); | |
| db.detach(); | |
| }); | |
| }); | |
| }); | |
| } | |
| // Ejecutar cada minuto (60000 ms) | |
| setInterval(sincronizar, 60000); | |
| sincronizar(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment