feat(notificaciones): global send flags + editable schedules
The "Flags del envío" panel lived inside the Servicios tab and only
governed the four bulk jobs. The pólizas half had no debug at all, so
there was no way to test a renewal notice without mailing a real
customer. The panel now lives in the /notificaciones shell above the
tabs and both halves read it.
`debug` on the renewal path diverts to the same override inbox as the
servicios jobs and deliberately does NOT write the `RenewalNotice` row
or advance the sweep's `lastSuccessfulAt` — the customer was not
notified, so nothing may gate the letter they are still owed.
`ignoreDayRestriction` and `useEmailLimit` stay estado-de-cuenta-only
and are labelled as such.
Both automatic sweeps are now operator-editable. The renewal cadence
was a `@Cron("0 6 * * *")` literal and servicios had no automatic run
at all; both now resolve through `NotificationScheduleService`, which
stores the cadence in `app_settings` and reinstalls the cron job on
save — no redeploy, no restart. Defaults preserve current behaviour:
pólizas 06:00 daily, servicios off. A scheduled run never inherits the
UI flags; it always sends for real.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
import { Injectable, Logger, ServiceUnavailableException } from "@nestjs/common";
|
||||
import {
|
||||
Injectable,
|
||||
Logger,
|
||||
OnModuleInit,
|
||||
ServiceUnavailableException,
|
||||
} from "@nestjs/common";
|
||||
import {
|
||||
Currency,
|
||||
EmailNotificationServicio,
|
||||
@@ -11,7 +16,9 @@ import { MailService } from "../mail/mail.service";
|
||||
import { PrismaService } from "../prisma/prisma.service";
|
||||
import { SettingsService } from "../settings/settings.service";
|
||||
import { NotificationLogService } from "./notification-log.service";
|
||||
import { NotificationScheduleService } from "./notification-schedule.service";
|
||||
import {
|
||||
DEBUG_RECIPIENT,
|
||||
SendAttempt,
|
||||
NotificationJobKind,
|
||||
NotificationJobResponse,
|
||||
@@ -72,7 +79,7 @@ const RATE_LIMIT_EMAILS = 100;
|
||||
const PAYMENT_LOOKBACK_HOURS = 24;
|
||||
|
||||
@Injectable()
|
||||
export class NotificationsService {
|
||||
export class NotificationsService implements OnModuleInit {
|
||||
private readonly logger = new Logger(NotificationsService.name);
|
||||
|
||||
constructor(
|
||||
@@ -80,10 +87,32 @@ export class NotificationsService {
|
||||
private readonly mail: MailService,
|
||||
private readonly log: NotificationLogService,
|
||||
private readonly settings: SettingsService,
|
||||
private readonly schedule: NotificationScheduleService,
|
||||
) {}
|
||||
|
||||
/** The automatic servicios sweep is the same "ejecutar todos" the button
|
||||
* fires. Off by default — see the defaults in `NotificationScheduleService`. */
|
||||
async onModuleInit(): Promise<void> {
|
||||
await this.schedule.register("servicios", () => this.scheduledRunAll());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unattended run of all four jobs. Never debug, and never
|
||||
* `ignoreDayRestriction`: an automatic run on the operator's own cadence is
|
||||
* exactly the case the Mon/Wed/Fri gate was written for, so bypassing it
|
||||
* here would mail the red list every single scheduled day.
|
||||
*/
|
||||
async scheduledRunAll(): Promise<void> {
|
||||
const result = await this.runAll({});
|
||||
this.logger.log(
|
||||
`Corrida programada de servicios: enviados ${result.sent}, ` +
|
||||
`omitidos ${result.skipped}, fallidos ${result.failed}, ` +
|
||||
`jobs con error ${result.errors}.`,
|
||||
);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* Public jobs — called by the controller and by future cron sweeps alike.
|
||||
* Public jobs — called by the controller, the schedule, and tests alike.
|
||||
* ========================================================================== */
|
||||
|
||||
/** Job 1 — Outstanding payments. */
|
||||
@@ -802,10 +831,10 @@ export class NotificationsService {
|
||||
* Internals — send / log / balance helpers.
|
||||
* ========================================================================== */
|
||||
|
||||
/** Where debug=1 sends everything. The PHP used
|
||||
* `rmancinas@freakma.net`; same here. */
|
||||
/** Where debug=1 sends everything. Shared with the renewal sweep so both
|
||||
* halves of /notificaciones divert to the same inbox. */
|
||||
private debugEmail(): string {
|
||||
return "rmancinas@freakma.net";
|
||||
return DEBUG_RECIPIENT;
|
||||
}
|
||||
|
||||
/** The customer's `minimumBalance`, or null if unset. The legacy TIPO
|
||||
|
||||
Reference in New Issue
Block a user