diff --git a/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts b/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts index 1e481e244591..3379703684ae 100644 --- a/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts +++ b/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts @@ -1666,7 +1666,7 @@ describe('formatDefenderRevokedEmailNotification', () => { // Assert expect(res).toBe( - 'Krafa um gæsluvarðhald sem taka átti fyrir hjá Héraðsdómi Þingvalla sunnudaginn 24. janúar 2021, kl. 08:15, hefur verið afturkölluð.

Sakborningur: Gaui Glæpon, kt. 000000-1111.

Dómstóllinn hafði skráð þig sem verjanda í málinu.', + 'Krafa um gæsluvarðhald sem taka átti fyrir hjá Héraðsdómi Þingvalla sunnudaginn 24. janúar 2021, kl. 08:15, hefur verið afturkölluð.

Sakborningur: Gaui Glæpon, kt. 000000-1111.

Dómstóllinn hafði skráð þig sem verjanda/talsmann í málinu.', ) }) @@ -1692,7 +1692,7 @@ describe('formatDefenderRevokedEmailNotification', () => { // Assert expect(res).toBe( - 'Krafa um farbann sem taka átti fyrir hjá Héraðsdómi Þingvalla sunnudaginn 24. janúar 2021, kl. 08:15, hefur verið afturkölluð.

Sakborningur: Gaui Glæpon, kt. 111100-1111.

Dómstóllinn hafði skráð þig sem verjanda í málinu.', + 'Krafa um farbann sem taka átti fyrir hjá Héraðsdómi Þingvalla sunnudaginn 24. janúar 2021, kl. 08:15, hefur verið afturkölluð.

Sakborningur: Gaui Glæpon, kt. 111100-1111.

Dómstóllinn hafði skráð þig sem verjanda/talsmann í málinu.', ) }) @@ -1718,7 +1718,7 @@ describe('formatDefenderRevokedEmailNotification', () => { // Assert expect(res).toBe( - 'Krafa um vistun á viðeigandi stofnun sem taka átti fyrir hjá ótilgreindum dómstóli á ótilgreindum tíma, hefur verið afturkölluð.

Sakborningur: Nafn ekki skráð, kt. ekki skráð.

Dómstóllinn hafði skráð þig sem verjanda í málinu.', + 'Krafa um vistun á viðeigandi stofnun sem taka átti fyrir hjá ótilgreindum dómstóli á ótilgreindum tíma, hefur verið afturkölluð.

Sakborningur: Nafn ekki skráð, kt. ekki skráð.

Dómstóllinn hafði skráð þig sem verjanda/talsmann í málinu.', ) }) @@ -1744,7 +1744,7 @@ describe('formatDefenderRevokedEmailNotification', () => { // Assert expect(res).toBe( - 'Krafa um rannsóknarheimild (rof bankaleyndar) sem taka átti fyrir hjá Héraðsdómi Þingvalla sunnudaginn 24. janúar 2021, kl. 08:15, hefur verið afturkölluð.

Sakborningur: Gaui Glæpon, kt. 111100-1111.

Dómstóllinn hafði skráð þig sem verjanda í málinu.', + 'Krafa um rannsóknarheimild (rof bankaleyndar) sem taka átti fyrir hjá Héraðsdómi Þingvalla sunnudaginn 24. janúar 2021, kl. 08:15, hefur verið afturkölluð.

Sakborningur: Gaui Glæpon, kt. 111100-1111.

Dómstóllinn hafði skráð þig sem verjanda/talsmann í málinu.', ) }) @@ -1770,33 +1770,7 @@ describe('formatDefenderRevokedEmailNotification', () => { // Assert expect(res).toBe( - 'Krafa um rannsóknarheimild sem taka átti fyrir hjá Héraðsdómi Þingvalla sunnudaginn 24. janúar 2021, kl. 08:15, hefur verið afturkölluð.

Sakborningur: Gaui Glæpon, fd. 01.01.2022.

Dómstóllinn hafði skráð þig sem verjanda í málinu.', - ) - }) - - test('should format revoked notification for indictments', () => { - // Arrange - const type = CaseType.INDICTMENT - const defendantNationalId = '01.01.2022' - const defendantName = 'Gaui Glæpon' - const defendantNoNationalId = true - const court = 'Héraðsdómur Þingvalla' - const courtDate = new Date('2021-01-24T08:15') - - // Act - const res = formatDefenderRevokedEmailNotification( - formatMessage, - type, - defendantNationalId, - defendantName, - defendantNoNationalId, - court, - courtDate, - ) - - // Assert - expect(res).toBe( - 'Ákæra sem taka átti fyrir hjá Héraðsdómi Þingvalla sunnudaginn 24. janúar 2021, kl. 08:15, hefur verið afturkölluð.

Sakborningur: Gaui Glæpon, fd. 01.01.2022.

Dómstóllinn hafði skráð þig sem verjanda í málinu.', + 'Krafa um rannsóknarheimild sem taka átti fyrir hjá Héraðsdómi Þingvalla sunnudaginn 24. janúar 2021, kl. 08:15, hefur verið afturkölluð.

Sakborningur: Gaui Glæpon, fd. 01.01.2022.

Dómstóllinn hafði skráð þig sem verjanda/talsmann í málinu.', ) }) }) diff --git a/apps/judicial-system/backend/src/app/formatters/formatters.ts b/apps/judicial-system/backend/src/app/formatters/formatters.ts index 7ac613007d41..e1e4d613f0e2 100644 --- a/apps/judicial-system/backend/src/app/formatters/formatters.ts +++ b/apps/judicial-system/backend/src/app/formatters/formatters.ts @@ -594,22 +594,17 @@ export const formatDefenderRevokedEmailNotification = ( ?.replace(' kl.', ', kl.') : 'NONE', }) - const revokedText = isIndictmentCase(type) - ? formatMessage(cf.revokedIndictment, { - courtText, - courtDateText, - }) - : formatMessage(cf.revoked, { - courtText, - courtDateText, - investigationPrefix: - type === CaseType.OTHER - ? 'onlyPrefix' - : isInvestigationCase(type) - ? 'withPrefix' - : 'noPrefix', - courtTypeName: formatCaseType(type), - }) + const revokedText = formatMessage(cf.revoked, { + courtText, + courtDateText, + investigationPrefix: + type === CaseType.OTHER + ? 'onlyPrefix' + : isInvestigationCase(type) + ? 'withPrefix' + : 'noPrefix', + courtTypeName: formatCaseType(type), + }) const defendantNationalIdText = defendantNoNationalId ? defendantNationalId || 'NONE' : formatNationalId(defendantNationalId || 'NONE') @@ -618,7 +613,7 @@ export const formatDefenderRevokedEmailNotification = ( defendantNationalId: defendantNationalIdText, defendantNoNationalId: defendantNoNationalId ? 'NONE' : 'SOME', }) - const defenderAssignedText = formatMessage(cf.defenderAssignedV2) + const defenderAssignedText = formatMessage(cf.defenderAssigned) return formatMessage(cf.body, { revokedText, diff --git a/apps/judicial-system/backend/src/app/messages/notifications.ts b/apps/judicial-system/backend/src/app/messages/notifications.ts index af2339b4fc38..c64652bb07d0 100644 --- a/apps/judicial-system/backend/src/app/messages/notifications.ts +++ b/apps/judicial-system/backend/src/app/messages/notifications.ts @@ -529,13 +529,6 @@ export const notifications = { description: 'Texti í pósti til verjanda/talsmanns sem tilgreinir að krafa sé afturkölluð', }, - revokedIndictment: { - id: 'judicial.system.backend:notifications.defender_revoked_email.revoked_indictment', - defaultMessage: - 'Ákæra sem taka átti fyrir hjá {courtText} {courtDateText}, hefur verið afturkölluð.', - description: - 'Texti í pósti til verjanda/talsmanns sem tilgreinir að ákæra sé afturkölluð', - }, defendant: { id: 'judicial.system.backend:notifications.defender_revoked_email.defendant', defaultMessage: @@ -543,9 +536,10 @@ export const notifications = { description: 'Texti í pósti til verjanda/talsmanns sem tilgreinir sakborning', }, - defenderAssignedV2: { - id: 'judicial.system.backend:notifications.defender_revoked_email.defender_assigned_v2', - defaultMessage: 'Dómstóllinn hafði skráð þig sem verjanda í málinu.', + defenderAssigned: { + id: 'judicial.system.backend:notifications.defender_revoked_email.defender_assigned_v3', + defaultMessage: + 'Dómstóllinn hafði skráð þig sem verjanda/talsmann í málinu.', description: 'Texti í pósti til verjanda/talsmanns sem tilgreinir að viðkomandi sé skráður verjandi', }, @@ -563,11 +557,17 @@ export const notifications = { description: 'Fyrirsögn í pósti til verjanda/talsmanns þegar krafa er afturkölluð', }, - indictmentSubject: { - id: 'judicial.system.backend:notifications.defender_revoked_email.indictment_subject', - defaultMessage: 'Ákæra afturkölluð', + indictmentBody: { + id: 'judicial.system.backend:notifications.defender_revoked_email.body_indictment', + defaultMessage: + 'Dómstóllinn hafði skráð þig sem verjanda í málinu.

{defenderHasAccessToRvg, select, true {Sjá nánar á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}} other {Þú getur nálgast gögn málsins hjá {courtName} ef þau hafa ekki þegar verið afhent}}.', description: - 'Fyrirsögn í pósti til verjanda/talsmanns þegar ákæra er afturkölluð', + 'Notaður sem beinagrind á pósti til verjanda þegar ákæra er afturkölluð', + }, + indictmentSubject: { + id: 'judicial.system.backend:notifications.defender_revoked_email.indictment_subject_v2', + defaultMessage: 'Ákæra afturkölluð í máli {courtCaseNumber}', + description: 'Fyrirsögn í pósti til verjanda þegar ákæra er afturkölluð', }, }), modified: defineMessages({ @@ -586,9 +586,9 @@ export const notifications = { 'Notaður sem texti í tölvupósti vegna breytingar á lengd gæslu/farbanns/vistunar þar sem ekki var úrskurðað í einangrun.', }, htmlDefender: { - id: 'judicial.system.backend:notifications.modified.html_defender', + id: 'judicial.system.backend:notifications.modified.html_defender_v2', defaultMessage: - '{actorInstitution}, {actorName} {actorTitle}, hefur uppfært lengd {caseType, select, ADMISSION_TO_FACILITY {vistunar} TRAVEL_BAN {farbanns} other {gæsluvarðhalds}} í máli {courtCaseNumber}.

{defenderHasAccessToRvg, select, true {Sjá nánar á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}} other {Þú getur nálgast gögn málsins hjá {courtName} ef þau hafa ekki þegar verið afhent}}.

Ný lokadagsetning: {validToDate}.', + '{actorInstitution}, {actorName} {actorTitle}, hefur uppfært lengd {caseType, select, ADMISSION_TO_FACILITY {vistunar} TRAVEL_BAN {farbanns} other {gæsluvarðhalds}} í máli {courtCaseNumber}.

{defenderHasAccessToRvg, select, true {Sjá nánar á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}} other {Þú getur nálgast gögn málsins hjá {courtName} ef þau hafa ekki þegar verið afhent}}.

Ný lokadagsetning: {validToDate}.', description: 'Notaður sem texti í tölvupósti til verjanda vegna breytingar á lengd gæslu/farbanns/vistunar þar sem ekki var úrskurðað í einangrun.', }, @@ -769,7 +769,6 @@ export const notifications = { description: 'Texti í pósti til aðila máls þegar kæra er afturkölluð', }, }), - emailNames: defineMessages({ prison: { id: 'judicial.system.backend:notifications.email_names.prison', @@ -834,4 +833,18 @@ export const notifications = { description: 'Texti í pósti til sækjanda máls þegar ákæra er endursend', }, }), + courtRevokedIndictmentEmail: defineMessages({ + subject: { + id: 'judicial.system.backend:notifications.court_revoked_indictment_email.subject', + defaultMessage: + 'Ákæra afturkölluð{courtCaseNumber, select, NONE {} other { í máli {courtCaseNumber}}}', + description: 'Fyrirsögn í pósti til dómstóls þegar ákæra er afturkölluð', + }, + body: { + id: 'judicial.system.backend:notifications.court_revoked_indictment_email.body', + defaultMessage: + '{prosecutorsOffice} hefur afturkallað ákæru {courtCaseNumber, select, NONE {í máli sem ekki hefur enn fengið málsnúmer} other {í máli {courtCaseNumber}}}.', + description: 'Texti í pósti til dómstóls þegar ákæra er afturkölluð', + }, + }), } diff --git a/apps/judicial-system/backend/src/app/modules/case/case.service.ts b/apps/judicial-system/backend/src/app/modules/case/case.service.ts index a6b0f5b397b5..90531b9eae0d 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.service.ts @@ -835,11 +835,8 @@ export class CaseService { return this.messageService.sendMessagesToQueue(messages) } - private addMessagesForDeletedCaseToQueue( - theCase: Case, - user: TUser, - ): Promise { - const messages: CaseMessage[] = [ + private getRevokeMessages(user: TUser, theCase: Case): CaseMessage[] { + return [ { type: MessageType.NOTIFICATION, user, @@ -847,8 +844,24 @@ export class CaseService { body: { type: NotificationType.REVOKED }, }, ] + } - return this.messageService.sendMessagesToQueue(messages) + private addMessagesForDeletedCaseToQueue( + theCase: Case, + user: TUser, + ): Promise { + return this.messageService.sendMessagesToQueue( + this.getRevokeMessages(user, theCase), + ) + } + + private addMessagesForRevokedIndictmentCaseToQueue( + theCase: Case, + user: TUser, + ): Promise { + return this.messageService.sendMessagesToQueue( + this.getRevokeMessages(user, theCase), + ) } private async addMessagesForAppealedCaseToQueue( @@ -1077,6 +1090,7 @@ export class CaseService { user: TUser, ): Promise { const isIndictment = isIndictmentCase(updatedCase.type) + if (updatedCase.state !== theCase.state) { // New case state if ( @@ -1086,13 +1100,17 @@ export class CaseService { // Only send messages if the case was in a SUBMITTED state - not when reopening a case await this.addMessagesForReceivedCaseToQueue(updatedCase, user) } else if (updatedCase.state === CaseState.DELETED) { - await this.addMessagesForDeletedCaseToQueue(updatedCase, user) + if (!isIndictment) { + await this.addMessagesForDeletedCaseToQueue(updatedCase, user) + } } else if (isCompletedCase(updatedCase.state)) { if (isIndictment) { - await this.addMessagesForCompletedIndictmentCaseToQueue( - updatedCase, - user, - ) + if (theCase.state !== CaseState.WAITING_FOR_CANCELLATION) { + await this.addMessagesForCompletedIndictmentCaseToQueue( + updatedCase, + user, + ) + } } else { await this.addMessagesForCompletedCaseToQueue(updatedCase, user) } @@ -1116,6 +1134,8 @@ export class CaseService { updatedCase, user, ) + } else if (updatedCase.state === CaseState.WAITING_FOR_CANCELLATION) { + await this.addMessagesForRevokedIndictmentCaseToQueue(updatedCase, user) } } diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/transition.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/transition.spec.ts index 2a81eeddc219..a1098a85ef53 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/transition.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/transition.spec.ts @@ -401,7 +401,10 @@ describe('CaseController - Transition', () => { }, ], ) - } else if (newState === CaseState.DELETED) { + } else if ( + newState === CaseState.DELETED && + !isIndictmentCase(theCase.type) + ) { expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith( [ { @@ -505,6 +508,23 @@ describe('CaseController - Transition', () => { }, ], ) + } else if ( + newState === CaseState.WAITING_FOR_CANCELLATION && + isIndictmentCase(theCase.type) + ) { + expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith( + [ + { + type: MessageType.NOTIFICATION, + user: { + ...defaultUser, + canConfirmIndictment: isIndictmentCase(theCase.type), + }, + caseId, + body: { type: NotificationType.REVOKED }, + }, + ], + ) } else { expect( mockMessageService.sendMessagesToQueue, diff --git a/apps/judicial-system/backend/src/app/modules/notification/notification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/notification.service.ts index 13e3ee13cc38..4764814ed4cd 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/notification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/notification.service.ts @@ -46,6 +46,7 @@ import { isIndictmentCase, isInvestigationCase, isProsecutionUser, + isRequestCase, isRestrictionCase, NotificationType, RequestSharedWithDefender, @@ -130,25 +131,26 @@ export class NotificationService { }) } - private async hasSentNotification(caseId: string, type: NotificationType) { - const previousNotifications = await this.notificationModel.findAll({ - where: { caseId, type }, - }) - return previousNotifications.length > 0 + private hasSentNotification( + type: NotificationType, + notifications?: Notification[], + ) { + return notifications?.some((notification) => notification.type === type) } - private async hasReceivedNotification( - caseId: string, - type: NotificationType | NotificationType[], + private hasReceivedNotification( + type?: NotificationType | NotificationType[], address?: string, + notifications?: Notification[], ) { - const previousNotifications = await this.notificationModel.findAll({ - where: { caseId, type }, - }) + const types = type ? [type].flat() : Object.values(NotificationType) - return previousNotifications.some((notification) => { - return notification.recipients.some( - (recipient) => recipient.address === address && recipient.success, + return notifications?.some((notification) => { + return ( + types.includes(notification.type) && + notification.recipients.some( + (recipient) => recipient.address === address && recipient.success, + ) ) }) } @@ -425,7 +427,7 @@ export class NotificationService { theCase.defenderName, theCase.defenderEmail, undefined, - Boolean(theCase.defenderNationalId) === false, + !theCase.defenderNationalId, ) } @@ -486,7 +488,7 @@ export class NotificationService { theCase.defenderName, theCase.defenderEmail, undefined, - Boolean(theCase.defenderNationalId) === false, + !theCase.defenderNationalId, ) } @@ -509,10 +511,10 @@ export class NotificationService { this.sendReadyForCourtEmailNotificationToProsecutor(theCase), ] - const courtHasBeenNotified = await this.hasReceivedNotification( - theCase.id, + const courtHasBeenNotified = this.hasReceivedNotification( NotificationType.READY_FOR_COURT, this.getCourtMobileNumbers(theCase.courtId), + theCase.notifications, ) if (!courtHasBeenNotified) { @@ -528,10 +530,10 @@ export class NotificationService { RequestSharedWithDefender.READY_FOR_COURT || theCase.requestSharedWithDefender === RequestSharedWithDefender.COURT_DATE ) { - const hasDefenderBeenNotified = await this.hasReceivedNotification( - theCase.id, + const hasDefenderBeenNotified = this.hasReceivedNotification( [NotificationType.READY_FOR_COURT, NotificationType.COURT_DATE], theCase.defenderEmail, + theCase.notifications, ) if (hasDefenderBeenNotified) { @@ -727,7 +729,7 @@ export class NotificationService { theCase.defenderName, theCase.defenderEmail, calendarInvite ? [calendarInvite] : undefined, - Boolean(theCase.defenderNationalId) === false, + !theCase.defenderNationalId, ).then((recipient) => { if (recipient.success) { // No need to wait @@ -773,7 +775,7 @@ export class NotificationService { theCase.defenderName, theCase.defenderEmail, undefined, - Boolean(theCase.defenderNationalId) === false, + !theCase.defenderNationalId, ) } @@ -910,10 +912,10 @@ export class NotificationService { ), ) - const hasDefenderBeenNotified = await this.hasReceivedNotification( - theCase.id, + const hasDefenderBeenNotified = this.hasReceivedNotification( [NotificationType.READY_FOR_COURT], theCase.defenderEmail, + theCase.notifications, ) if (!hasDefenderBeenNotified) { @@ -1181,10 +1183,10 @@ export class NotificationService { (theCase.decision === CaseDecision.REJECTING || theCase.decision === CaseDecision.ACCEPTING_ALTERNATIVE_TRAVEL_BAN) ) { - const prisonHasBeenNotified = await this.hasReceivedNotification( - theCase.id, + const prisonHasBeenNotified = this.hasReceivedNotification( NotificationType.COURT_DATE, this.config.email.prisonEmail, + theCase.notifications, ) if (prisonHasBeenNotified) { @@ -1245,7 +1247,7 @@ export class NotificationService { theCase.defenderName, theCase.defenderEmail, undefined, - Boolean(theCase.defenderNationalId) === false, + !theCase.defenderNationalId, ) } @@ -1354,24 +1356,6 @@ export class NotificationService { //#endregion //#region REVOKED notifications */ - private async existsRevokableNotification( - caseId: string, - address?: string, - isIndictment?: boolean, - ): Promise { - return this.hasReceivedNotification( - caseId, - isIndictment - ? [NotificationType.DEFENDER_ASSIGNED] - : [ - NotificationType.HEADS_UP, - NotificationType.READY_FOR_COURT, - NotificationType.COURT_DATE, - ], - address, - ) - } - private sendRevokedSmsNotificationToCourt(theCase: Case): Promise { const smsText = formatCourtRevokedSmsNotification( this.formatMessage, @@ -1409,10 +1393,12 @@ export class NotificationService { html, this.formatMessage(notifications.emailNames.prison), this.config.email.prisonEmail, + undefined, + true, ) } - private sendRevokedEmailNotificationToDefender( + private sendRevokedEmailNotificationToDefenderForRequestCase( caseType: CaseType, defendant: Defendant, defenderName?: string, @@ -1420,11 +1406,10 @@ export class NotificationService { arraignmentDate?: Date, courtName?: string, ): Promise { - const subject = isIndictmentCase(caseType) - ? this.formatMessage(notifications.defenderRevokedEmail.indictmentSubject) - : this.formatMessage(notifications.defenderRevokedEmail.subject, { - caseType, - }) + const subject = this.formatMessage( + notifications.defenderRevokedEmail.subject, + { caseType }, + ) const html = formatDefenderRevokedEmailNotification( this.formatMessage, @@ -1436,74 +1421,190 @@ export class NotificationService { arraignmentDate, ) - return this.sendEmail(subject, html, defenderName, defenderEmail) + return this.sendEmail( + subject, + html, + defenderName, + defenderEmail, + undefined, + true, + ) } - private async sendRevokedNotifications( + private sendRevokedEmailNotificationToDefenderForIndictmentCase( + caseId: string, + defenderNationalId?: string, + defenderName?: string, + defenderEmail?: string, + courtName?: string, + courtCaseNumber?: string, + ): Promise { + const subject = this.formatMessage( + notifications.defenderRevokedEmail.indictmentSubject, + { courtCaseNumber }, + ) + + const html = this.formatMessage( + notifications.defenderRevokedEmail.indictmentBody, + { + courtName: courtName?.replace('dómur', 'dómi'), + defenderHasAccessToRvg: Boolean(defenderNationalId), + linkStart: ``, + linkEnd: '', + }, + ) + + return this.sendEmail( + subject, + html, + defenderName, + defenderEmail, + undefined, + !defenderNationalId, + ) + } + + private async sendRevokedNotificationsForRequestCase( theCase: Case, ): Promise { const promises: Promise[] = [] - const arraignmentDate = DateLog.arraignmentDate(theCase.dateLogs)?.date - const courtWasNotified = - !isIndictmentCase(theCase.type) && - (await this.existsRevokableNotification( - theCase.id, - this.getCourtMobileNumbers(theCase.courtId), - )) + const courtWasNotified = this.hasReceivedNotification( + undefined, + this.getCourtMobileNumbers(theCase.courtId), + theCase.notifications, + ) if (courtWasNotified) { promises.push(this.sendRevokedSmsNotificationToCourt(theCase)) } - const prisonWasNotified = - (theCase.type === CaseType.CUSTODY || - theCase.type === CaseType.ADMISSION_TO_FACILITY) && - (await this.existsRevokableNotification( - theCase.id, - this.config.email.prisonEmail, - )) + const prisonWasNotified = this.hasReceivedNotification( + undefined, + this.config.email.prisonEmail, + theCase.notifications, + ) if (prisonWasNotified) { promises.push(this.sendRevokedEmailNotificationToPrison(theCase)) } - if (isIndictmentCase(theCase.type)) { - for (const defendant of theCase.defendants ?? []) { - const defenderWasNotified = await this.existsRevokableNotification( - theCase.id, - defendant.defenderEmail, - isIndictmentCase(theCase.type), + const defenderWasNotified = this.hasReceivedNotification( + undefined, + theCase.defenderEmail, + theCase.notifications, + ) + + if (defenderWasNotified && theCase.defendants) { + const arraignmentDate = DateLog.arraignmentDate(theCase.dateLogs)?.date + + promises.push( + this.sendRevokedEmailNotificationToDefenderForRequestCase( + theCase.type, + theCase.defendants[0], + theCase.defenderName, + theCase.defenderEmail, + arraignmentDate, + theCase.court?.name, + ), + ) + } + + const recipients = await Promise.all(promises) + + if (recipients.length === 0) { + // Nothing to send + return { notificationSent: true } + } + + return this.recordNotification( + theCase.id, + NotificationType.REVOKED, + recipients, + ) + } + + private sendRevokedNotificationToCourt( + theCase: Case, + recipientName?: string, + recipientEmail?: string, + ): Promise { + const subject = this.formatMessage( + notifications.courtRevokedIndictmentEmail.subject, + { + courtCaseNumber: theCase.courtCaseNumber ?? 'NONE', + }, + ) + const body = this.formatMessage( + notifications.courtRevokedIndictmentEmail.body, + { + prosecutorsOffice: theCase.creatingProsecutor?.institution?.name, + courtCaseNumber: theCase.courtCaseNumber ?? 'NONE', + }, + ) + + return this.sendEmail(subject, body, recipientName, recipientEmail) + } + + private async sendRevodeNotificationsForIndictmentCase( + theCase: Case, + ): Promise { + const promises: Promise[] = [] + + if (!theCase.judge && !theCase.registrar) { + promises.push( + this.sendRevokedNotificationToCourt( + theCase, + theCase.court?.name, + this.getCourtEmail(theCase.courtId), + ), + ) + } else { + if (theCase.judge) { + promises.push( + this.sendRevokedNotificationToCourt( + theCase, + theCase.judge.name, + theCase.judge.email, + ), ) + } - if (defenderWasNotified) { - promises.push( - this.sendRevokedEmailNotificationToDefender( - theCase.type, - defendant, - defendant.defenderName, - defendant.defenderEmail, - arraignmentDate, - theCase.court?.name, - ), - ) - } + if (theCase.registrar) { + promises.push( + this.sendRevokedNotificationToCourt( + theCase, + theCase.registrar.name, + theCase.registrar.email, + ), + ) } - } else { - const defenderWasNotified = await this.existsRevokableNotification( - theCase.id, - theCase.defenderEmail, - isIndictmentCase(theCase.type), + } + + const uniqDefendants = _uniqBy( + theCase.defendants ?? [], + (d: Defendant) => d.defenderEmail, + ) + for (const defendant of uniqDefendants) { + const defenderWasNotified = this.hasReceivedNotification( + undefined, + defendant.defenderEmail, + theCase.notifications, ) - if (defenderWasNotified && theCase.defendants) { + + if (defenderWasNotified) { promises.push( - this.sendRevokedEmailNotificationToDefender( - theCase.type, - theCase.defendants[0], - theCase.defenderName, - theCase.defenderEmail, - arraignmentDate, + this.sendRevokedEmailNotificationToDefenderForIndictmentCase( + theCase.id, + defendant.defenderNationalId, + defendant.defenderName, + defendant.defenderEmail, theCase.court?.name, + theCase.courtCaseNumber, ), ) } @@ -1522,21 +1623,31 @@ export class NotificationService { recipients, ) } + + private sendRevokedNotifications( + theCase: Case, + ): Promise { + if (isRequestCase(theCase.type)) { + return this.sendRevokedNotificationsForRequestCase(theCase) + } else { + return this.sendRevodeNotificationsForIndictmentCase(theCase) + } + } //#endregion //#region DEFENDER_ASSIGNED notifications */ - private async shouldSendDefenderAssignedNotification( + private shouldSendDefenderAssignedNotification( theCase: Case, defenderEmail?: string, - ): Promise { + ): boolean { if (!defenderEmail) { return false } if (isIndictmentCase(theCase.type)) { - const hasSentNotificationBefore = await this.hasReceivedNotification( - theCase.id, + const hasSentNotificationBefore = this.hasReceivedNotification( NotificationType.DEFENDER_ASSIGNED, defenderEmail, + theCase.notifications, ) if (hasSentNotificationBefore) { @@ -1552,15 +1663,16 @@ export class NotificationService { if (!isDefenderIncludedInSessionArrangements) return false } else { - const hasDefenderBeenNotified = await this.hasReceivedNotification( - theCase.id, + const hasDefenderBeenNotified = this.hasReceivedNotification( [ NotificationType.READY_FOR_COURT, NotificationType.COURT_DATE, NotificationType.DEFENDER_ASSIGNED, ], theCase.defenderEmail, + theCase.notifications, ) + if (hasDefenderBeenNotified) { return false } @@ -1605,7 +1717,7 @@ export class NotificationService { for (const defendant of uniqDefendants) { const { defenderEmail, defenderNationalId, defenderName } = defendant - const shouldSend = await this.shouldSendDefenderAssignedNotification( + const shouldSend = this.shouldSendDefenderAssignedNotification( theCase, defenderEmail, ) @@ -1622,21 +1734,13 @@ export class NotificationService { } } } else if (DateLog.arraignmentDate(theCase.dateLogs)?.date) { - const shouldSend = await this.shouldSendDefenderAssignedNotification( + const shouldSend = this.shouldSendDefenderAssignedNotification( theCase, theCase.defenderEmail, ) if (shouldSend) { - const recipient = await this.sendCourtDateEmailNotificationToDefender( - theCase, - ) - - return this.recordNotification( - theCase.id, - NotificationType.DEFENDER_ASSIGNED, - [recipient], - ) + promises.push(this.sendCourtDateEmailNotificationToDefender(theCase)) } } @@ -1661,11 +1765,11 @@ export class NotificationService { ): Promise { if ( !theCase.registrar || - (await this.hasReceivedNotification( - theCase.id, + this.hasReceivedNotification( NotificationType.DEFENDANTS_NOT_UPDATED_AT_COURT, theCase.registrar?.email, - )) + theCase.notifications, + ) ) { // Nothing to send return { notificationSent: true } @@ -1715,8 +1819,6 @@ export class NotificationService { html, theCase.prosecutor?.name, theCase.prosecutor?.email, - undefined, - true, ) return this.recordNotification( @@ -1749,8 +1851,6 @@ export class NotificationService { html, theCase.prosecutor?.name, theCase.prosecutor?.email, - undefined, - true, ) return this.recordNotification( @@ -1889,7 +1989,7 @@ export class NotificationService { theCase.defenderName, theCase.defenderEmail, undefined, - Boolean(theCase.defenderNationalId) === false, + !theCase.defenderNationalId, ), ) } @@ -1996,7 +2096,7 @@ export class NotificationService { theCase.defenderName, theCase.defenderEmail, undefined, - Boolean(theCase.defenderNationalId) === false, + !theCase.defenderNationalId, ), ) } @@ -2130,7 +2230,7 @@ export class NotificationService { theCase.defenderName, theCase.defenderEmail, undefined, - Boolean(theCase.defenderNationalId) === false, + !theCase.defenderNationalId, ), ) } @@ -2185,14 +2285,7 @@ export class NotificationService { courtOfAppealUsers.forEach((user) => { if (user) { promises.push( - this.sendEmail( - subject, - courtOfAppealHtml, - user.name, - user.email, - undefined, - true, - ), + this.sendEmail(subject, courtOfAppealHtml, user.name, user.email), ) } }) @@ -2237,9 +2330,9 @@ export class NotificationService { private async sendAppealCompletedResultNotifications( theCase: Case, ): Promise { - const isReopened = await this.hasSentNotification( - theCase.id, + const isReopened = this.hasSentNotification( NotificationType.APPEAL_COMPLETED, + theCase.notifications, ) const promises = [] @@ -2338,7 +2431,7 @@ export class NotificationService { theCase.defenderName, theCase.defenderEmail, undefined, - Boolean(theCase.defenderNationalId) === false, + !theCase.defenderNationalId, ), ) } @@ -2376,7 +2469,7 @@ export class NotificationService { theCase.defenderName, theCase.defenderEmail, undefined, - Boolean(theCase.defenderNationalId) === false, + !theCase.defenderNationalId, ), ) @@ -2427,7 +2520,9 @@ export class NotificationService { courtCaseNumber: theCase.courtCaseNumber, }) - const sendTo = await this.getWithdrawnNotificationRecipients( + // This may result in a defender with no national id getting a link to RVG + // TODO: Separate defenders from other recipients and handle no national id + const sendTo = this.getWithdrawnNotificationRecipients( theCase, user, wasWithdrawnByProsecution, @@ -2440,6 +2535,7 @@ export class NotificationService { }) const recipients = await Promise.all(promises) + return this.recordNotification( theCase.id, NotificationType.APPEAL_WITHDRAWN, @@ -2447,14 +2543,14 @@ export class NotificationService { ) } - private async getWithdrawnNotificationRecipients( + private getWithdrawnNotificationRecipients( theCase: Case, user: User, wasWithdrawnByProsecution: boolean, - ): Promise { - const hasBeenAssigned = await this.hasSentNotification( - theCase.id, + ): RecipientInfo[] { + const hasBeenAssigned = this.hasSentNotification( NotificationType.APPEAL_JUDGES_ASSIGNED, + theCase.notifications, ) const recipients = [ diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/createTestingNotificationModule.ts b/apps/judicial-system/backend/src/app/modules/notification/test/createTestingNotificationModule.ts index 0909a33faa6f..11983dbe9cc7 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/createTestingNotificationModule.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/createTestingNotificationModule.ts @@ -81,9 +81,6 @@ export const createTestingNotificationModule = async () => { provide: getModelToken(Notification), useValue: { create: jest.fn(), - findOne: jest.fn(), - findAll: jest.fn(), - update: jest.fn(), }, }, NotificationService, diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealCompletedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealCompletedNotifications.spec.ts index 4012c218e6d0..88b1e6ab7f73 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealCompletedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealCompletedNotifications.spec.ts @@ -49,19 +49,12 @@ describe('InternalNotificationController - Send appeal completed notifications', beforeEach(async () => { process.env.COURTS_EMAILS = `{"4676f08b-aab4-4b4f-a366-697540788088":"${courtOfAppealsEmail}"}` - const { - emailService, - notificationConfig, - notificationModel, - internalNotificationController, - } = await createTestingNotificationModule() + const { emailService, notificationConfig, internalNotificationController } = + await createTestingNotificationModule() mockEmailService = emailService mockConfig = notificationConfig - const mockFindAll = notificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValue([]) - givenWhenThen = async ( defenderNationalId?: string, appealRulingDecision?: CaseAppealRulingDecision, diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealWithdrawnNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealWithdrawnNotifications.spec.ts index 141d4cc38a4a..1d83fc0d0db8 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealWithdrawnNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealWithdrawnNotifications.spec.ts @@ -23,6 +23,7 @@ interface Then { type GivenWhenThen = ( userRole: UserRole, appealReceivedByCourtDate?: Date, + notifications?: Notification[], ) => Promise describe('InternalNotificationController - Send appeal withdrawn notifications', () => { @@ -60,10 +61,11 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', mockEmailService = emailService mockNotificationModel = notificationModel - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValue([]) - - givenWhenThen = async (userRole, appealReceivedByCourtDate) => { + givenWhenThen = async ( + userRole, + appealReceivedByCourtDate, + notifications, + ) => { const then = {} as Then await internalNotificationController @@ -88,6 +90,7 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', judge: { name: judgeName, email: judgeEmail }, appealJudge1: { name: appealJudge1Name, email: appealJudge1Email }, registrar: { name: registrarName, email: registrarEmail }, + notifications, } as Case, { user: { @@ -151,15 +154,13 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', beforeEach(async () => { const mockCreate = mockNotificationModel.create as jest.Mock mockCreate.mockResolvedValueOnce({} as Notification) - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValueOnce([ + + then = await givenWhenThen(UserRole.PROSECUTOR, receivedDate, [ { caseId, type: NotificationType.APPEAL_JUDGES_ASSIGNED, } as Notification, ]) - - then = await givenWhenThen(UserRole.PROSECUTOR, receivedDate) }) it('should send notification to defender', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCourtDateNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCourtDateNotifications.spec.ts index e1e092ff174e..fc497ddc5de5 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCourtDateNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCourtDateNotifications.spec.ts @@ -49,9 +49,6 @@ describe('InternalNotificationController - Send court date notifications', () => mockEmailService = emailService mockNotificationModel = notificationModel - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValue([]) - givenWhenThen = async ( theCase: Case, notificationDto: SendInternalNotificationDto, @@ -86,9 +83,6 @@ describe('InternalNotificationController - Send court date notifications', () => } as Case beforeEach(async () => { - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValueOnce([]) - then = await givenWhenThen(theCase, notificationDto) }) @@ -141,16 +135,20 @@ describe('InternalNotificationController - Send court date notifications', () => beforeEach(async () => { const mockCreate = mockNotificationModel.create as jest.Mock mockCreate.mockResolvedValueOnce({} as Notification) - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValueOnce([ - { - caseId, - type: NotificationType.READY_FOR_COURT, - recipients: [{ address: defenderEmail, success: true }], - } as Notification, - ]) - then = await givenWhenThen(theCase, notificationDto) + then = await givenWhenThen( + { + ...theCase, + notifications: [ + { + caseId, + type: NotificationType.READY_FOR_COURT, + recipients: [{ address: defenderEmail, success: true }], + }, + ], + } as Case, + notificationDto, + ) }) it('should not send link to case to defender', () => { @@ -190,9 +188,6 @@ describe('InternalNotificationController - Send court date notifications', () => } as Case beforeEach(async () => { - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValueOnce([]) - then = await givenWhenThen(theCase, notificationDto) }) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefendantsNotUpdatedAtCourtNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefendantsNotUpdatedAtCourtNotifications.spec.ts index ad2941570d7d..6e98046a7a9b 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefendantsNotUpdatedAtCourtNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefendantsNotUpdatedAtCourtNotifications.spec.ts @@ -9,7 +9,6 @@ import { createTestingNotificationModule } from '../createTestingNotificationMod import { Case } from '../../../case' import { SendInternalNotificationDto } from '../../dto/sendInternalNotification.dto' import { DeliverResponse } from '../../models/deliver.response' -import { Notification } from '../../models/notification.model' interface Then { result: DeliverResponse @@ -39,18 +38,13 @@ describe('InternalNotificationController - Send defendants not updated at court } as Case let mockEmailService: EmailService - let mockNotificationModel: typeof Notification let givenWhenThen: GivenWhenThen beforeEach(async () => { - const { emailService, notificationModel, internalNotificationController } = + const { emailService, internalNotificationController } = await createTestingNotificationModule() mockEmailService = emailService - mockNotificationModel = notificationModel - - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValue([]) givenWhenThen = async ( caseId: string, @@ -91,11 +85,19 @@ describe('InternalNotificationController - Send defendants not updated at court let then: Then beforeEach(async () => { - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValueOnce([ - { recipients: [{ address: registrarEmail, success: true }] }, - ]) - then = await givenWhenThen(caseId, theCase, notificationDto) + then = await givenWhenThen( + caseId, + { + ...theCase, + notifications: [ + { + type: NotificationType.DEFENDANTS_NOT_UPDATED_AT_COURT, + recipients: [{ address: registrarEmail, success: true }], + }, + ], + } as Case, + notificationDto, + ) }) it('should not send email', () => { diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefenderAssignedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefenderAssignedNotifications.spec.ts index 81dcac2bfade..cdefc359ef4c 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefenderAssignedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefenderAssignedNotifications.spec.ts @@ -56,9 +56,6 @@ describe('InternalNotificationController - Send defender assigned notifications' mockConfig = notificationConfig mockNotificationModel = notificationModel - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValue([]) - givenWhenThen = async ( caseId: string, theCase: Case, @@ -241,16 +238,21 @@ describe('InternalNotificationController - Send defender assigned notifications' beforeEach(async () => { const mockCreate = mockNotificationModel.create as jest.Mock mockCreate.mockResolvedValueOnce({} as Notification) - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValueOnce([ - { - caseId, - type: notificationDto.type, - recipients: [{ address: defendant.defenderEmail, success: true }], - } as Notification, - ]) - then = await givenWhenThen(caseId, theCase, notificationDto) + then = await givenWhenThen( + caseId, + { + ...theCase, + notifications: [ + { + caseId, + type: notificationDto.type, + recipients: [{ address: defendant.defenderEmail, success: true }], + }, + ], + } as Case, + notificationDto, + ) }) it('should return notification was not sent', () => { diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentDeniedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentDeniedNotifications.spec.ts index b75d942d4755..6e6463038ad4 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentDeniedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentDeniedNotifications.spec.ts @@ -13,7 +13,6 @@ import { createTestingNotificationModule } from '../createTestingNotificationMod import { Case } from '../../../case' import { SendInternalNotificationDto } from '../../dto/sendInternalNotification.dto' import { DeliverResponse } from '../../models/deliver.response' -import { Notification } from '../../models/notification.model' jest.mock('../../../../factories') @@ -35,18 +34,13 @@ describe('InternalNotificationController - Send indictment denied notification', const policeCaseNumbers = [uuid(), uuid()] let mockEmailService: EmailService - let mockNotificationModel: typeof Notification let givenWhenThen: GivenWhenThen beforeEach(async () => { - const { emailService, internalNotificationController, notificationModel } = + const { emailService, internalNotificationController } = await createTestingNotificationModule() mockEmailService = emailService - mockNotificationModel = notificationModel - - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValue([]) givenWhenThen = async ( theCase: Case, @@ -79,9 +73,6 @@ describe('InternalNotificationController - Send indictment denied notification', } as Case beforeEach(async () => { - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValueOnce([]) - then = await givenWhenThen(theCase, notificationDto) }) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentReturnedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentReturnedNotifications.spec.ts index 86ba67ab84f4..4c5737885fb1 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentReturnedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentReturnedNotifications.spec.ts @@ -13,7 +13,6 @@ import { createTestingNotificationModule } from '../createTestingNotificationMod import { Case } from '../../../case' import { SendInternalNotificationDto } from '../../dto/sendInternalNotification.dto' import { DeliverResponse } from '../../models/deliver.response' -import { Notification } from '../../models/notification.model' jest.mock('../../../../factories') @@ -36,18 +35,13 @@ describe('InternalNotificationController - Send indictment returned notification const courtName = uuid() let mockEmailService: EmailService - let mockNotificationModel: typeof Notification let givenWhenThen: GivenWhenThen beforeEach(async () => { - const { emailService, internalNotificationController, notificationModel } = + const { emailService, internalNotificationController } = await createTestingNotificationModule() mockEmailService = emailService - mockNotificationModel = notificationModel - - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValue([]) givenWhenThen = async ( theCase: Case, @@ -81,9 +75,6 @@ describe('InternalNotificationController - Send indictment returned notification } as Case beforeEach(async () => { - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValueOnce([]) - then = await givenWhenThen(theCase, notificationDto) }) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendReadyForCourtNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendReadyForCourtNotifications.spec.ts index 34d47928b101..df061d7eb038 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendReadyForCourtNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendReadyForCourtNotifications.spec.ts @@ -74,7 +74,6 @@ describe('InternalNotificationController - Send ready for court notifications fo let mockEmailService: EmailService let mockSmsService: SmsService let mockNotificationConfig: ConfigType - let mockNotificationModel: typeof Notification let givenWhenThen: GivenWhenThen beforeEach(async () => { @@ -84,17 +83,12 @@ describe('InternalNotificationController - Send ready for court notifications fo emailService, smsService, notificationConfig, - notificationModel, internalNotificationController, } = await createTestingNotificationModule() mockEmailService = emailService mockSmsService = smsService mockNotificationConfig = notificationConfig - mockNotificationModel = notificationModel - - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValue([]) givenWhenThen = async (caseId, theCase, notificationDto) => { const then = {} as Then @@ -115,12 +109,6 @@ describe('InternalNotificationController - Send ready for court notifications fo then = await givenWhenThen(caseId, theCase, notificationDto) }) - it('should lookup previous ready for court notifications', () => { - expect(mockNotificationModel.findAll).toHaveBeenCalledWith({ - where: { caseId, type: NotificationType.READY_FOR_COURT }, - }) - }) - it('should send ready for court email notification to prosecutor', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith({ from: { @@ -153,21 +141,26 @@ describe('InternalNotificationController - Send ready for court notifications fo describe('subsequent notifications', () => { beforeEach(async () => { - const mockFindOne = mockNotificationModel.findAll as jest.Mock - mockFindOne.mockResolvedValueOnce([ + await givenWhenThen( + caseId, { - caseId, - type: NotificationType.READY_FOR_COURT, - recipients: [ + ...theCase, + notifications: [ { - address: mockNotificationConfig.sms.courtsMobileNumbers[courtId], - success: true, + caseId, + type: NotificationType.READY_FOR_COURT, + recipients: [ + { + address: + mockNotificationConfig.sms.courtsMobileNumbers[courtId], + success: true, + }, + ], }, ], - }, - ]) - - await givenWhenThen(caseId, theCase, notificationDto) + } as Case, + notificationDto, + ) }) it('should send ready for court email notification to prosecutor', () => { @@ -195,15 +188,6 @@ describe('InternalNotificationController - Send ready for court notifications fo ) }) - it('should lookup previous court date notifications', () => { - expect(mockNotificationModel.findAll).toHaveBeenCalledWith({ - where: { - caseId, - type: [NotificationType.READY_FOR_COURT, NotificationType.COURT_DATE], - }, - }) - }) - it('should not send ready for court email notification to defender', () => { expect(mockEmailService.sendEmail).not.toHaveBeenCalledWith( expect.objectContaining({ @@ -234,16 +218,19 @@ describe('InternalNotificationController - Send ready for court notifications fo describe('defender notification', () => { beforeEach(async () => { - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValueOnce([]).mockResolvedValueOnce([ + await givenWhenThen( + caseId, { - recipients: [ - { name: 'Saul Goodman', address: 'saul@dummy.is', success: true }, + ...theCase, + notifications: [ + { + type: NotificationType.READY_FOR_COURT, + recipients: [{ address: 'saul@dummy.is', success: true }], + }, ], - }, - ]) - - await givenWhenThen(caseId, theCase, notificationDto) + } as Case, + notificationDto, + ) }) it('should send ready for court email updated notification to defender', () => { diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRevokedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRevokedNotifications.spec.ts new file mode 100644 index 000000000000..2f5d518e82f9 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRevokedNotifications.spec.ts @@ -0,0 +1,116 @@ +import { uuid } from 'uuidv4' + +import { EmailService } from '@island.is/email-service' + +import { NotificationType } from '@island.is/judicial-system/types' + +import { createTestingNotificationModule } from '../createTestingNotificationModule' + +import { Case } from '../../../case' +import { SendInternalNotificationDto } from '../../dto/sendInternalNotification.dto' +import { DeliverResponse } from '../../models/deliver.response' +import { Notification } from '../../models/notification.model' + +interface Then { + result: DeliverResponse + error: Error +} + +type GivenWhenThen = (notifications?: Notification[]) => Promise + +describe('InternalNotificationController - Send revoked notifications for indictment cases', () => { + const caseId = uuid() + const judgeName = uuid() + const judgeEmail = uuid() + const registrarName = uuid() + const registrarEmail = uuid() + const defenderNationalId = uuid() + const defenderName = uuid() + const defenderEmail = uuid() + const prosecutorsOfficeName = uuid() + const courtName = uuid() + const courtCaseNumber = uuid() + const theCase = { + id: caseId, + judge: { name: judgeName, email: judgeEmail }, + registrar: { name: registrarName, email: registrarEmail }, + defendants: [{ defenderNationalId, defenderName, defenderEmail }], + creatingProsecutor: { institution: { name: prosecutorsOfficeName } }, + court: { name: courtName }, + courtCaseNumber, + } + + let mockEmailService: EmailService + let mockNotificationModel: typeof Notification + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + const { emailService, notificationModel, internalNotificationController } = + await createTestingNotificationModule() + + mockEmailService = emailService + mockNotificationModel = notificationModel + + givenWhenThen = async (notifications?: Notification[]) => { + const then = {} as Then + + await internalNotificationController + .sendCaseNotification( + theCase.id, + { ...theCase, notifications } as Case, + { type: NotificationType.REVOKED } as SendInternalNotificationDto, + ) + .then((result) => (then.result = result)) + .catch((error) => (then.error = error)) + + return then + } + }) + + describe('notifications sent', () => { + let then: Then + + beforeEach(async () => { + then = await givenWhenThen([ + { + type: NotificationType.COURT_DATE, + recipients: [{ address: defenderEmail, success: true }], + } as Notification, + ]) + }) + + it('should send a notifications', () => { + expect(mockEmailService.sendEmail).toHaveBeenCalledWith( + expect.objectContaining({ + to: [{ address: judgeEmail, name: judgeName }], + subject: `Ákæra afturkölluð í máli ${courtCaseNumber}`, + html: `${prosecutorsOfficeName} hefur afturkallað ákæru í máli ${courtCaseNumber}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, + }), + ) + expect(mockEmailService.sendEmail).toHaveBeenCalledWith( + expect.objectContaining({ + to: [{ address: registrarEmail, name: registrarName }], + subject: `Ákæra afturkölluð í máli ${courtCaseNumber}`, + html: `${prosecutorsOfficeName} hefur afturkallað ákæru í máli ${courtCaseNumber}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, + }), + ) + expect(mockEmailService.sendEmail).toHaveBeenCalledWith( + expect.objectContaining({ + to: [{ address: defenderEmail, name: defenderName }], + subject: `Ákæra afturkölluð í máli ${courtCaseNumber}`, + html: `Dómstóllinn hafði skráð þig sem verjanda í málinu.

Sjá nánar á yfirlitssíðu málsins í Réttarvörslugátt.`, + }), + ) + expect(mockNotificationModel.create).toHaveBeenCalledWith({ + caseId: caseId, + type: NotificationType.REVOKED, + recipients: [ + { address: judgeEmail, success: true }, + { address: registrarEmail, success: true }, + { address: defenderEmail, success: true }, + ], + }) + expect(then.result).toEqual({ delivered: true }) + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRulingNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRulingNotifications.spec.ts index d8fa2a8618b1..73d76c2e2489 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRulingNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRulingNotifications.spec.ts @@ -21,7 +21,6 @@ import { Case } from '../../../case' import { Defendant, DefendantService } from '../../../defendant' import { SendInternalNotificationDto } from '../../dto/sendInternalNotification.dto' import { DeliverResponse } from '../../models/deliver.response' -import { Notification } from '../../models/notification.model' import { notificationModuleConfig } from '../../notification.config' jest.mock('../../../../factories') @@ -46,7 +45,6 @@ describe('InternalNotificationController - Send ruling notifications', () => { let mockEmailService: EmailService let mockConfig: ConfigType - let mockNotificationModel: typeof Notification let mockDefendantService: DefendantService let givenWhenThen: GivenWhenThen @@ -56,19 +54,14 @@ describe('InternalNotificationController - Send ruling notifications', () => { const { emailService, notificationConfig, - notificationModel, defendantService, internalNotificationController, } = await createTestingNotificationModule() mockEmailService = emailService mockConfig = notificationConfig - mockNotificationModel = notificationModel mockDefendantService = defendantService - const mockFindAll = mockNotificationModel.findAll as jest.Mock - mockFindAll.mockResolvedValue([]) - givenWhenThen = async (caseId: string, theCase: Case) => { const then = {} as Then