chore: catálogo obligaciones, cierre automático, fixes SAT y facturación
- Catálogo de obligaciones fiscales expandido a 30 entradas con campo requierePago. - Soporte de frecuencia cuatrimestral en obligaciones y declaraciones. - Automatización de cierre de obligaciones fiscales desde Documentos › Declaraciones. - Nuevas tablas obligacion_evidencias, obligacion_periodos estados y declaracion_obligaciones. - Nuevo servicio obligacion-evidencias.service.ts y endpoints REST. - Refactor de declaraciones.service.ts para vincular obligaciones y crear evidencias. - Notificaciones por email para evidencias de obligaciones. - Adjuntar PDFs en correo de declaración subida. - Fix drill-down de CFDIs: carga completa al visualizar. - Fix sincronización SAT: tipos P/N, UUID case-insensitive, no reutilizar requestId. - Fix suscripciones pending en /configuracion/planes-despacho. - Fix sugerencias de Clave Producto SAT: importar catálogo y robustecer autocomplete. - Quitar toggle manual de completado en Configuración › Obligaciones fiscales › Tareas. - Scripts de soporte para Demo Ventas y utilerías (change-user-email, resend-welcome, import-clave-prod-serv). - Documentación de cambios en docs/CAMBIOS-2026-05-04.md.
This commit is contained in:
@@ -299,7 +299,7 @@ async function saveCfdis(
|
||||
cfdi_tipo_relacion=$88, cfdis_relacionados=$89,
|
||||
last_sat_sync=NOW(), sat_sync_job_id=$90::uuid,
|
||||
actualizado_en=NOW()
|
||||
WHERE uuid = $1`,
|
||||
WHERE LOWER(uuid) = LOWER($1)`,
|
||||
[cfdi.uuid, ...vals]
|
||||
);
|
||||
// Re-insert conceptos for updated CFDI
|
||||
@@ -355,7 +355,7 @@ async function saveCfdis(
|
||||
[...vals, contribuyenteId]
|
||||
);
|
||||
// Get the inserted cfdi id and save conceptos
|
||||
const { rows: [newRow] } = await pool.query(`SELECT id FROM cfdis WHERE uuid = $1`, [cfdi.uuid]);
|
||||
const { rows: [newRow] } = await pool.query(`SELECT id FROM cfdis WHERE LOWER(uuid) = LOWER($1)`, [cfdi.uuid]);
|
||||
if (newRow) await saveConceptosWithRetry(pool, newRow.id, cfdi);
|
||||
inserted++;
|
||||
}
|
||||
@@ -609,30 +609,35 @@ async function requestAndDownload(
|
||||
});
|
||||
let existingMap = (jobRow?.satRequestIds as Record<string, string> | null) || {};
|
||||
|
||||
// NOTA: se desactivó la reutilización de requestIds de jobs previos porque el SAT
|
||||
// limita las descargas por solicitud. Reusar un requestId de un job anterior puede
|
||||
// agotar el límite y devolver "Máximo de descargas permitidas", dejando el recovery
|
||||
// sin poder descargar. Cada job nuevo crea sus propias solicitudes.
|
||||
//
|
||||
// Si no existe en el job actual, buscar en el job más reciente del mismo tenant/contribuyente
|
||||
// SOLO si el rango de fechas es idéntico (mismo dateFrom/dateTo).
|
||||
if (!existingMap[kindKey]) {
|
||||
const previousJob = await prisma.satSyncJob.findFirst({
|
||||
where: {
|
||||
tenantId: jobRow?.tenantId,
|
||||
contribuyenteId: jobRow?.contribuyenteId ?? null,
|
||||
id: { not: jobId },
|
||||
dateFrom: jobRow?.dateFrom,
|
||||
dateTo: jobRow?.dateTo,
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
select: { satRequestIds: true },
|
||||
});
|
||||
if (previousJob?.satRequestIds) {
|
||||
const prevMap = previousJob.satRequestIds as Record<string, string>;
|
||||
if (prevMap[kindKey]) {
|
||||
console.log(`[SAT] Reutilizando requestId de job previo (${label}): ${prevMap[kindKey]}`);
|
||||
// Copiar al job actual para futuros usos
|
||||
await persistSatRequestId(jobId, kindKey, prevMap[kindKey]);
|
||||
existingMap = { ...existingMap, [kindKey]: prevMap[kindKey] };
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (!existingMap[kindKey]) {
|
||||
// const previousJob = await prisma.satSyncJob.findFirst({
|
||||
// where: {
|
||||
// tenantId: jobRow?.tenantId,
|
||||
// contribuyenteId: jobRow?.contribuyenteId ?? null,
|
||||
// id: { not: jobId },
|
||||
// dateFrom: jobRow?.dateFrom,
|
||||
// dateTo: jobRow?.dateTo,
|
||||
// },
|
||||
// orderBy: { createdAt: 'desc' },
|
||||
// select: { satRequestIds: true },
|
||||
// });
|
||||
// if (previousJob?.satRequestIds) {
|
||||
// const prevMap = previousJob.satRequestIds as Record<string, string>;
|
||||
// if (prevMap[kindKey]) {
|
||||
// console.log(`[SAT] Reutilizando requestId de job previo (${label}): ${prevMap[kindKey]}`);
|
||||
// // Copiar al job actual para futuros usos
|
||||
// await persistSatRequestId(jobId, kindKey, prevMap[kindKey]);
|
||||
// existingMap = { ...existingMap, [kindKey]: prevMap[kindKey] };
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
let requestId: string | null = existingMap[kindKey] || null;
|
||||
let verifyResult: Awaited<ReturnType<typeof verifySatRequest>> | undefined;
|
||||
|
||||
Reference in New Issue
Block a user