Update: nueva version Horux Despachos
This commit is contained in:
104
apps/api/src/jobs/notifications.job.ts
Normal file
104
apps/api/src/jobs/notifications.job.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Cron diario 8:30 AM (America/Mexico_City) que envía emails de:
|
||||
* - Alertas fiscales nuevas (Option B — una sola vez por alerta).
|
||||
* - Recordatorios próximos a vencer en ventanas 3d / 1d / 0d.
|
||||
*
|
||||
* Por-tenant try/catch: un fallo en un tenant no bloquea al resto.
|
||||
*/
|
||||
import cron from 'node-cron';
|
||||
import { prisma, tenantDb } from '../config/database.js';
|
||||
import { processNewAlertas, processProximosRecordatorios } from '../services/notifications.service.js';
|
||||
|
||||
const SCHEDULE = '30 8 * * *'; // 08:30 AM diario
|
||||
|
||||
let task: ReturnType<typeof cron.schedule> | null = null;
|
||||
|
||||
/** Ejecuta ambos procesos para UN tenant. Exportado para disparo manual. */
|
||||
export async function runNotificationsForTenant(tenantId: string): Promise<{
|
||||
alertasNuevas: number;
|
||||
recordatoriosEnviados: number;
|
||||
}> {
|
||||
const tenant = await prisma.tenant.findUnique({
|
||||
where: { id: tenantId },
|
||||
select: { id: true, nombre: true, rfc: true, active: true, databaseName: true },
|
||||
});
|
||||
if (!tenant || !tenant.active) {
|
||||
return { alertasNuevas: 0, recordatoriosEnviados: 0 };
|
||||
}
|
||||
|
||||
const pool = await tenantDb.getPool(tenantId, tenant.databaseName);
|
||||
const ctx = { rfc: tenant.rfc, nombre: tenant.nombre };
|
||||
|
||||
const [alertasResult, recordResult] = await Promise.all([
|
||||
processNewAlertas(pool, tenantId, ctx).catch(err => {
|
||||
console.error(`[Notifications] Alertas (${tenant.rfc}) fallo:`, err.message || err);
|
||||
return { contribuyentes: 0, nuevasTotal: 0 };
|
||||
}),
|
||||
processProximosRecordatorios(pool, tenantId, ctx).catch(err => {
|
||||
console.error(`[Notifications] Recordatorios (${tenant.rfc}) fallo:`, err.message || err);
|
||||
return { enviados: 0 };
|
||||
}),
|
||||
]);
|
||||
|
||||
if (alertasResult.nuevasTotal > 0 || recordResult.enviados > 0) {
|
||||
console.log(`[Notifications] ${tenant.rfc}: ${alertasResult.nuevasTotal} alertas nuevas, ${recordResult.enviados} recordatorios`);
|
||||
}
|
||||
|
||||
return {
|
||||
alertasNuevas: alertasResult.nuevasTotal,
|
||||
recordatoriosEnviados: recordResult.enviados,
|
||||
};
|
||||
}
|
||||
|
||||
/** Itera todos los tenants activos. */
|
||||
export async function runNotifications(): Promise<{
|
||||
tenants: number;
|
||||
alertasNuevas: number;
|
||||
recordatoriosEnviados: number;
|
||||
}> {
|
||||
const tenants = await prisma.tenant.findMany({
|
||||
where: { active: true },
|
||||
select: { id: true, rfc: true },
|
||||
});
|
||||
|
||||
let alertasNuevas = 0;
|
||||
let recordatoriosEnviados = 0;
|
||||
for (const t of tenants) {
|
||||
try {
|
||||
const r = await runNotificationsForTenant(t.id);
|
||||
alertasNuevas += r.alertasNuevas;
|
||||
recordatoriosEnviados += r.recordatoriosEnviados;
|
||||
} catch (err: any) {
|
||||
console.error(`[Notifications] Tenant ${t.rfc} fallo completo:`, err.message || err);
|
||||
}
|
||||
}
|
||||
return { tenants: tenants.length, alertasNuevas, recordatoriosEnviados };
|
||||
}
|
||||
|
||||
export function startNotificationsJob(): void {
|
||||
if (task) {
|
||||
console.warn('[Notifications Cron] Ya iniciado');
|
||||
return;
|
||||
}
|
||||
task = cron.schedule(SCHEDULE, async () => {
|
||||
try {
|
||||
const result = await runNotifications();
|
||||
console.log(
|
||||
`[Notifications Cron] ${result.tenants} tenants — ` +
|
||||
`${result.alertasNuevas} alertas nuevas, ${result.recordatoriosEnviados} recordatorios`,
|
||||
);
|
||||
} catch (err: any) {
|
||||
console.error('[Notifications Cron] Error general:', err.message || err);
|
||||
}
|
||||
}, {
|
||||
timezone: 'America/Mexico_City',
|
||||
});
|
||||
console.log(`[Notifications Cron] Programado: ${SCHEDULE} (08:30 AM diario America/Mexico_City)`);
|
||||
}
|
||||
|
||||
export function stopNotificationsJob(): void {
|
||||
if (task) {
|
||||
task.stop();
|
||||
task = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user