diff --git a/src/scheduler/workers/processListsBeforeAndDuringStart/helpers.ts b/src/scheduler/workers/processListsBeforeAndDuringStart/helpers.ts index 31f8bae2a..a1b9c4e8a 100644 --- a/src/scheduler/workers/processListsBeforeAndDuringStart/helpers.ts +++ b/src/scheduler/workers/processListsBeforeAndDuringStart/helpers.ts @@ -6,6 +6,7 @@ import type { import { logger } from "scheduler/logger"; import type { Audit, Event, AnnualReviewProviderEmailType } from "@prisma/client"; import { prisma } from "scheduler/prismaClient"; +import { log } from "winston"; export const now = new Date(Date.now()); const todayDateString = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate())); @@ -15,6 +16,7 @@ export function formatDate(date: Date = todayDateString) { return date.toLocaleString("en-gb", options); } +// TODO: use a query like in `shouldSend`. This function relies on the entire event or audit to be loaded into the fn, then uses iterations to determine if an email has been sent or superseded. export function isEmailSentBefore( event: Event | Audit | undefined, reminderType: ListAnnualReviewPostReminderType | ListItemAnnualReviewProviderReminderType @@ -52,21 +54,34 @@ export function isEmailSentBefore( export async function shouldSend( emailType: AnnualReviewProviderEmailType, listItemId: number, - annualReviewReference: string + annualReviewReference?: string ): Promise { if (!annualReviewReference) { + logger.warn( + `shouldSend: Annual review reference was not supplied, could not look up whether ${emailType} should be sent to ${listItemId}.` + ); return false; } - /** * This query uses postgres enums. Enums in postgres are ordered so `>` operator can be used. */ - const event = await prisma.$queryRaw`select * from "Event" + const event: Event[] | undefined = await prisma.$queryRaw`select * from "Event" where "listItemId" = ${listItemId} and "annualReviewEmailType" > ${emailType}::"AnnualReviewProviderEmailType" and "jsonData"->>'reference' = '${annualReviewReference}' and "type" = 'REMINDER' order by "time" limit 1`; - return !event; + /** + * Query always returns array, hence [0]. + */ + const hasItems = (event ?? []).length >= 1; + + if (hasItems) { + logger.info(`shouldSend: ${emailType} Email has already been sent to ${listItemId} on ${event?.[0].time}`); + return false; + } + + logger.debug(`shouldSend: ${emailType} Email has not been sent to ${listItemId}`); + return true; } diff --git a/src/scheduler/workers/processListsBeforeAndDuringStart/main.ts b/src/scheduler/workers/processListsBeforeAndDuringStart/main.ts index edee88cb7..7d9b8ed17 100644 --- a/src/scheduler/workers/processListsBeforeAndDuringStart/main.ts +++ b/src/scheduler/workers/processListsBeforeAndDuringStart/main.ts @@ -1,7 +1,7 @@ import { endOfDay, isSameDay, isWithinInterval, startOfDay, subDays } from "date-fns"; import { lowerCase, startCase } from "lodash"; -import { AuditEvent, ListItemEvent, AnnualReviewPostEmailType, AnnualReviewProviderEmailType } from "@prisma/client"; -import type { Event, Audit } from "@prisma/client"; +import { AuditEvent, ListItemEvent, AnnualReviewProviderEmailType } from "@prisma/client"; +import type { Event, Audit, AnnualReviewPostEmailType } from "@prisma/client"; import { prisma } from "scheduler/prismaClient"; import type { SendEmailResponse } from "notifications-node-client"; @@ -86,17 +86,13 @@ async function processProviderEmailsForListItems(list: List, listItems: ListItem */ for (const listItem of listItems) { const annualReviewReference = list.jsonData.currentAnnualReview?.reference; - const shouldSendStartedProviderEmail = await shouldSend( "sendStartedProviderEmail", listItem.id, - annualReviewReference ?? "" + annualReviewReference ); if (!shouldSendStartedProviderEmail) { - logger.debug( - `Not sending "sendStartedProviderEmail" to ${listItem.id}, already sent or superseded by another email` - ); return; } @@ -165,7 +161,7 @@ export async function processList(list: List, listItemsForList: ListItemWithHist ) { isEmailSent = isEmailSentBefore(latestAudit as Audit, "sendOneMonthPostEmail"); if (!isEmailSent) { - await processPostEmailsForList(list, "POST_ONE_MONTH", AnnualReviewPostEmailType.sendOneMonthPostEmail); + await processPostEmailsForList(list, "POST_ONE_MONTH", "sendOneMonthPostEmail"); } return; } @@ -177,23 +173,22 @@ export async function processList(list: List, listItemsForList: ListItemWithHist ) { isEmailSent = isEmailSentBefore(latestAudit as Audit, "sendOneWeekPostEmail"); if (!isEmailSent) { - await processPostEmailsForList(list, "POST_ONE_WEEK", AnnualReviewPostEmailType.sendOneWeekPostEmail); + await processPostEmailsForList(list, "POST_ONE_WEEK", "sendOneWeekPostEmail"); } return; } if (isSameDay(today, new Date(annualReviewKeyDates?.POST_ONE_DAY ?? ""))) { isEmailSent = isEmailSentBefore(latestAudit as Audit, "sendOneDayPostEmail"); if (!isEmailSent) { - await processPostEmailsForList(list, "POST_ONE_DAY", AnnualReviewPostEmailType.sendOneDayPostEmail); + await processPostEmailsForList(list, "POST_ONE_DAY", "sendOneDayPostEmail"); } return; } - if (isSameDay(new Date(annualReviewKeyDates?.START ?? ""), today)) { // email posts to notify of annual review start isEmailSent = isEmailSentBefore(latestAudit as Audit, "sendStartedPostEmail"); if (!isEmailSent) { - await processPostEmailsForList(list, "START", AnnualReviewPostEmailType.sendStartedPostEmail); + await processPostEmailsForList(list, "START", "sendStartedPostEmail"); } // update ListItem.isAnnualReview if today = the START milestone date