- Add gradient header with emisor info and prominent serie/folio - Improve status badges with pill design - Add receptor section with left accent border - Show complete uso CFDI descriptions - Create card grid for payment method, forma pago, moneda - Improve conceptos table with zebra striping and SAT keys - Add elegant totals box with blue footer - Enhance timbre fiscal section with QR placeholder and SAT URL - Add update-cfdi-xml.js script for bulk XML import Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
136 lines
3.8 KiB
JavaScript
136 lines
3.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Script para actualizar CFDIs existentes con su XML original
|
|
* Uso: node scripts/update-cfdi-xml.js <directorio> <schema>
|
|
* 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 <directorio> <schema>');
|
|
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);
|
|
});
|