fix(sat,conciliacion): propagar contribuyenteId en sync SAT y campos faltantes en visor de conciliacion

- sat-sync.job.ts: cron diario e incremental ahora iteran contribuyentes
  por tenant y pasan contribuyenteId a startSync(). Evita que CFDIs
  importados del SAT queden con contribuyente_id = NULL.

- sat.service.ts: retryJob() ahora reintenta con job.contribuyenteId.

- conciliacion.service.ts: agrega campos faltantes al SELECT de CFDIs:
  status, formaPago, serie, folio, usoCfdi, subtotal, descuento,
  moneda, tipoCambio, ivaTraslado, ivaRetencion, isrRetencion,
  fechaCertSat. Antes el visor mostraba 'CANCELADO' para todos los
  CFDIs (status era undefined) y faltaban datos de forma de pago,
  impuestos, serie/folio, etc.

Refs: docs/CAMBIOS-2026-05-09.md secciones 6 y 7
This commit is contained in:
Horux Dev
2026-05-11 03:58:53 +00:00
parent 9f11a0ba39
commit e21ccd6860
4 changed files with 237 additions and 25 deletions

View File

@@ -58,24 +58,56 @@ async function needsInitialSync(tenantId: string): Promise<boolean> {
}
/**
* Ejecuta sincronización para un tenant
* Ejecuta sincronización para un tenant y sus contribuyentes
*/
async function syncTenant(tenantId: string): Promise<void> {
try {
// Verificar si hay sync activo
const status = await getSyncStatus(tenantId);
if (status.hasActiveSync) {
console.log(`[SAT Cron] Tenant ${tenantId} ya tiene sync activo, omitiendo`);
return;
}
// Determinar tipo de sync
const needsInitial = await needsInitialSync(tenantId);
const syncType = needsInitial ? 'initial' : 'daily';
console.log(`[SAT Cron] Iniciando sync ${syncType} para tenant ${tenantId}`);
const jobId = await startSync(tenantId, syncType);
console.log(`[SAT Cron] Job ${jobId} iniciado para tenant ${tenantId}`);
// Obtener contribuyentes del tenant
const tenant = await prisma.tenant.findUnique({
where: { id: tenantId },
select: { databaseName: true },
});
let contribuyenteIds: string[] = [];
if (tenant?.databaseName) {
const pool = await tenantDb.getPool(tenantId, tenant.databaseName);
const { rows } = await pool.query('SELECT entidad_id FROM contribuyentes');
contribuyenteIds = rows.map((r: any) => r.entidad_id);
}
// Si no hay contribuyentes, sincronizar a nivel tenant (legacy Horux 360)
if (contribuyenteIds.length === 0) {
const status = await getSyncStatus(tenantId);
if (status.hasActiveSync) {
console.log(`[SAT Cron] Tenant ${tenantId} ya tiene sync activo, omitiendo`);
return;
}
console.log(`[SAT Cron] Iniciando sync ${syncType} para tenant ${tenantId} (sin contribuyentes)`);
const jobId = await startSync(tenantId, syncType);
console.log(`[SAT Cron] Job ${jobId} iniciado para tenant ${tenantId}`);
return;
}
// Sincronizar cada contribuyente
for (const contribuyenteId of contribuyenteIds) {
try {
const status = await getSyncStatus(tenantId, contribuyenteId);
if (status.hasActiveSync) {
console.log(`[SAT Cron] Tenant ${tenantId} contribuyente ${contribuyenteId} ya tiene sync activo, omitiendo`);
continue;
}
console.log(`[SAT Cron] Iniciando sync ${syncType} para tenant ${tenantId} contribuyente ${contribuyenteId}`);
const jobId = await startSync(tenantId, syncType, undefined, undefined, contribuyenteId);
console.log(`[SAT Cron] Job ${jobId} iniciado para tenant ${tenantId} contribuyente ${contribuyenteId}`);
} catch (error: any) {
console.error(`[SAT Cron] Error sincronizando tenant ${tenantId} contribuyente ${contribuyenteId}:`, error.message);
}
}
} catch (error: any) {
console.error(`[SAT Cron] Error sincronizando tenant ${tenantId}:`, error.message);
}
@@ -150,19 +182,11 @@ async function getTenantsConSatIncremental(): Promise<string[]> {
}
/**
* Dispara una sincronización incremental (ventana de 6 horas) para un tenant.
* Si el tenant ya tiene un sync activo, omite para no solapar solicitudes al SAT.
* Si el tenant nunca ha hecho `initial`, omite: el incremental no debe actuar
* como primera descarga — la inicial requiere correrse aparte.
* Dispara una sincronización incremental (ventana de 6 horas) para un tenant
* y sus contribuyentes.
*/
async function incrementalSyncTenant(tenantId: string): Promise<void> {
try {
const status = await getSyncStatus(tenantId);
if (status.hasActiveSync) {
console.log(`[SAT Cron Inc] Tenant ${tenantId} con sync activo, omitiendo`);
return;
}
const completedInitial = await prisma.satSyncJob.findFirst({
where: { tenantId, type: 'initial', status: 'completed' },
});
@@ -171,9 +195,48 @@ async function incrementalSyncTenant(tenantId: string): Promise<void> {
return;
}
console.log(`[SAT Cron Inc] Iniciando incremental para tenant ${tenantId}`);
const jobId = await startSync(tenantId, 'incremental');
console.log(`[SAT Cron Inc] Job ${jobId} iniciado`);
// Obtener contribuyentes del tenant
const tenant = await prisma.tenant.findUnique({
where: { id: tenantId },
select: { databaseName: true },
});
let contribuyenteIds: string[] = [];
if (tenant?.databaseName) {
const pool = await tenantDb.getPool(tenantId, tenant.databaseName);
const { rows } = await pool.query('SELECT entidad_id FROM contribuyentes');
contribuyenteIds = rows.map((r: any) => r.entidad_id);
}
// Si no hay contribuyentes, sincronizar a nivel tenant (legacy)
if (contribuyenteIds.length === 0) {
const status = await getSyncStatus(tenantId);
if (status.hasActiveSync) {
console.log(`[SAT Cron Inc] Tenant ${tenantId} con sync activo, omitiendo`);
return;
}
console.log(`[SAT Cron Inc] Iniciando incremental para tenant ${tenantId} (sin contribuyentes)`);
const jobId = await startSync(tenantId, 'incremental');
console.log(`[SAT Cron Inc] Job ${jobId} iniciado`);
return;
}
// Sincronizar cada contribuyente
for (const contribuyenteId of contribuyenteIds) {
try {
const status = await getSyncStatus(tenantId, contribuyenteId);
if (status.hasActiveSync) {
console.log(`[SAT Cron Inc] Tenant ${tenantId} contribuyente ${contribuyenteId} con sync activo, omitiendo`);
continue;
}
console.log(`[SAT Cron Inc] Iniciando incremental para tenant ${tenantId} contribuyente ${contribuyenteId}`);
const jobId = await startSync(tenantId, 'incremental', undefined, undefined, contribuyenteId);
console.log(`[SAT Cron Inc] Job ${jobId} iniciado`);
} catch (error: any) {
console.error(`[SAT Cron Inc] Error para tenant ${tenantId} contribuyente ${contribuyenteId}:`, error.message);
}
}
} catch (error: any) {
console.error(`[SAT Cron Inc] Error para tenant ${tenantId}:`, error.message);
}