const fs = require('fs'); const path = require('path'); const { Pool } = require('pg'); const { parseXml } = require('/root/HoruxDespachosNuevo/apps/api/dist/services/sat/sat-parser.service.js'); const DB_PASSWORD = 'ZxHMrmnwanvLfLDdNJdRthFjWF2Lj1Rb'; const BASE_DIR = '/root/HoruxDespachosNuevo/apps/api/data/xmls'; function getPool(dbName) { return new Pool({ host: 'localhost', user: 'postgres', password: DB_PASSWORD, database: dbName }); } async function reprocessXml(filePath, rfc, tipoCfdi) { let xmlContent = fs.readFileSync(filePath, 'utf-8'); if (xmlContent.charCodeAt(0) === 0xFEFF) { xmlContent = xmlContent.slice(1); } const cfdi = parseXml(xmlContent, tipoCfdi); if (!cfdi) { console.log(` SKIP: parseXml returned null for ${filePath}`); return false; } const dbName = `horux_${rfc.toLowerCase()}`; const pool = getPool(dbName); try { const uuidNorm = cfdi.uuid.toLowerCase(); const { rows: existing } = await pool.query( `SELECT id FROM cfdis WHERE LOWER(uuid) = $1`, [uuidNorm] ); if (existing.length === 0) { console.log(` SKIP: CFDI ${uuidNorm} not found in DB ${dbName}`); return false; } const cfdiId = existing[0].id; const tc = cfdi.tipoCambio || 1; const m = (v) => (v || 0) * tc; // Update cfdis await pool.query( `UPDATE cfdis SET serie = $1, folio = $2, status = $3, fecha_emision = $4, fecha_cert_sat = $5, rfc_emisor = $6, nombre_emisor = $7, rfc_receptor = $8, nombre_receptor = $9, subtotal = $10, subtotal_mxn = $11, descuento = $12, descuento_mxn = $13, total = $14, total_mxn = $15, moneda = $16, tipo_cambio = $17, metodo_pago = $18, forma_pago = $19, uso_cfdi = $20, pac = $21, isr_retencion = $22, isr_retencion_mxn = $23, iva_traslado = $24, iva_traslado_mxn = $25, iva_retencion = $26, iva_retencion_mxn = $27, ieps_traslado = $28, ieps_traslado_mxn = $29, ieps_retencion = $30, ieps_retencion_mxn = $31, impuestos_locales_trasladado = $32, impuestos_locales_trasladado_mxn = $33, impuestos_locales_retenidos = $34, impuestos_locales_retenidos_mxn = $35, monto_pago = $36, monto_pago_mxn = $37, fecha_pago_p = $38, num_parcialidad = $39, isr_retencion_pago = $40, isr_retencion_pago_mxn = $41, iva_traslado_pago = $42, iva_traslado_pago_mxn = $43, iva_retencion_pago = $44, iva_retencion_pago_mxn = $45, ieps_traslado_pago = $46, ieps_traslado_pago_mxn = $47, ieps_retencion_pago = $48, ieps_retencion_pago_mxn = $49, fecha_pago = $50, fecha_inicial_pago = $51, fecha_final_pago = $52, num_dias_pagados = $53, num_seguro_social = $54, puesto = $55, salario_base_cot_apor = $56, salario_base_cot_apor_mxn = $57, salario_diario_integrado = $58, salario_diario_integrado_mxn = $59, total_percepciones = $60, total_percepciones_mxn = $61, total_deducciones = $62, total_deducciones_mxn = $63, imp_retenidos_nomina = $64, imp_retenidos_nomina_mxn = $65, otras_deducciones_nomina = $66, otras_deducciones_nomina_mxn = $67, subsidio_causado = $68, subsidio_causado_mxn = $69, regimen_fiscal_emisor = $70, regimen_fiscal_receptor = $71, xml_original = $72, cfdi_tipo_relacion = $73, cfdis_relacionados = $74, saldo_insoluto = $75, uuid_relacionado = $76, actualizado_en = NOW() WHERE id = $77`, [ cfdi.serie, cfdi.folio, cfdi.status, cfdi.fechaEmision, cfdi.fechaCertSat, cfdi.rfcEmisor, cfdi.nombreEmisor, cfdi.rfcReceptor, cfdi.nombreReceptor, cfdi.subtotal, m(cfdi.subtotal), cfdi.descuento, m(cfdi.descuento), cfdi.total, m(cfdi.total), cfdi.moneda, cfdi.tipoCambio, cfdi.metodoPago, cfdi.formaPago, cfdi.usoCfdi, cfdi.pac, cfdi.isrRetencion, m(cfdi.isrRetencion), cfdi.ivaTraslado, m(cfdi.ivaTraslado), cfdi.ivaRetencion, m(cfdi.ivaRetencion), cfdi.iepsTraslado, m(cfdi.iepsTraslado), cfdi.iepsRetencion, m(cfdi.iepsRetencion), cfdi.impuestosLocalesTrasladado, m(cfdi.impuestosLocalesTrasladado), cfdi.impuestosLocalesRetenidos, m(cfdi.impuestosLocalesRetenidos), cfdi.montoPago, m(cfdi.montoPago), cfdi.fechaPagoP, cfdi.numParcialidad, cfdi.isrRetencionPago, m(cfdi.isrRetencionPago), cfdi.ivaTrasladoPago, m(cfdi.ivaTrasladoPago), cfdi.ivaRetencionPago, m(cfdi.ivaRetencionPago), cfdi.iepsTrasladoPago, m(cfdi.iepsTrasladoPago), cfdi.iepsRetencionPago, m(cfdi.iepsRetencionPago), cfdi.fechaPago, cfdi.fechaInicialPago, cfdi.fechaFinalPago, cfdi.numDiasPagados, cfdi.numSeguroSocial, cfdi.puesto, cfdi.salarioBaseCotApor, m(cfdi.salarioBaseCotApor), cfdi.salarioDiarioIntegrado, m(cfdi.salarioDiarioIntegrado), cfdi.totalPercepciones, m(cfdi.totalPercepciones), cfdi.totalDeducciones, m(cfdi.totalDeducciones), cfdi.impRetenidosNomina, m(cfdi.impRetenidosNomina), cfdi.otrasDeduccionesNomina, m(cfdi.otrasDeduccionesNomina), cfdi.subsidioCausado, m(cfdi.subsidioCausado), cfdi.regimenFiscalEmisor, cfdi.regimenFiscalReceptor, xmlContent, cfdi.cfdiTipoRelacion, cfdi.cfdisRelacionados, cfdi.saldoInsoluto, cfdi.uuidRelacionado, cfdiId ] ); // Re-insert conceptos await pool.query(`DELETE FROM cfdi_conceptos WHERE cfdi_id = $1`, [cfdiId]); for (const c of cfdi.conceptos || []) { await pool.query( `INSERT INTO cfdi_conceptos ( cfdi_id, clave_prod_serv, no_identificacion, descripcion, cantidad, clave_unidad, unidad, valor_unitario, valor_unitario_mxn, importe, importe_mxn, descuento, descuento_mxn, isr_retencion, isr_retencion_mxn, iva_traslado, iva_traslado_mxn, iva_retencion, iva_retencion_mxn, ieps_traslado, ieps_traslado_mxn, ieps_retencion, ieps_retencion_mxn ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23)`, [ cfdiId, c.claveProdServ, c.noIdentificacion, c.descripcion, c.cantidad, c.claveUnidad, c.unidad, c.valorUnitario, m(c.valorUnitario), c.importe, m(c.importe), c.descuento, m(c.descuento), c.isrRetencion, m(c.isrRetencion), c.ivaTraslado, m(c.ivaTraslado), c.ivaRetencion, m(c.ivaRetencion), c.iepsTraslado, m(c.iepsTraslado), c.iepsRetencion, m(c.iepsRetencion) ] ); } console.log(` OK: ${uuidNorm} updated in ${dbName}`); return true; } catch (err) { console.error(` ERROR: ${filePath} - ${err.message}`); return false; } finally { await pool.end(); } } async function main() { let processed = 0; let skipped = 0; let errors = 0; const rfcs = fs.readdirSync(BASE_DIR).filter(d => fs.statSync(path.join(BASE_DIR, d)).isDirectory()); for (const rfc of rfcs) { const rfcDir = path.join(BASE_DIR, rfc); const tipos = fs.readdirSync(rfcDir).filter(d => fs.statSync(path.join(rfcDir, d)).isDirectory()); for (const tipo of tipos) { const tipoDir = path.join(rfcDir, tipo); const packages = fs.readdirSync(tipoDir).filter(d => fs.statSync(path.join(tipoDir, d)).isDirectory()); for (const pkg of packages) { const pkgDir = path.join(tipoDir, pkg); const files = fs.readdirSync(pkgDir).filter(f => f.endsWith('.xml')); for (const file of files) { const filePath = path.join(pkgDir, file); const buf = fs.readFileSync(filePath); if (buf.length < 3 || !(buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF)) { continue; } console.log(`Processing ${filePath}...`); const ok = await reprocessXml(filePath, rfc, tipo); if (ok) processed++; else skipped++; if (!ok) errors++; } } } } console.log(`\nDone. Processed: ${processed}, Skipped/Errors: ${errors}`); } main().catch(console.error);