From 14a7777cbc432128bc87d7cb3dfe5e9d48951460 Mon Sep 17 00:00:00 2001 From: unakb Date: Wed, 27 Nov 2024 19:17:20 +0000 Subject: [PATCH] feat(j-s): Indictment sent or withdrawn from prison notifications (#16943) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add a send to fmst button that routes to a page * Remove unused code * Add UI * Create event log when indictment is sent to FMSt * Create event log when indictment is sent to FMSt * Create event log when indictment is sent to FMSt * Refactor * Refactor * Refactor * Updates FMST queries * Refactor * Refactor * Refactor * Refactor * Add DefendantEventLog table * Refactor * Send sentToPrisonAdminDate to client * Show sent to prison admin date on info cards * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Frontend: Upload file to prison admin * Frontend: Upload file to prison admin * Add SentToPrisonAdmin tag in cases list for PP * Merge * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * chore(j-s): Notifications to prison when indictment sent or withdrawn * fix(j-s): Test fix * chore: charts update dirty files * fix(j-s): Tests * chore: nx format:write update dirty files * Refactor * Remove caseId from defendant event log table * Remove caseId from defendant event log table * Revert * Remove unused code * Refactor * Removes unused event type * Removes the defendant event log from the api * Moves defendant event log to defendant model * Fixes backend case retrieval * test(j-s): add missing test * fix(j-s): Tests * Update civilClaimantNotification.service.ts * Reorders notification types to group related type together --------- Co-authored-by: Ívar Oddsson Co-authored-by: Guðjón Guðjónsson Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../backend/infra/judicial-system-backend.ts | 2 + .../modules/defendant/defendant.service.ts | 49 ++++-- .../test/defendantController/update.spec.ts | 52 +++++++ .../internalNotification.controller.ts | 10 +- .../notification/notification.config.ts | 4 + .../notification/notification.module.ts | 10 +- .../caseNotification.service.ts | 20 +-- .../civilClaimantNotification.service.ts | 14 +- .../civilClaimantNotification.strings.ts | 0 .../defendantNotification.service.ts | 92 ++++++++++- .../defendantNotification.strings.ts | 25 +++ .../institutionNotification.service.ts | 14 +- .../institutionNotification.strings.ts | 0 .../subpoenaNotification.service.ts | 12 +- .../subpoenaNotification.strings.ts | 0 .../test/createTestingNotificationModule.ts | 8 +- ...tmentSentToPrisonAdminNotification.spec.ts | 144 ++++++++++++++++++ ...hdrawnFromPrisonAdminNotifications.spec.ts | 140 +++++++++++++++++ charts/judicial-system/values.dev.yaml | 1 + charts/judicial-system/values.prod.yaml | 1 + charts/judicial-system/values.staging.yaml | 1 + .../judicial-system-backend/values.dev.yaml | 1 + .../judicial-system-backend/values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../types/src/lib/notification.ts | 4 + 25 files changed, 546 insertions(+), 60 deletions(-) rename apps/judicial-system/backend/src/app/modules/notification/{ => services/caseNotification}/caseNotification.service.ts (99%) rename apps/judicial-system/backend/src/app/modules/notification/{ => services/civilClaimantNotification}/civilClaimantNotification.service.ts (91%) rename apps/judicial-system/backend/src/app/modules/notification/{ => services/civilClaimantNotification}/civilClaimantNotification.strings.ts (100%) rename apps/judicial-system/backend/src/app/modules/notification/{ => services/defendantNotification}/defendantNotification.service.ts (67%) rename apps/judicial-system/backend/src/app/modules/notification/{ => services/defendantNotification}/defendantNotification.strings.ts (54%) rename apps/judicial-system/backend/src/app/modules/notification/{ => services/institutionNotification}/institutionNotification.service.ts (87%) rename apps/judicial-system/backend/src/app/modules/notification/{ => services/institutionNotification}/institutionNotification.strings.ts (100%) rename apps/judicial-system/backend/src/app/modules/notification/{ => services/subpoenaNotification}/subpoenaNotification.service.ts (92%) rename apps/judicial-system/backend/src/app/modules/notification/{ => services/subpoenaNotification}/subpoenaNotification.strings.ts (100%) create mode 100644 apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendIndictmentSentToPrisonAdminNotification.spec.ts create mode 100644 apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendIndictmentWithdrawnFromPrisonAdminNotifications.spec.ts diff --git a/apps/judicial-system/backend/infra/judicial-system-backend.ts b/apps/judicial-system/backend/infra/judicial-system-backend.ts index aa29ca39050c..bab01682da23 100644 --- a/apps/judicial-system/backend/infra/judicial-system-backend.ts +++ b/apps/judicial-system/backend/infra/judicial-system-backend.ts @@ -70,6 +70,8 @@ export const serviceSetup = (): ServiceBuilder<'judicial-system-backend'> => EMAIL_REPLY_TO_NAME: '/k8s/judicial-system/EMAIL_REPLY_TO_NAME', PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL', PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL', + PRISON_ADMIN_INDICTMENT_EMAILS: + '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS', AUTH_JWT_SECRET: '/k8s/judicial-system/AUTH_JWT_SECRET', ADMIN_USERS: '/k8s/judicial-system/ADMIN_USERS', BACKEND_ACCESS_TOKEN: '/k8s/judicial-system/BACKEND_ACCESS_TOKEN', diff --git a/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts b/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts index e163917b953f..59ca01645a40 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts @@ -74,6 +74,26 @@ export class DefendantService { return message } + private getMessagesForIndictmentToPrisonAdminChanges( + defendant: Defendant, + ): Message { + const messageType = + defendant.isSentToPrisonAdmin === true + ? DefendantNotificationType.INDICTMENT_SENT_TO_PRISON_ADMIN + : DefendantNotificationType.INDICTMENT_WITHDRAWN_FROM_PRISON_ADMIN + + const message = { + type: MessageType.DEFENDANT_NOTIFICATION, + caseId: defendant.caseId, + elementId: defendant.id, + body: { + type: messageType, + }, + } + + return message + } + private getUpdatedDefendant( numberOfAffectedRows: number, defendants: Defendant[], @@ -143,19 +163,19 @@ export class DefendantService { return } + const messages: Message[] = [] + if ( updatedDefendant.isDefenderChoiceConfirmed && !oldDefendant.isDefenderChoiceConfirmed ) { // Defender choice was just confirmed by the court - const messages: Message[] = [ - { - type: MessageType.DELIVERY_TO_COURT_INDICTMENT_DEFENDER, - user, - caseId: theCase.id, - elementId: updatedDefendant.id, - }, - ] + messages.push({ + type: MessageType.DELIVERY_TO_COURT_INDICTMENT_DEFENDER, + user, + caseId: theCase.id, + elementId: updatedDefendant.id, + }) if ( updatedDefendant.defenderChoice === DefenderChoice.CHOOSE || @@ -171,9 +191,20 @@ export class DefendantService { }) } } + } else if ( + updatedDefendant.isSentToPrisonAdmin !== undefined && + updatedDefendant.isSentToPrisonAdmin !== oldDefendant.isSentToPrisonAdmin + ) { + messages.push( + this.getMessagesForIndictmentToPrisonAdminChanges(updatedDefendant), + ) + } - return this.messageService.sendMessagesToQueue(messages) + if (messages.length === 0) { + return } + + return this.messageService.sendMessagesToQueue(messages) } async createForNewCase( diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/update.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/update.spec.ts index 9b5d893e2bc1..34c235472f60 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/update.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/update.spec.ts @@ -209,6 +209,58 @@ describe('DefendantController - Update', () => { }, ) + describe('defendant in indictment is sent to prison admin', () => { + const defendantUpdate = { isSentToPrisonAdmin: true } + const updatedDefendant = { ...defendant, ...defendantUpdate } + + beforeEach(async () => { + const mockUpdate = mockDefendantModel.update as jest.Mock + mockUpdate.mockResolvedValueOnce([1, [updatedDefendant]]) + + await givenWhenThen(defendantUpdate, CaseType.INDICTMENT, caseId) + }) + + it('should queue messages', () => { + expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([ + { + type: MessageType.DEFENDANT_NOTIFICATION, + caseId, + elementId: defendantId, + body: { + type: DefendantNotificationType.INDICTMENT_SENT_TO_PRISON_ADMIN, + }, + }, + ]) + expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledTimes(1) + }) + }) + + describe('defendant in indictment is withdrawn from prison admin', () => { + const defendantUpdate = { isSentToPrisonAdmin: false } + const updatedDefendant = { ...defendant, ...defendantUpdate } + + beforeEach(async () => { + const mockUpdate = mockDefendantModel.update as jest.Mock + mockUpdate.mockResolvedValueOnce([1, [updatedDefendant]]) + + await givenWhenThen(defendantUpdate, CaseType.INDICTMENT, caseId) + }) + + it('should queue messages for indictment withdrawn from prison admin', () => { + expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([ + { + type: MessageType.DEFENDANT_NOTIFICATION, + caseId, + elementId: defendantId, + body: { + type: DefendantNotificationType.INDICTMENT_WITHDRAWN_FROM_PRISON_ADMIN, + }, + }, + ]) + expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledTimes(1) + }) + }) + describe('defendant update fails', () => { let then: Then diff --git a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts index c0e2ca1a0003..5e03c0f784b3 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts @@ -34,12 +34,12 @@ import { InstitutionNotificationDto } from './dto/institutionNotification.dto' import { NotificationDispatchDto } from './dto/notificationDispatch.dto' import { SubpoenaNotificationDto } from './dto/subpoenaNotification.dto' import { DeliverResponse } from './models/deliver.response' -import { CaseNotificationService } from './caseNotification.service' -import { CivilClaimantNotificationService } from './civilClaimantNotification.service' -import { DefendantNotificationService } from './defendantNotification.service' -import { InstitutionNotificationService } from './institutionNotification.service' +import { CaseNotificationService } from './services/caseNotification/caseNotification.service' +import { CivilClaimantNotificationService } from './services/civilClaimantNotification/civilClaimantNotification.service' +import { DefendantNotificationService } from './services/defendantNotification/defendantNotification.service' +import { InstitutionNotificationService } from './services/institutionNotification/institutionNotification.service' +import { SubpoenaNotificationService } from './services/subpoenaNotification/subpoenaNotification.service' import { NotificationDispatchService } from './notificationDispatch.service' -import { SubpoenaNotificationService } from './subpoenaNotification.service' @UseGuards(TokenGuard) @Controller('api/internal') diff --git a/apps/judicial-system/backend/src/app/modules/notification/notification.config.ts b/apps/judicial-system/backend/src/app/modules/notification/notification.config.ts index ba742a4742a6..aecd5dabb6c9 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/notification.config.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/notification.config.ts @@ -14,6 +14,10 @@ export const notificationModuleConfig = defineConfig({ replyToName: env.required('EMAIL_REPLY_TO_NAME', 'Réttarvörslugátt'), prisonEmail: env.required('PRISON_EMAIL', ''), prisonAdminEmail: env.required('PRISON_ADMIN_EMAIL', ''), + prisonAdminIndictmentEmails: env.required( + 'PRISON_ADMIN_INDICTMENT_EMAILS', + '', + ), courtsEmails: env.requiredJSON('COURTS_EMAILS', {}) as { [key: string]: string }, diff --git a/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts b/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts index 14fb32c0a565..101978e3d3ba 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts @@ -17,15 +17,15 @@ import { UserModule, } from '../index' import { Notification } from './models/notification.model' -import { CaseNotificationService } from './caseNotification.service' -import { CivilClaimantNotificationService } from './civilClaimantNotification.service' -import { DefendantNotificationService } from './defendantNotification.service' -import { InstitutionNotificationService } from './institutionNotification.service' +import { CaseNotificationService } from './services/caseNotification/caseNotification.service' +import { CivilClaimantNotificationService } from './services/civilClaimantNotification/civilClaimantNotification.service' +import { DefendantNotificationService } from './services/defendantNotification/defendantNotification.service' +import { InstitutionNotificationService } from './services/institutionNotification/institutionNotification.service' +import { SubpoenaNotificationService } from './services/subpoenaNotification/subpoenaNotification.service' import { InternalNotificationController } from './internalNotification.controller' import { NotificationController } from './notification.controller' import { NotificationService } from './notification.service' import { NotificationDispatchService } from './notificationDispatch.service' -import { SubpoenaNotificationService } from './subpoenaNotification.service' @Module({ imports: [ diff --git a/apps/judicial-system/backend/src/app/modules/notification/caseNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/services/caseNotification/caseNotification.service.ts similarity index 99% rename from apps/judicial-system/backend/src/app/modules/notification/caseNotification.service.ts rename to apps/judicial-system/backend/src/app/modules/notification/services/caseNotification/caseNotification.service.ts index 910270ff53e7..0c4cbba076cb 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/caseNotification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/services/caseNotification/caseNotification.service.ts @@ -68,20 +68,20 @@ import { formatProsecutorCourtDateEmailNotification, formatProsecutorReadyForCourtEmailNotification, formatProsecutorReceivedByCourtSmsNotification, -} from '../../formatters' -import { notifications } from '../../messages' -import { type Case, DateLog } from '../case' -import { CourtService } from '../court' +} from '../../../../formatters' +import { notifications } from '../../../../messages' +import { type Case, DateLog } from '../../../case' +import { CourtService } from '../../../court' import { type CivilClaimant, type Defendant, DefendantService, -} from '../defendant' -import { EventService } from '../event' -import { DeliverResponse } from './models/deliver.response' -import { Notification, Recipient } from './models/notification.model' -import { BaseNotificationService } from './baseNotification.service' -import { notificationModuleConfig } from './notification.config' +} from '../../../defendant' +import { EventService } from '../../../event' +import { BaseNotificationService } from '../../baseNotification.service' +import { DeliverResponse } from '../../models/deliver.response' +import { Notification, Recipient } from '../../models/notification.model' +import { notificationModuleConfig } from '../../notification.config' interface Attachment { filename: string diff --git a/apps/judicial-system/backend/src/app/modules/notification/civilClaimantNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/services/civilClaimantNotification/civilClaimantNotification.service.ts similarity index 91% rename from apps/judicial-system/backend/src/app/modules/notification/civilClaimantNotification.service.ts rename to apps/judicial-system/backend/src/app/modules/notification/services/civilClaimantNotification/civilClaimantNotification.service.ts index c80250801348..ab8744a8dbd6 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/civilClaimantNotification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/services/civilClaimantNotification/civilClaimantNotification.service.ts @@ -16,14 +16,14 @@ import { DEFENDER_INDICTMENT_ROUTE } from '@island.is/judicial-system/consts' import { capitalize } from '@island.is/judicial-system/formatters' import { CivilClaimantNotificationType } from '@island.is/judicial-system/types' -import { Case } from '../case' -import { CivilClaimant } from '../defendant' -import { EventService } from '../event' -import { DeliverResponse } from './models/deliver.response' -import { Notification, Recipient } from './models/notification.model' -import { BaseNotificationService } from './baseNotification.service' +import { Case } from '../../../case' +import { CivilClaimant } from '../../../defendant' +import { EventService } from '../../../event' +import { BaseNotificationService } from '../../baseNotification.service' +import { DeliverResponse } from '../../models/deliver.response' +import { Notification, Recipient } from '../../models/notification.model' +import { notificationModuleConfig } from '../../notification.config' import { strings } from './civilClaimantNotification.strings' -import { notificationModuleConfig } from './notification.config' @Injectable() export class CivilClaimantNotificationService extends BaseNotificationService { diff --git a/apps/judicial-system/backend/src/app/modules/notification/civilClaimantNotification.strings.ts b/apps/judicial-system/backend/src/app/modules/notification/services/civilClaimantNotification/civilClaimantNotification.strings.ts similarity index 100% rename from apps/judicial-system/backend/src/app/modules/notification/civilClaimantNotification.strings.ts rename to apps/judicial-system/backend/src/app/modules/notification/services/civilClaimantNotification/civilClaimantNotification.strings.ts diff --git a/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/services/defendantNotification/defendantNotification.service.ts similarity index 67% rename from apps/judicial-system/backend/src/app/modules/notification/defendantNotification.service.ts rename to apps/judicial-system/backend/src/app/modules/notification/services/defendantNotification/defendantNotification.service.ts index d3f6a0f86f2b..041073d9d4b2 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/services/defendantNotification/defendantNotification.service.ts @@ -19,14 +19,14 @@ import { isIndictmentCase, } from '@island.is/judicial-system/types' -import { Case } from '../case' -import { Defendant } from '../defendant' -import { EventService } from '../event' -import { DeliverResponse } from './models/deliver.response' -import { Notification, Recipient } from './models/notification.model' -import { BaseNotificationService } from './baseNotification.service' +import { Case } from '../../../case' +import { Defendant } from '../../../defendant' +import { EventService } from '../../../event' +import { BaseNotificationService } from '../../baseNotification.service' +import { DeliverResponse } from '../../models/deliver.response' +import { Notification, Recipient } from '../../models/notification.model' +import { notificationModuleConfig } from '../../notification.config' import { strings } from './defendantNotification.strings' -import { notificationModuleConfig } from './notification.config' @Injectable() export class DefendantNotificationService extends BaseNotificationService { @@ -179,6 +179,80 @@ export class DefendantNotificationService extends BaseNotificationService { return { delivered: true } } + private sendIndictmentSentToPrisonAdminNotification(theCase: Case) { + const formattedSubject = this.formatMessage( + strings.indictmentSentToPrisonAdminSubject, + { + courtCaseNumber: theCase.courtCaseNumber, + }, + ) + + const formattedBody = this.formatMessage( + strings.indictmentSentToPrisonAdminBody, + { + courtCaseNumber: theCase.courtCaseNumber, + linkStart: ``, + linkEnd: '', + }, + ) + + // We want to send separate emails to each recipient + const to = this.config.email.prisonAdminIndictmentEmails + .split(',') + .map((email) => email.trim()) + .map((email) => { + return { + name: 'Fangelsismálastofnun', + email, + } + }) + + return this.sendEmails( + theCase, + DefendantNotificationType.INDICTMENT_SENT_TO_PRISON_ADMIN, + formattedSubject, + formattedBody, + to, + ) + } + + private sendIndictmentWithdrawnFromPrisonAdminNotification(theCase: Case) { + const courtCaseNumber = theCase.courtCaseNumber + + const formattedSubject = this.formatMessage( + strings.indictmentWithdrawnFromPrisonAdminSubject, + { + courtCaseNumber, + }, + ) + + const formattedBody = this.formatMessage( + strings.indictmentWithdrawnFromPrisonAdminBody, + { + courtCaseNumber, + }, + ) + + // We want to send separate emails to each recipient + const to = this.config.email.prisonAdminIndictmentEmails + .split(',') + .map((email) => email.trim()) + .map((email) => { + return { + name: 'Fangelsismálastofnun', + email, + } + }) + + return this.sendEmails( + theCase, + DefendantNotificationType.INDICTMENT_WITHDRAWN_FROM_PRISON_ADMIN, + formattedSubject, + formattedBody, + to, + ) + } + private sendNotification( notificationType: DefendantNotificationType, theCase: Case, @@ -189,6 +263,10 @@ export class DefendantNotificationService extends BaseNotificationService { return this.sendDefendantSelectedDefenderNotification(theCase) case DefendantNotificationType.DEFENDER_ASSIGNED: return this.sendDefenderAssignedNotification(theCase, defendant) + case DefendantNotificationType.INDICTMENT_SENT_TO_PRISON_ADMIN: + return this.sendIndictmentSentToPrisonAdminNotification(theCase) + case DefendantNotificationType.INDICTMENT_WITHDRAWN_FROM_PRISON_ADMIN: + return this.sendIndictmentWithdrawnFromPrisonAdminNotification(theCase) default: throw new InternalServerErrorException( `Invalid notification type: ${notificationType}`, diff --git a/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.strings.ts b/apps/judicial-system/backend/src/app/modules/notification/services/defendantNotification/defendantNotification.strings.ts similarity index 54% rename from apps/judicial-system/backend/src/app/modules/notification/defendantNotification.strings.ts rename to apps/judicial-system/backend/src/app/modules/notification/services/defendantNotification/defendantNotification.strings.ts index 0bd021c0dc53..02eebc37019c 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.strings.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/services/defendantNotification/defendantNotification.strings.ts @@ -27,4 +27,29 @@ export const strings = { description: 'Body of the notification when a defender is assigned a confirmed as a defender in indictment cases', }), + indictmentSentToPrisonAdminSubject: defineMessage({ + id: 'judicial.system.backend:defendant_notifications.indictment_sent_to_prison_admin_subject', + defaultMessage: 'Mál {courtCaseNumber} til fullnustu', + description: + 'Titill í tilkynningu til FMST þegar mál er sent til fullnustu', + }), + indictmentSentToPrisonAdminBody: defineMessage({ + id: 'judicial.system.backend:defendant_notifications.indictment_sent_to_prison_admin_body', + defaultMessage: + 'Ríkissaksóknari hefur sent mál {courtCaseNumber} til fullnustu.

Skjöl málsins eru aðgengileg á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}.', + description: 'Texti í tilkynningu til FMST þegar mál er sent til fullnustu', + }), + indictmentWithdrawnFromPrisonAdminSubject: defineMessage({ + id: 'judicial.system.backend:defendant_notifications.indictment_withdrawn_from_prison_admin_subject', + defaultMessage: 'Mál {courtCaseNumber} afturkallað úr fullnustu', + description: + 'Titill í tilkynningu til FMST þegar mál er afturkallað úr fullnustu', + }), + indictmentWithdrawnFromPrisonAdminBody: defineMessage({ + id: 'judicial.system.backend:defendant_notifications.indictment_withdrawn_from_prison_admin_body', + defaultMessage: + 'Ríkissaksóknari hefur afturkallað mál {courtCaseNumber} úr fullnustu.', + description: + 'Texti í tilkynningu til FMST þegar mál er afturkallað úr fullnustu', + }), } diff --git a/apps/judicial-system/backend/src/app/modules/notification/institutionNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/services/institutionNotification/institutionNotification.service.ts similarity index 87% rename from apps/judicial-system/backend/src/app/modules/notification/institutionNotification.service.ts rename to apps/judicial-system/backend/src/app/modules/notification/services/institutionNotification/institutionNotification.service.ts index 82727cab9a70..8d3c9ae9af2a 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/institutionNotification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/services/institutionNotification/institutionNotification.service.ts @@ -12,14 +12,14 @@ import { type ConfigType } from '@island.is/nest/config' import { InstitutionNotificationType } from '@island.is/judicial-system/types' -import { InternalCaseService } from '../case' -import { EventService } from '../event' -import { type User, UserService } from '../user' -import { DeliverResponse } from './models/deliver.response' -import { Notification } from './models/notification.model' -import { BaseNotificationService } from './baseNotification.service' +import { InternalCaseService } from '../../../case' +import { EventService } from '../../../event' +import { type User, UserService } from '../../../user' +import { BaseNotificationService } from '../../baseNotification.service' +import { DeliverResponse } from '../../models/deliver.response' +import { Notification } from '../../models/notification.model' +import { notificationModuleConfig } from '../../notification.config' import { strings } from './institutionNotification.strings' -import { notificationModuleConfig } from './notification.config' @Injectable() export class InstitutionNotificationService extends BaseNotificationService { diff --git a/apps/judicial-system/backend/src/app/modules/notification/institutionNotification.strings.ts b/apps/judicial-system/backend/src/app/modules/notification/services/institutionNotification/institutionNotification.strings.ts similarity index 100% rename from apps/judicial-system/backend/src/app/modules/notification/institutionNotification.strings.ts rename to apps/judicial-system/backend/src/app/modules/notification/services/institutionNotification/institutionNotification.strings.ts diff --git a/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/services/subpoenaNotification/subpoenaNotification.service.ts similarity index 92% rename from apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.service.ts rename to apps/judicial-system/backend/src/app/modules/notification/services/subpoenaNotification/subpoenaNotification.service.ts index 41a4630f0c92..575d73abd748 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/services/subpoenaNotification/subpoenaNotification.service.ts @@ -15,12 +15,12 @@ import { type ConfigType } from '@island.is/nest/config' import { ROUTE_HANDLER_ROUTE } from '@island.is/judicial-system/consts' import { SubpoenaNotificationType } from '@island.is/judicial-system/types' -import { Case } from '../case' -import { EventService } from '../event' -import { DeliverResponse } from './models/deliver.response' -import { Notification, Recipient } from './models/notification.model' -import { BaseNotificationService } from './baseNotification.service' -import { notificationModuleConfig } from './notification.config' +import { Case } from '../../../case' +import { EventService } from '../../../event' +import { BaseNotificationService } from '../../baseNotification.service' +import { DeliverResponse } from '../../models/deliver.response' +import { Notification, Recipient } from '../../models/notification.model' +import { notificationModuleConfig } from '../../notification.config' import { strings } from './subpoenaNotification.strings' @Injectable() diff --git a/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.strings.ts b/apps/judicial-system/backend/src/app/modules/notification/services/subpoenaNotification/subpoenaNotification.strings.ts similarity index 100% rename from apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.strings.ts rename to apps/judicial-system/backend/src/app/modules/notification/services/subpoenaNotification/subpoenaNotification.strings.ts 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 8c89e945896c..c5ea4ac34521 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 @@ -24,16 +24,16 @@ import { DefendantService } from '../../defendant' import { eventModuleConfig, EventService } from '../../event' import { InstitutionService } from '../../institution' import { UserService } from '../../user' -import { CaseNotificationService } from '../caseNotification.service' -import { CivilClaimantNotificationService } from '../civilClaimantNotification.service' -import { DefendantNotificationService } from '../defendantNotification.service' -import { InstitutionNotificationService } from '../institutionNotification.service' import { InternalNotificationController } from '../internalNotification.controller' import { Notification } from '../models/notification.model' import { notificationModuleConfig } from '../notification.config' import { NotificationController } from '../notification.controller' import { NotificationService } from '../notification.service' import { NotificationDispatchService } from '../notificationDispatch.service' +import { CaseNotificationService } from '../services/caseNotification/caseNotification.service' +import { CivilClaimantNotificationService } from '../services/civilClaimantNotification/civilClaimantNotification.service' +import { DefendantNotificationService } from '../services/defendantNotification/defendantNotification.service' +import { InstitutionNotificationService } from '../services/institutionNotification/institutionNotification.service' jest.mock('@island.is/judicial-system/message') diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendIndictmentSentToPrisonAdminNotification.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendIndictmentSentToPrisonAdminNotification.spec.ts new file mode 100644 index 000000000000..1c5f3468bbfa --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendIndictmentSentToPrisonAdminNotification.spec.ts @@ -0,0 +1,144 @@ +import { uuid } from 'uuidv4' + +import { EmailService } from '@island.is/email-service' + +import { ROUTE_HANDLER_ROUTE } from '@island.is/judicial-system/consts' +import { + CaseType, + DefendantNotificationType, +} from '@island.is/judicial-system/types' + +import { createTestingNotificationModule } from '../../createTestingNotificationModule' + +import { Case } from '../../../../case' +import { Defendant } from '../../../../defendant' +import { DefendantNotificationDto } from '../../../dto/defendantNotification.dto' +import { DeliverResponse } from '../../../models/deliver.response' +import { Notification } from '../../../models/notification.model' + +jest.mock('../../../../../factories') + +interface Then { + result: DeliverResponse + error: Error +} + +type GivenWhenThen = ( + caseId: string, + defendantId: string, + theCase: Case, + defendant: Defendant, + notificationDto: DefendantNotificationDto, +) => Promise + +describe('InternalNotificationController - Defendant - Send indictment sent to prison admin notification', () => { + const caseId = uuid() + const defendantId = uuid() + const emails = [ + 'prisonadminindictment@omnitrix.is', + 'prisonadminindictment2@omnitrix.is', + ] + + let mockEmailService: EmailService + let mockNotificationModel: typeof Notification + let defendantNotificationDTO: DefendantNotificationDto + + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + process.env.PRISON_ADMIN_INDICTMENT_EMAILS = emails.join(',') + + const { emailService, internalNotificationController, notificationModel } = + await createTestingNotificationModule() + + defendantNotificationDTO = { + type: DefendantNotificationType.INDICTMENT_SENT_TO_PRISON_ADMIN, + } + + mockEmailService = emailService + mockNotificationModel = notificationModel + + givenWhenThen = async ( + caseId: string, + defendantId: string, + theCase: Case, + defendant: Defendant, + notificationDto: DefendantNotificationDto, + ) => { + const then = {} as Then + + try { + then.result = + await internalNotificationController.sendDefendantNotification( + caseId, + defendantId, + theCase, + defendant, + notificationDto, + ) + } catch (error) { + then.error = error as Error + } + + return then + } + }) + + describe('when sending indictment to prison admin', () => { + const defendant = { + id: defendantId, + } as Defendant + + const theCase = { + id: caseId, + courtCaseNumber: 'S-123-456/2024', + type: CaseType.INDICTMENT, + defendants: [defendant], + } as Case + + beforeEach(async () => { + await givenWhenThen( + caseId, + defendantId, + theCase, + defendant, + defendantNotificationDTO, + ) + }) + + it('should send a notification to prison admin emails', () => { + expect(mockEmailService.sendEmail).toBeCalledTimes(emails.length) + emails.forEach((email) => { + expect(mockEmailService.sendEmail).toBeCalledWith( + expect.objectContaining({ + to: [ + { + name: 'Fangelsismálastofnun', + address: email, + }, + ], + + attachments: undefined, + subject: `Mál S-123-456/2024 til fullnustu`, + html: expect.stringContaining(ROUTE_HANDLER_ROUTE), + text: expect.stringContaining( + 'Ríkissaksóknari hefur sent mál S-123-456/2024 til fullnustu.', + ), + }), + ) + }) + }) + + it('should record notification', () => { + expect(mockNotificationModel.create).toHaveBeenCalledTimes(1) + expect(mockNotificationModel.create).toHaveBeenCalledWith({ + caseId, + type: defendantNotificationDTO.type, + recipients: emails.map((email) => ({ + address: email, + success: true, + })), + }) + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendIndictmentWithdrawnFromPrisonAdminNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendIndictmentWithdrawnFromPrisonAdminNotifications.spec.ts new file mode 100644 index 000000000000..3a35e28ac53e --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendIndictmentWithdrawnFromPrisonAdminNotifications.spec.ts @@ -0,0 +1,140 @@ +import { uuid } from 'uuidv4' + +import { EmailService } from '@island.is/email-service' + +import { + CaseType, + DefendantNotificationType, +} from '@island.is/judicial-system/types' + +import { createTestingNotificationModule } from '../../createTestingNotificationModule' + +import { Case } from '../../../../case' +import { Defendant } from '../../../../defendant' +import { DefendantNotificationDto } from '../../../dto/defendantNotification.dto' +import { DeliverResponse } from '../../../models/deliver.response' +import { Notification } from '../../../models/notification.model' + +jest.mock('../../../../../factories') + +interface Then { + result: DeliverResponse + error: Error +} + +type GivenWhenThen = ( + caseId: string, + defendantId: string, + theCase: Case, + defendant: Defendant, + notificationDto: DefendantNotificationDto, +) => Promise + +describe('InternalNotificationController - Defendant - Send indictment withdrawn from prison admin notification', () => { + const caseId = uuid() + const defendantId = uuid() + const emails = [ + 'prisonadminindictment@omnitrix.is', + 'prisonadminindictment2@omnitrix.is', + ] + + let mockEmailService: EmailService + let mockNotificationModel: typeof Notification + let defendantNotificationDTO: DefendantNotificationDto + + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + process.env.PRISON_ADMIN_INDICTMENT_EMAILS = emails.join(',') + + const { emailService, internalNotificationController, notificationModel } = + await createTestingNotificationModule() + + defendantNotificationDTO = { + type: DefendantNotificationType.INDICTMENT_WITHDRAWN_FROM_PRISON_ADMIN, + } + + mockEmailService = emailService + mockNotificationModel = notificationModel + + givenWhenThen = async ( + caseId: string, + defendantId: string, + theCase: Case, + defendant: Defendant, + notificationDto: DefendantNotificationDto, + ) => { + const then = {} as Then + + try { + then.result = + await internalNotificationController.sendDefendantNotification( + caseId, + defendantId, + theCase, + defendant, + notificationDto, + ) + } catch (error) { + then.error = error as Error + } + + return then + } + }) + + describe('when withdrawing indictment from prison admin', () => { + const defendant = { + id: defendantId, + } as Defendant + + const theCase = { + id: caseId, + courtCaseNumber: 'S-123-456/2024', + type: CaseType.INDICTMENT, + defendants: [defendant], + } as Case + + beforeEach(async () => { + await givenWhenThen( + caseId, + defendantId, + theCase, + defendant, + defendantNotificationDTO, + ) + }) + + it('should send a notification to prison admin emails', () => { + expect(mockEmailService.sendEmail).toBeCalledTimes(emails.length) + emails.forEach((email) => { + expect(mockEmailService.sendEmail).toBeCalledWith( + expect.objectContaining({ + to: [ + { + name: 'Fangelsismálastofnun', + address: email, + }, + ], + subject: `Mál S-123-456/2024 afturkallað úr fullnustu`, + text: expect.stringContaining( + 'Ríkissaksóknari hefur afturkallað mál S-123-456/2024 úr fullnustu.', + ), + }), + ) + }) + }) + + it('should record notification', () => { + expect(mockNotificationModel.create).toHaveBeenCalledTimes(1) + expect(mockNotificationModel.create).toHaveBeenCalledWith({ + caseId, + type: defendantNotificationDTO.type, + recipients: emails.map((email) => ({ + address: email, + success: true, + })), + }) + }) + }) +}) diff --git a/charts/judicial-system/values.dev.yaml b/charts/judicial-system/values.dev.yaml index b8ffb857f3cd..dd58423affdc 100644 --- a/charts/judicial-system/values.dev.yaml +++ b/charts/judicial-system/values.dev.yaml @@ -226,6 +226,7 @@ judicial-system-backend: NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' + PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' XROAD_CLIENT_CERT: '/k8s/judicial-system/XROAD_CLIENT_CERT' XROAD_CLIENT_KEY: '/k8s/judicial-system/XROAD_CLIENT_KEY' diff --git a/charts/judicial-system/values.prod.yaml b/charts/judicial-system/values.prod.yaml index 79a47b052253..dc74c1e64d61 100644 --- a/charts/judicial-system/values.prod.yaml +++ b/charts/judicial-system/values.prod.yaml @@ -226,6 +226,7 @@ judicial-system-backend: NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' + PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' XROAD_CLIENT_CERT: '/k8s/judicial-system/XROAD_CLIENT_CERT' XROAD_CLIENT_KEY: '/k8s/judicial-system/XROAD_CLIENT_KEY' diff --git a/charts/judicial-system/values.staging.yaml b/charts/judicial-system/values.staging.yaml index c7c7b8263784..3dacb3f6fb4a 100644 --- a/charts/judicial-system/values.staging.yaml +++ b/charts/judicial-system/values.staging.yaml @@ -226,6 +226,7 @@ judicial-system-backend: NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' + PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' XROAD_CLIENT_CERT: '/k8s/judicial-system/XROAD_CLIENT_CERT' XROAD_CLIENT_KEY: '/k8s/judicial-system/XROAD_CLIENT_KEY' diff --git a/charts/services/judicial-system-backend/values.dev.yaml b/charts/services/judicial-system-backend/values.dev.yaml index 54cc5578a728..8aeb2656041e 100644 --- a/charts/services/judicial-system-backend/values.dev.yaml +++ b/charts/services/judicial-system-backend/values.dev.yaml @@ -139,6 +139,7 @@ secrets: NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' + PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' XROAD_CLIENT_CERT: '/k8s/judicial-system/XROAD_CLIENT_CERT' XROAD_CLIENT_KEY: '/k8s/judicial-system/XROAD_CLIENT_KEY' diff --git a/charts/services/judicial-system-backend/values.prod.yaml b/charts/services/judicial-system-backend/values.prod.yaml index 35e807945f14..2807d821b9d9 100644 --- a/charts/services/judicial-system-backend/values.prod.yaml +++ b/charts/services/judicial-system-backend/values.prod.yaml @@ -139,6 +139,7 @@ secrets: NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' + PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' XROAD_CLIENT_CERT: '/k8s/judicial-system/XROAD_CLIENT_CERT' XROAD_CLIENT_KEY: '/k8s/judicial-system/XROAD_CLIENT_KEY' diff --git a/charts/services/judicial-system-backend/values.staging.yaml b/charts/services/judicial-system-backend/values.staging.yaml index 57039508f4ca..7221f2acfd51 100644 --- a/charts/services/judicial-system-backend/values.staging.yaml +++ b/charts/services/judicial-system-backend/values.staging.yaml @@ -139,6 +139,7 @@ secrets: NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' + PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' XROAD_CLIENT_CERT: '/k8s/judicial-system/XROAD_CLIENT_CERT' XROAD_CLIENT_KEY: '/k8s/judicial-system/XROAD_CLIENT_KEY' diff --git a/libs/judicial-system/types/src/lib/notification.ts b/libs/judicial-system/types/src/lib/notification.ts index 6c449ffc196e..e9b8df060e63 100644 --- a/libs/judicial-system/types/src/lib/notification.ts +++ b/libs/judicial-system/types/src/lib/notification.ts @@ -23,6 +23,8 @@ export enum CaseNotificationType { export enum DefendantNotificationType { DEFENDANT_SELECTED_DEFENDER = 'DEFENDANT_SELECTED_DEFENDER', DEFENDER_ASSIGNED = 'DEFENDER_ASSIGNED', + INDICTMENT_SENT_TO_PRISON_ADMIN = 'INDICTMENT_SENT_TO_PRISON_ADMIN', + INDICTMENT_WITHDRAWN_FROM_PRISON_ADMIN = 'INDICTMENT_WITHDRAWN_FROM_PRISON_ADMIN', } export enum SubpoenaNotificationType { @@ -60,6 +62,8 @@ export enum NotificationType { CASE_FILES_UPDATED = CaseNotificationType.CASE_FILES_UPDATED, DEFENDANT_SELECTED_DEFENDER = DefendantNotificationType.DEFENDANT_SELECTED_DEFENDER, DEFENDER_ASSIGNED = DefendantNotificationType.DEFENDER_ASSIGNED, + INDICTMENT_SENT_TO_PRISON_ADMIN = DefendantNotificationType.INDICTMENT_SENT_TO_PRISON_ADMIN, + INDICTMENT_WITHDRAWN_FROM_PRISON_ADMIN = DefendantNotificationType.INDICTMENT_WITHDRAWN_FROM_PRISON_ADMIN, SERVICE_SUCCESSFUL = SubpoenaNotificationType.SERVICE_SUCCESSFUL, SERVICE_FAILED = SubpoenaNotificationType.SERVICE_FAILED, SPOKESPERSON_ASSIGNED = CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED,