#!/usr/bin/env node /** * Script para actualizar CFDIs existentes con su XML original * Uso: node scripts/update-cfdi-xml.js * Ejemplo: node scripts/update-cfdi-xml.js /root/xmls tenant_roem691011ez4 */ const fs = require('fs'); const path = require('path'); const { Pool } = require('pg'); // Configuración de la base de datos const pool = new Pool({ host: 'localhost', port: 5432, database: 'horux360', user: 'postgres', password: 'postgres', }); // Extraer UUID del XML function extractUuidFromXml(xmlContent) { // Buscar UUID en TimbreFiscalDigital const uuidMatch = xmlContent.match(/UUID=["']([A-Fa-f0-9-]{36})["']/i); return uuidMatch ? uuidMatch[1].toUpperCase() : null; } // Procesar archivos en lotes async function processFiles(directory, schema, batchSize = 500) { const files = fs.readdirSync(directory).filter(f => f.toLowerCase().endsWith('.xml')); console.log(`\nEncontrados ${files.length} archivos XML en ${directory}`); console.log(`Schema: ${schema}`); console.log(`Tamaño de lote: ${batchSize}\n`); let updated = 0; let notFound = 0; let errors = 0; let processed = 0; const startTime = Date.now(); // Procesar en lotes for (let i = 0; i < files.length; i += batchSize) { const batch = files.slice(i, i + batchSize); const updates = []; // Leer y parsear XMLs del lote for (const file of batch) { try { const filePath = path.join(directory, file); const xmlContent = fs.readFileSync(filePath, 'utf8'); const uuid = extractUuidFromXml(xmlContent); if (uuid) { updates.push({ uuid, xmlContent }); } else { errors++; if (errors <= 5) { console.log(` ⚠ No se encontró UUID en: ${file}`); } } } catch (err) { errors++; if (errors <= 5) { console.log(` ✗ Error leyendo ${file}: ${err.message}`); } } } // Actualizar base de datos en una transacción const client = await pool.connect(); try { await client.query('BEGIN'); for (const { uuid, xmlContent } of updates) { const result = await client.query( `UPDATE "${schema}".cfdis SET xml_original = $1 WHERE UPPER(uuid_fiscal) = $2 AND xml_original IS NULL`, [xmlContent, uuid] ); if (result.rowCount > 0) { updated++; } else { notFound++; } } await client.query('COMMIT'); } catch (err) { await client.query('ROLLBACK'); console.error(`Error en lote: ${err.message}`); errors += batch.length; } finally { client.release(); } processed += batch.length; // Progreso const percent = ((processed / files.length) * 100).toFixed(1); const elapsed = ((Date.now() - startTime) / 1000).toFixed(0); const rate = (processed / elapsed).toFixed(0); process.stdout.write(`\r Procesando: ${processed}/${files.length} (${percent}%) - ${updated} actualizados - ${rate} archivos/seg `); } const totalTime = ((Date.now() - startTime) / 1000).toFixed(1); console.log(`\n\n✓ Completado en ${totalTime} segundos`); console.log(` - Actualizados: ${updated}`); console.log(` - No encontrados (UUID no existe en BD): ${notFound}`); console.log(` - Errores: ${errors}`); await pool.end(); } // Main const args = process.argv.slice(2); if (args.length < 2) { console.log('Uso: node scripts/update-cfdi-xml.js '); console.log('Ejemplo: node scripts/update-cfdi-xml.js /root/xmls tenant_roem691011ez4'); process.exit(1); } const [directory, schema] = args; if (!fs.existsSync(directory)) { console.error(`Error: El directorio ${directory} no existe`); process.exit(1); } processFiles(directory, schema).catch(err => { console.error('Error fatal:', err); process.exit(1); });