feat: SAT sync improvements, XML export, and operational fixes
SAT sync enhancements: - Filter active (vigente) CFDIs only via DocumentStatus to avoid SAT rejecting recibidos with "No se permite descarga de XML cancelados" - Reclassify CFDIs at save time: tipo='ingreso' received by tenant becomes 'egreso' based on RFC (emisor vs receptor) - Fix pool cleanup bug during long syncs: refresh getPool() on each saveCfdis call instead of holding stale reference for 45+ minutes - Add X-View-Tenant support to SAT controller via viewingTenantId - Add tenantMiddleware to SAT routes for global admin impersonation Cron jobs: - Add separate every-6-hours schedule for specific RFCs - ROEM691011EZ4 configured for frequent sync (00, 06, 12, 18 MX time) XML filesystem export: - Write .xml files to /var/horux/xml/<RFC>/YYYY/MM/UUID.xml - Activated per-RFC via XML_EXPORT_RFCS allowlist - Organized by year/month for browsability Auth improvements: - Send welcome + admin-notification emails on /auth/register (previously only /tenants createTenant flow sent emails) - Set role='contador' for self-registered users (not admin) to prevent new tenants from accessing cross-tenant data Infrastructure: - Set express trust proxy=1 to accept X-Forwarded-For from Nginx (fixes ERR_ERL_UNEXPECTED_X_FORWARDED_FOR from rate limiter) Operational scripts: - setup-horux360-tenant.ts: Provision Horux 360 tenant manually - send-welcome-aaron.ts: Resend welcome email for Aaron (registered before welcome-on-register was added) - export-xmls-roem.ts: Backfill filesystem XMLs from DB for ROEM Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
40
apps/api/scripts/export-xmls-roem.ts
Normal file
40
apps/api/scripts/export-xmls-roem.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Exporta todos los XMLs existentes de ROEM691011EZ4 al filesystem
|
||||
*/
|
||||
import { config } from 'dotenv';
|
||||
import { resolve } from 'path';
|
||||
config({ path: resolve(process.cwd(), '.env') });
|
||||
|
||||
import { Pool } from 'pg';
|
||||
import { mkdirSync, writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
const BASE_PATH = '/var/horux/xml/ROEM691011EZ4';
|
||||
const DB_URL = process.env.DATABASE_URL!.replace(/\/[^/?]+(\?.*)?$/, '/horux_roem691011ez4$1');
|
||||
|
||||
async function main() {
|
||||
const pool = new Pool({ connectionString: DB_URL });
|
||||
|
||||
const { rows } = await pool.query(
|
||||
`SELECT uuid_fiscal, fecha_emision, xml_original FROM cfdis WHERE xml_original IS NOT NULL`
|
||||
);
|
||||
|
||||
console.log(`Exportando ${rows.length} XMLs...`);
|
||||
let count = 0;
|
||||
|
||||
for (const row of rows) {
|
||||
const fecha = new Date(row.fecha_emision);
|
||||
const year = fecha.getFullYear().toString();
|
||||
const month = (fecha.getMonth() + 1).toString().padStart(2, '0');
|
||||
const dir = join(BASE_PATH, year, month);
|
||||
|
||||
mkdirSync(dir, { recursive: true });
|
||||
writeFileSync(join(dir, `${row.uuid_fiscal}.xml`), row.xml_original, 'utf-8');
|
||||
count++;
|
||||
}
|
||||
|
||||
console.log(`${count} XMLs exportados a ${BASE_PATH}`);
|
||||
await pool.end();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
Reference in New Issue
Block a user