From ff2e71f7d6dd523c1a4bac959f99cfc06e20ded9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Wed, 25 Sep 2024 14:41:40 +0200 Subject: [PATCH 01/61] Locks subpoena fields when arraignment date has been set --- .../routes/Court/Indictments/Subpoena/Subpoena.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx index 92455ec72667..4d13fb753f1c 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx @@ -46,11 +46,11 @@ const Subpoena: FC = () => { } = useCourtArrangements(workingCase, setWorkingCase, 'arraignmentDate') const { sendNotification } = useCase() - const isArraignmentDone = Boolean(workingCase.indictmentDecision) + const isArraignmentScheduled = Boolean(workingCase.arraignmentDate) const handleNavigationTo = useCallback( async (destination: keyof stepValidationsType) => { - if (isArraignmentDone) { + if (isArraignmentScheduled) { router.push(`${destination}/${workingCase.id}`) return } @@ -89,7 +89,7 @@ const Subpoena: FC = () => { router.push(`${destination}/${workingCase.id}`) }, [ - isArraignmentDone, + isArraignmentScheduled, sendCourtDateToServer, workingCase.defendants, workingCase.notifications, @@ -134,8 +134,8 @@ const Subpoena: FC = () => { handleCourtDateChange={handleCourtDateChange} handleCourtRoomChange={handleCourtRoomChange} courtDate={workingCase.arraignmentDate} - dateTimeDisabled={isArraignmentDone} - courtRoomDisabled={isArraignmentDone} + dateTimeDisabled={isArraignmentScheduled} + courtRoomDisabled={isArraignmentScheduled} courtRoomRequired /> @@ -169,14 +169,14 @@ const Subpoena: FC = () => { previousUrl={`${constants.INDICTMENTS_RECEPTION_AND_ASSIGNMENT_ROUTE}/${workingCase.id}`} nextIsLoading={isLoadingWorkingCase} onNextButtonClick={() => { - if (isArraignmentDone) { + if (isArraignmentScheduled) { router.push( `${constants.INDICTMENTS_DEFENDER_ROUTE}/${workingCase.id}`, ) } else setNavigateTo(constants.INDICTMENTS_DEFENDER_ROUTE) }} nextButtonText={ - isArraignmentDone + isArraignmentScheduled ? undefined : formatMessage(strings.nextButtonText) } From 4293520384d759174848a0176214f8af59d0f5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Wed, 25 Sep 2024 12:50:59 +0000 Subject: [PATCH 02/61] Show subpoena status --- .../api/src/app/modules/defendant/index.ts | 1 + .../defendant/models/defendant.model.ts | 5 +++ .../defendant/models/subpoena.model.ts | 34 +++++++++++++++++++ .../src/app/modules/case/case.service.ts | 9 ++++- .../modules/subpoena/models/subpoena.model.ts | 4 +++ .../src/components/FormProvider/case.graphql | 6 ++++ .../Court/Indictments/Overview/Overview.tsx | 22 +++++++++++- .../IndictmentOverview/IndictmentOverview.tsx | 3 +- 8 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts diff --git a/apps/judicial-system/api/src/app/modules/defendant/index.ts b/apps/judicial-system/api/src/app/modules/defendant/index.ts index 0811956a0ca8..040ddad841e3 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/index.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/index.ts @@ -2,3 +2,4 @@ export { Defendant } from './models/defendant.model' export { DeleteDefendantResponse } from './models/delete.response' export { CivilClaimant } from './models/civilClaimant.model' export { DeleteCivilClaimantResponse } from './models/deleteCivilClaimant.response' +export { Subpoena } from './models/subpoena.model' diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts index cc761d4b363a..595b696e2c28 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts @@ -8,6 +8,8 @@ import { SubpoenaType, } from '@island.is/judicial-system/types' +import { Subpoena } from './subpoena.model' + registerEnumType(Gender, { name: 'Gender' }) registerEnumType(DefendantPlea, { name: 'DefendantPlea' }) registerEnumType(ServiceRequirement, { name: 'ServiceRequirement' }) @@ -75,4 +77,7 @@ export class Defendant { @Field(() => SubpoenaType, { nullable: true }) readonly subpoenaType?: SubpoenaType + + @Field(() => [Subpoena], { nullable: true }) + readonly subpoenas?: Subpoena[] } diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts new file mode 100644 index 000000000000..3b981c7a4bc7 --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts @@ -0,0 +1,34 @@ +import { Field, ID, ObjectType } from '@nestjs/graphql' + +@ObjectType() +export class Subpoena { + @Field(() => ID) + readonly id!: string + + @Field(() => String, { nullable: true }) + readonly created?: string + + @Field(() => String, { nullable: true }) + readonly modified?: string + + @Field(() => ID, { nullable: true }) + readonly subpoenaId!: string + + @Field(() => ID, { nullable: true }) + readonly defendantId?: string + + @Field(() => ID, { nullable: true }) + readonly caseId?: string + + @Field(() => Boolean, { nullable: true }) + readonly acknowledged?: boolean + + @Field(() => String, { nullable: true }) + readonly acknowledgedDate?: string + + @Field(() => String, { nullable: true }) + readonly registeredBy?: string + + @Field(() => String, { nullable: true }) + readonly comment?: string +} 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 1d4043abd85d..dc953ceae5e0 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 @@ -62,6 +62,7 @@ import { CaseFile, FileService } from '../file' import { IndictmentCount } from '../indictment-count' import { Institution } from '../institution' import { Notification } from '../notification' +import { Subpoena } from '../subpoena/models/subpoena.model' import { User } from '../user' import { CreateCaseDto } from './dto/createCase.dto' import { getCasesQueryFilter } from './filters/cases.filter' @@ -271,7 +272,13 @@ export const include: Includeable[] = [ ], }, { model: Case, as: 'childCase' }, - { model: Defendant, as: 'defendants' }, + { + model: Defendant, + as: 'defendants', + include: [ + { model: Subpoena, as: 'subpoenas', order: [['created', 'DESC']] }, + ], + }, { model: CivilClaimant, as: 'civilClaimants' }, { model: IndictmentCount, as: 'indictmentCounts' }, { diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts index 5a540fd3af27..868ee5a7a921 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts @@ -61,6 +61,10 @@ export class Subpoena extends Model { @ApiPropertyOptional({ type: Boolean }) acknowledged?: string + @Column({ type: DataType.DATE, allowNull: true }) + @ApiPropertyOptional({ type: Date }) + acknowledgedDate?: Date + @Column({ type: DataType.STRING, allowNull: true }) @ApiPropertyOptional({ type: String }) registeredBy?: string diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index 70cdc86833c7..6716cfa9397e 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -26,6 +26,12 @@ query Case($input: CaseQueryInput!) { verdictViewDate verdictAppealDeadline subpoenaType + subpoenas { + acknowledged + acknowledgedDate + registeredBy + comment + } } defenderName defenderNationalId diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index ce7a017ecbcb..173a05570ea9 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -2,8 +2,9 @@ import { useCallback, useContext, useState } from 'react' import { useIntl } from 'react-intl' import { useRouter } from 'next/router' -import { Box } from '@island.is/island-ui/core' +import { AlertMessage, Box, Text } from '@island.is/island-ui/core' import * as constants from '@island.is/judicial-system/consts' +import { formatDate } from '@island.is/judicial-system/formatters' import { core, titles } from '@island.is/judicial-system-web/messages' import { ConnectedCaseFilesAccordionItem, @@ -75,6 +76,25 @@ const IndictmentOverview = () => { {formatMessage(strings.inProgressTitle)} + {workingCase.defendants?.map((defendant) => + defendant.subpoenas?.map((subpoena) => ( + + + {`${subpoena.registeredBy} -- ${formatDate( + subpoena.acknowledgedDate, + 'Pp', + )}`} + + {subpoena.comment} + + } + type="warning" + /> + )), + )} {workingCase.court && latestDate?.date && workingCase.indictmentDecision !== IndictmentDecision.COMPLETING && diff --git a/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx b/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx index cb2da97042f7..9a4d212e1b76 100644 --- a/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx +++ b/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx @@ -2,11 +2,12 @@ import { FC, useCallback, useContext, useState } from 'react' import { useIntl } from 'react-intl' import { useRouter } from 'next/router' -import { Box, Button } from '@island.is/island-ui/core' +import { AlertMessage, Box, Button } from '@island.is/island-ui/core' import * as constants from '@island.is/judicial-system/consts' import { isCompletedCase, isDefenceUser, + isDistrictCourtUser, } from '@island.is/judicial-system/types' import { titles } from '@island.is/judicial-system-web/messages' import { From cf631db4b8d9115243fffc8e56b5c4d1ce9d5380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Wed, 25 Sep 2024 15:49:34 +0200 Subject: [PATCH 03/61] Move arraignment date message handling to the server side --- .../src/app/modules/case/case.service.ts | 44 ++++++++++++++++--- .../notification/notification.service.ts | 9 ---- .../app/modules/subpoena/subpoena.service.ts | 1 + .../Court/Indictments/Subpoena/Subpoena.tsx | 24 +--------- 4 files changed, 39 insertions(+), 39 deletions(-) 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 1d4043abd85d..2a21877fa13f 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 @@ -1143,15 +1143,29 @@ export class CaseService { private addMessagesForNewCourtDateToQueue( theCase: Case, user: TUser, + arraignmentDateChanged: boolean, ): Promise { - return this.messageService.sendMessagesToQueue([ + const messages: Message[] = [ { type: MessageType.NOTIFICATION, user, caseId: theCase.id, body: { type: NotificationType.COURT_DATE }, }, - ]) + ] + + if (arraignmentDateChanged) { + theCase.defendants?.forEach((defendant) => { + messages.push({ + type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, + user, + caseId: theCase.id, + elementId: defendant.id, + }) + }) + } + + return this.messageService.sendMessagesToQueue(messages) } private async addMessagesForUpdatedCaseToQueue( @@ -1310,11 +1324,27 @@ export class CaseService { } // This only applies to indictments - const courtDate = DateLog.courtDate(theCase.dateLogs) - const updatedCourtDate = DateLog.courtDate(updatedCase.dateLogs) - if (updatedCourtDate && updatedCourtDate.date !== courtDate?.date) { - // New court date - await this.addMessagesForNewCourtDateToQueue(updatedCase, user) + if (isIndictment) { + const arraignmentDate = DateLog.arraignmentDate(theCase.dateLogs) + const updatedArraignmentDate = DateLog.arraignmentDate( + updatedCase.dateLogs, + ) + const arraignmentDateChanged = + updatedArraignmentDate && + updatedArraignmentDate.date !== arraignmentDate?.date + const courtDate = DateLog.courtDate(theCase.dateLogs) + const updatedCourtDate = DateLog.courtDate(updatedCase.dateLogs) + const courtDateChanged = + updatedCourtDate && updatedCourtDate.date !== courtDate?.date + + if (arraignmentDateChanged || courtDateChanged) { + // New arraignment date or new court date + await this.addMessagesForNewCourtDateToQueue( + updatedCase, + user, + Boolean(arraignmentDateChanged), + ) + } } } 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 e4ffc288777d..8103b1106f14 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 @@ -69,15 +69,6 @@ export class NotificationService { ] } else { messages = [this.getNotificationMessage(type, user, theCase)] - theCase.defendants?.forEach((defendant) => { - // TODO: move this elsewhere when we know exactly where the trigger should be - messages.push({ - type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, - user, - caseId: theCase.id, - elementId: defendant.id, - }) - }) } break case NotificationType.HEADS_UP: diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 62d0355fd57c..c28cf4ec4875 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -126,6 +126,7 @@ export class SubpoenaService { return { delivered: false } } + // TODO: Improve error handling by checking how many rows were affected and posting error event await this.subpoenaModel.update( { subpoenaId: createdSubpoena.subpoenaId }, { where: { id: subpoena.id } }, diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx index 4d13fb753f1c..11c767d7008f 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx @@ -19,14 +19,9 @@ import { SectionHeading, useCourtArrangements, } from '@island.is/judicial-system-web/src/components' -import { NotificationType } from '@island.is/judicial-system-web/src/graphql/schema' import { SubpoenaType } from '@island.is/judicial-system-web/src/routes/Court/components' import type { stepValidationsType } from '@island.is/judicial-system-web/src/utils/formHelper' -import { - useCase, - useDefendants, -} from '@island.is/judicial-system-web/src/utils/hooks' -import { hasSentNotification } from '@island.is/judicial-system-web/src/utils/stepHelper' +import { useDefendants } from '@island.is/judicial-system-web/src/utils/hooks' import { isSubpoenaStepValid } from '@island.is/judicial-system-web/src/utils/validate' import { subpoena as strings } from './Subpoena.strings' @@ -39,12 +34,10 @@ const Subpoena: FC = () => { const { formatMessage } = useIntl() const { courtDate, - courtDateHasChanged, handleCourtDateChange, handleCourtRoomChange, sendCourtDateToServer, } = useCourtArrangements(workingCase, setWorkingCase, 'arraignmentDate') - const { sendNotification } = useCase() const isArraignmentScheduled = Boolean(workingCase.arraignmentDate) @@ -69,18 +62,6 @@ const Subpoena: FC = () => { }) } - if ( - !hasSentNotification( - NotificationType.COURT_DATE, - workingCase.notifications, - ).hasSent || - courtDateHasChanged - ) { - promises.push( - sendNotification(workingCase.id, NotificationType.COURT_DATE), - ) - } - const allDataSentToServer = await Promise.all(promises) if (!allDataSentToServer.every((result) => result)) { return @@ -92,11 +73,8 @@ const Subpoena: FC = () => { isArraignmentScheduled, sendCourtDateToServer, workingCase.defendants, - workingCase.notifications, workingCase.id, - courtDateHasChanged, updateDefendant, - sendNotification, ], ) From 9192246cd32f31de15dff4b58985ff6dabc2796d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Wed, 25 Sep 2024 16:22:36 +0200 Subject: [PATCH 04/61] Updates tests and fixes date comparison --- .../src/app/modules/case/case.service.ts | 6 ++- .../case/test/caseController/update.spec.ts | 48 ++++++++++++++++++- 2 files changed, 50 insertions(+), 4 deletions(-) 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 2a21877fa13f..9f3973a8f152 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 @@ -1331,11 +1331,13 @@ export class CaseService { ) const arraignmentDateChanged = updatedArraignmentDate && - updatedArraignmentDate.date !== arraignmentDate?.date + updatedArraignmentDate.date.getTime() !== + arraignmentDate?.date.getTime() const courtDate = DateLog.courtDate(theCase.dateLogs) const updatedCourtDate = DateLog.courtDate(updatedCase.dateLogs) const courtDateChanged = - updatedCourtDate && updatedCourtDate.date !== courtDate?.date + updatedCourtDate && + updatedCourtDate.date.getTime() !== courtDate?.date.getTime() if (arraignmentDateChanged || courtDateChanged) { // New arraignment date or new court date diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts index 0ffde6ad16ca..932319cac89e 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts @@ -872,11 +872,56 @@ describe('CaseController - Update', () => { }) }) - describe('court date updated', () => { + describe('indictment arraignment date updated', () => { + const arraignmentDate = { date: new Date(), location: uuid() } + const caseToUpdate = { arraignmentDate } + const updatedCase = { + ...theCase, + type: CaseType.INDICTMENT, + dateLogs: [{ dateType: DateType.ARRAIGNMENT_DATE, ...arraignmentDate }], + } + + beforeEach(async () => { + const mockFindOne = mockCaseModel.findOne as jest.Mock + mockFindOne.mockResolvedValueOnce(updatedCase) + + await givenWhenThen(caseId, user, theCase, caseToUpdate) + }) + + it('should update case', () => { + expect(mockDateLogModel.create).toHaveBeenCalledWith( + { dateType: DateType.ARRAIGNMENT_DATE, caseId, ...arraignmentDate }, + { transaction }, + ) + expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([ + { + type: MessageType.NOTIFICATION, + user, + caseId, + body: { type: NotificationType.COURT_DATE }, + }, + { + type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, + user, + caseId: theCase.id, + elementId: defendantId1, + }, + { + type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, + user, + caseId: theCase.id, + elementId: defendantId2, + }, + ]) + }) + }) + + describe('indictment court date updated', () => { const courtDate = { date: new Date(), location: uuid() } const caseToUpdate = { courtDate } const updatedCase = { ...theCase, + type: CaseType.INDICTMENT, dateLogs: [{ dateType: DateType.COURT_DATE, ...courtDate }], } @@ -892,7 +937,6 @@ describe('CaseController - Update', () => { { dateType: DateType.COURT_DATE, caseId, ...courtDate }, { transaction }, ) - expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([ { type: MessageType.NOTIFICATION, From f6f664271c2932b0c44dfdeb8ee7ffa32d3a56fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Wed, 25 Sep 2024 14:25:27 +0000 Subject: [PATCH 05/61] Change subpoena data structure --- .../defendant/models/subpoena.model.ts | 14 ++-- .../20240925130059-update-subpoena.js | 73 +++++++++++++++++++ .../subpoena/dto/updateSubpoena.dto.ts | 12 +-- .../modules/subpoena/models/subpoena.model.ts | 16 ++-- .../src/components/FormProvider/case.graphql | 6 +- .../Court/Indictments/Overview/Overview.tsx | 32 ++++---- .../IndictmentOverview/IndictmentOverview.tsx | 3 +- libs/judicial-system/types/src/index.ts | 1 + .../types/src/lib/defendant.ts | 8 ++ 9 files changed, 129 insertions(+), 36 deletions(-) create mode 100644 apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts index 3b981c7a4bc7..079f9725ca0b 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts @@ -1,4 +1,8 @@ -import { Field, ID, ObjectType } from '@nestjs/graphql' +import { Field, ID, ObjectType, registerEnumType } from '@nestjs/graphql' + +import { ServiceStatus } from '@island.is/judicial-system/types' + +registerEnumType(ServiceStatus, { name: 'ServiceStatus' }) @ObjectType() export class Subpoena { @@ -20,14 +24,14 @@ export class Subpoena { @Field(() => ID, { nullable: true }) readonly caseId?: string - @Field(() => Boolean, { nullable: true }) - readonly acknowledged?: boolean + @Field(() => ServiceStatus, { nullable: true }) + readonly serviceStatus?: ServiceStatus @Field(() => String, { nullable: true }) - readonly acknowledgedDate?: string + readonly serviceDate?: string @Field(() => String, { nullable: true }) - readonly registeredBy?: string + readonly servicedBy?: string @Field(() => String, { nullable: true }) readonly comment?: string diff --git a/apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js b/apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js new file mode 100644 index 000000000000..4579f7f7b2d8 --- /dev/null +++ b/apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js @@ -0,0 +1,73 @@ +'use strict' + +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.sequelize.transaction((transaction) => + queryInterface + .changeColumn( + 'subpoena', + 'acknowledged', + { type: Sequelize.STRING, allowNull: true }, + { transaction }, + ) + .then( + () => + queryInterface.renameColumn( + 'subpoena', + 'acknowledged', + 'service_status', + { transaction }, + ), + + queryInterface.renameColumn( + 'subpoena', + 'acknowledged_date', + 'service_date', + { transaction }, + ), + + queryInterface.renameColumn( + 'subpoena', + 'registered_by', + 'serviced_by', + { transaction }, + ), + ), + ) + }, + + down: (queryInterface) => { + return queryInterface.sequelize.transaction((transaction) => + queryInterface + .renameColumn('subpoena', 'service_status', 'acknowledged', { + transaction, + }) + .then( + () => + queryInterface.changeColumn( + 'subpoena', + 'acknowledged', + { + type: 'BOOLEAN USING CAST("acknowledged" as BOOLEAN)', + allowNull: true, + }, + { transaction }, + ), + + queryInterface.renameColumn( + 'subpoena', + 'service_date', + 'acknowledged_date', + { transaction }, + ), + + queryInterface.renameColumn( + 'subpoena', + 'serviced_by', + 'registered_by', + { transaction }, + ), + ), + ) + }, +} diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts index b482b972be30..ca3417871e53 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts @@ -1,19 +1,19 @@ -import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator' +import { IsEnum, IsOptional, IsString } from 'class-validator' import { ApiPropertyOptional } from '@nestjs/swagger' -import { DefenderChoice } from '@island.is/judicial-system/types' +import { DefenderChoice, ServiceStatus } from '@island.is/judicial-system/types' export class UpdateSubpoenaDto { @IsOptional() - @IsBoolean() - @ApiPropertyOptional({ type: Boolean }) - readonly acknowledged?: boolean + @IsEnum(ServiceStatus) + @ApiPropertyOptional({ enum: ServiceStatus }) + readonly serviceStatus?: ServiceStatus @IsOptional() @IsString() @ApiPropertyOptional({ type: String }) - readonly registeredBy?: string + readonly servicedBy?: string @IsOptional() @IsString() diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts index 868ee5a7a921..f08c267d9dac 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts @@ -11,6 +11,8 @@ import { import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' +import { ServiceStatus } from '@island.is/judicial-system/types' + import { Case } from '../../case/models/case.model' import { Defendant } from '../../defendant/models/defendant.model' @@ -57,17 +59,21 @@ export class Subpoena extends Model { @ApiPropertyOptional({ type: Case }) case?: Case - @Column({ type: DataType.BOOLEAN, allowNull: true }) - @ApiPropertyOptional({ type: Boolean }) - acknowledged?: string + @Column({ + type: DataType.ENUM, + allowNull: true, + values: Object.values(ServiceStatus), + }) + @ApiPropertyOptional({ enum: ServiceStatus }) + serviceStatus?: ServiceStatus @Column({ type: DataType.DATE, allowNull: true }) @ApiPropertyOptional({ type: Date }) - acknowledgedDate?: Date + serviceDate?: Date @Column({ type: DataType.STRING, allowNull: true }) @ApiPropertyOptional({ type: String }) - registeredBy?: string + servicedBy?: string @Column({ type: DataType.TEXT, allowNull: true }) @ApiPropertyOptional({ type: String }) diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index 6716cfa9397e..95123fb3f448 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -27,9 +27,9 @@ query Case($input: CaseQueryInput!) { verdictAppealDeadline subpoenaType subpoenas { - acknowledged - acknowledgedDate - registeredBy + serviceStatus + serviceDate + servicedBy comment } } diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 173a05570ea9..4c2894d5eb6e 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -78,21 +78,23 @@ const IndictmentOverview = () => { {workingCase.defendants?.map((defendant) => defendant.subpoenas?.map((subpoena) => ( - - - {`${subpoena.registeredBy} -- ${formatDate( - subpoena.acknowledgedDate, - 'Pp', - )}`} - - {subpoena.comment} - - } - type="warning" - /> + + + + {`${subpoena.servicedBy} -- ${formatDate( + subpoena.serviceDate, + 'Pp', + )}`} + + {subpoena.comment} + + } + type="warning" + /> + )), )} {workingCase.court && diff --git a/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx b/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx index 9a4d212e1b76..cb2da97042f7 100644 --- a/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx +++ b/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx @@ -2,12 +2,11 @@ import { FC, useCallback, useContext, useState } from 'react' import { useIntl } from 'react-intl' import { useRouter } from 'next/router' -import { AlertMessage, Box, Button } from '@island.is/island-ui/core' +import { Box, Button } from '@island.is/island-ui/core' import * as constants from '@island.is/judicial-system/consts' import { isCompletedCase, isDefenceUser, - isDistrictCourtUser, } from '@island.is/judicial-system/types' import { titles } from '@island.is/judicial-system-web/messages' import { diff --git a/libs/judicial-system/types/src/index.ts b/libs/judicial-system/types/src/index.ts index 9694c67c1755..899aa61fe4b4 100644 --- a/libs/judicial-system/types/src/index.ts +++ b/libs/judicial-system/types/src/index.ts @@ -6,6 +6,7 @@ export { SubpoenaType, DefendantPlea, ServiceRequirement, + ServiceStatus, } from './lib/defendant' export { InstitutionType } from './lib/institution' export { NotificationType } from './lib/notification' diff --git a/libs/judicial-system/types/src/lib/defendant.ts b/libs/judicial-system/types/src/lib/defendant.ts index 4be363ff2622..5994ab09f293 100644 --- a/libs/judicial-system/types/src/lib/defendant.ts +++ b/libs/judicial-system/types/src/lib/defendant.ts @@ -27,3 +27,11 @@ export enum ServiceRequirement { NOT_REQUIRED = 'NOT_REQUIRED', NOT_APPLICABLE = 'NOT_APPLICABLE', } + +export enum ServiceStatus { + ELECTRONICALLY = 'ELECTRONICALLY', // Via digital mailbox on island.is + DEFENDER = 'DEFENDER', // Via a person's defender + IN_PERSON = 'IN_PERSON', + FAILED = 'FAILED', + EXPIRED = 'EXPIRED', // If a subpoena expires +} From 7f04ec80dc012681d0781a1f5afc1bfcb47ccc16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Wed, 25 Sep 2024 14:38:57 +0000 Subject: [PATCH 06/61] Checkpoint --- .../routes/Prosecutor/Indictments/Processing/Processing.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx index 63d9972e6ca3..5275f10af3ef 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx @@ -256,7 +256,11 @@ const Processing: FC = () => { ) useEffect(() => { - if (!personData || !personData.items || personData.items.length === 0) { + if (!personData || !personData.items) { + return + } + + if (personData.items.length === 0) { setNationalIdNotFound(true) return } From 26e2c1360392b614057fcc26f9f7757b6d4bf67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Wed, 25 Sep 2024 15:00:13 +0000 Subject: [PATCH 07/61] Map ServiceStatus to human readable text --- .../Indictments/Overview/Overview.strings.ts | 20 ++++++++++ .../Court/Indictments/Overview/Overview.tsx | 38 ++++++++++++++++--- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts index b479674bf746..d1fd34d85132 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts @@ -12,4 +12,24 @@ export const strings = defineMessages({ defaultMessage: 'Endursenda', description: 'Notaður sem texti á takka til að endursenda ákæru.', }, + serviceStatusSuccess: { + id: 'judicial.system.core:indictment_overview.service_status_success', + defaultMessage: 'Birting tókst', + description: 'Notaður sem texti þegar birting tókst.', + }, + serviceStatusExpired: { + id: 'judicial.system.core:indictment_overview.service_status_expired', + defaultMessage: 'Birting tókst ekki', + description: 'Notaður sem texti þegar birting rann út á tíma.', + }, + serviceStatusFailed: { + id: 'judicial.system.core:indictment_overview.service_status_failed', + defaultMessage: 'Árangurslaus birting', + description: 'Notaður sem texti þegar birting tókst ekki.', + }, + serviceStatusUnknown: { + id: 'judicial.system.core:indictment_overview.service_status_unknown', + defaultMessage: 'Birtingastaða óþekkt', + description: 'Notaður sem texti þegar ekki er vitað um stöðu birtingar.', + }, }) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 4c2894d5eb6e..13bbf34a1ace 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -1,5 +1,5 @@ import { useCallback, useContext, useState } from 'react' -import { useIntl } from 'react-intl' +import { MessageDescriptor, useIntl } from 'react-intl' import { useRouter } from 'next/router' import { AlertMessage, Box, Text } from '@island.is/island-ui/core' @@ -21,13 +21,34 @@ import { PageTitle, useIndictmentsLawsBroken, } from '@island.is/judicial-system-web/src/components' -import { IndictmentDecision } from '@island.is/judicial-system-web/src/graphql/schema' +import { + IndictmentDecision, + ServiceStatus, +} from '@island.is/judicial-system-web/src/graphql/schema' import { useDefendants } from '@island.is/judicial-system-web/src/utils/hooks' import { SubpoenaType } from '../../components' import ReturnIndictmentModal from '../ReturnIndictmentCaseModal/ReturnIndictmentCaseModal' import { strings } from './Overview.strings' +const mapServiceStatus = ( + serviceStatus?: ServiceStatus | null, +): MessageDescriptor => { + switch (serviceStatus) { + case ServiceStatus.DEFENDER: + case ServiceStatus.ELECTRONICALLY: + case ServiceStatus.IN_PERSON: + return strings.serviceStatusSuccess + case ServiceStatus.EXPIRED: + return strings.serviceStatusExpired + case ServiceStatus.FAILED: + return strings.serviceStatusFailed + // Should not happen + default: + return strings.inProgressTitle + } +} + const IndictmentOverview = () => { const router = useRouter() const { workingCase, isLoadingWorkingCase, caseNotFound, setWorkingCase } = @@ -80,11 +101,13 @@ const IndictmentOverview = () => { defendant.subpoenas?.map((subpoena) => ( - {`${subpoena.servicedBy} -- ${formatDate( + {`${subpoena.servicedBy} - ${formatDate( subpoena.serviceDate, 'Pp', )}`} @@ -92,7 +115,12 @@ const IndictmentOverview = () => { {subpoena.comment} } - type="warning" + type={ + subpoena.serviceStatus === ServiceStatus.FAILED || + subpoena.serviceStatus === ServiceStatus.EXPIRED + ? 'warning' + : 'success' + } /> )), From 1a182ed87d3f14737edb47a956f4a69b2f891878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 26 Sep 2024 11:13:59 +0000 Subject: [PATCH 08/61] Defender and electronically service --- .../defendant/models/subpoena.model.ts | 2 +- .../20240925130059-update-subpoena.js | 4 +-- .../subpoena/dto/updateSubpoena.dto.ts | 2 +- .../modules/subpoena/models/subpoena.model.ts | 2 +- .../src/components/FormProvider/case.graphql | 2 +- .../Indictments/Overview/Overview.strings.ts | 10 ++++++ .../Court/Indictments/Overview/Overview.tsx | 33 +++++++++++++++++-- 7 files changed, 47 insertions(+), 8 deletions(-) diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts index 079f9725ca0b..48250bc32368 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts @@ -31,7 +31,7 @@ export class Subpoena { readonly serviceDate?: string @Field(() => String, { nullable: true }) - readonly servicedBy?: string + readonly servedBy?: string @Field(() => String, { nullable: true }) readonly comment?: string diff --git a/apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js b/apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js index 4579f7f7b2d8..204c50fd25bf 100644 --- a/apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js +++ b/apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js @@ -29,7 +29,7 @@ module.exports = { queryInterface.renameColumn( 'subpoena', 'registered_by', - 'serviced_by', + 'served_by', { transaction }, ), ), @@ -63,7 +63,7 @@ module.exports = { queryInterface.renameColumn( 'subpoena', - 'serviced_by', + 'served_by', 'registered_by', { transaction }, ), diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts index ca3417871e53..49fce7066ac7 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts @@ -13,7 +13,7 @@ export class UpdateSubpoenaDto { @IsOptional() @IsString() @ApiPropertyOptional({ type: String }) - readonly servicedBy?: string + readonly servedBy?: string @IsOptional() @IsString() diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts index f08c267d9dac..1a9531eb49ef 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts @@ -73,7 +73,7 @@ export class Subpoena extends Model { @Column({ type: DataType.STRING, allowNull: true }) @ApiPropertyOptional({ type: String }) - servicedBy?: string + servedBy?: string @Column({ type: DataType.TEXT, allowNull: true }) @ApiPropertyOptional({ type: String }) diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index 95123fb3f448..751365ba646a 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -29,7 +29,7 @@ query Case($input: CaseQueryInput!) { subpoenas { serviceStatus serviceDate - servicedBy + servedBy comment } } diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts index d1fd34d85132..a396c451d4e3 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts @@ -32,4 +32,14 @@ export const strings = defineMessages({ defaultMessage: 'Birtingastaða óþekkt', description: 'Notaður sem texti þegar ekki er vitað um stöðu birtingar.', }, + servedToDefender: { + id: 'judicial.system.core:indictment_overview.served_to_defender', + defaultMessage: 'Birt fyrir verjanda - {lawyerName} {practice}', + description: 'Notaður sem texti þegar birti var verjanda.', + }, + servedToElectronically: { + id: 'judicial.system.core:indictment_overview.served_electronically', + defaultMessage: 'Rafrænt pósthólf island.is - {date}', + description: 'Notaður sem texti þegar birti var í pósthólfi.', + }, }) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 13bbf34a1ace..fe29a9fedb5a 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -49,6 +49,23 @@ const mapServiceStatus = ( } } +const mapComment = ( + serviceStatus?: ServiceStatus | null, +): MessageDescriptor | null => { + switch (serviceStatus) { + case ServiceStatus.DEFENDER: + return strings.servedToDefender + case ServiceStatus.ELECTRONICALLY: + return strings.servedToElectronically + case ServiceStatus.EXPIRED: + case ServiceStatus.FAILED: + case ServiceStatus.IN_PERSON: + return null + default: + return null + } +} + const IndictmentOverview = () => { const router = useRouter() const { workingCase, isLoadingWorkingCase, caseNotFound, setWorkingCase } = @@ -107,12 +124,24 @@ const IndictmentOverview = () => { message={ - {`${subpoena.servicedBy} - ${formatDate( + {`${subpoena.servedBy} - ${formatDate( subpoena.serviceDate, 'Pp', )}`} - {subpoena.comment} + + {subpoena.serviceStatus === ServiceStatus.DEFENDER + ? formatMessage(strings.servedToDefender, { + lawyerName: 'asd', + practice: 'asdasd', + }) + : subpoena.serviceStatus === + ServiceStatus.ELECTRONICALLY + ? formatMessage(strings.servedToElectronically, { + date: 'asdasd', + }) + : subpoena.comment} + } type={ From 8124dbf12e26c2b6065a879cdfe6b9e2ba7f953c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 26 Sep 2024 13:39:51 +0200 Subject: [PATCH 09/61] Schedules new subpoenas --- .../Court/Indictments/Overview/Overview.tsx | 6 +- .../Indictments/Subpoena/Subpoena.strings.ts | 6 + .../Court/Indictments/Subpoena/Subpoena.tsx | 39 ++++-- .../components/SubpoenaType/SubpoenaType.tsx | 117 ++++++++++-------- 4 files changed, 107 insertions(+), 61 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 0df16e8b0a6d..176c08156d9e 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -38,6 +38,7 @@ const IndictmentOverview = () => { const [modalVisible, setModalVisible] = useState<'RETURN_INDICTMENT'>() const latestDate = workingCase.courtDate ?? workingCase.arraignmentDate + const isArraignmentScheduled = Boolean(workingCase.arraignmentDate) // const caseHasBeenReceivedByCourt = workingCase.state === CaseState.RECEIVED const handleNavigationTo = useCallback( @@ -122,7 +123,10 @@ const IndictmentOverview = () => { { ({ + defendant, + disabled: isArraignmentScheduled, + }))} workingCase={workingCase} setWorkingCase={setWorkingCase} updateDefendantState={updateDefendantState} diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.strings.ts index 8c8c4c2c47e3..baa01967b083 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.strings.ts @@ -60,4 +60,10 @@ export const subpoena = defineMessages({ description: 'Notaður sem texti fyrir Handtökufyrirkall valkost á Fyrirkalls skjá í dómaraflæði í ákærum.', }, + newSubpoenaButtonText: { + id: 'judicial.system.core:subpoena.new_subpoena_button_text', + defaultMessage: 'Nýtt fyrirkall', + description: + 'Notaður sem texti á takka sem býður notanda að búa til nýtt fyrirkall.', + }, }) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx index 11c767d7008f..1a7e280c87e6 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx @@ -2,7 +2,7 @@ import { FC, useCallback, useContext, useState } from 'react' import { useIntl } from 'react-intl' import router from 'next/router' -import { Box } from '@island.is/island-ui/core' +import { Box, Button } from '@island.is/island-ui/core' import * as constants from '@island.is/judicial-system/consts' import { titles } from '@island.is/judicial-system-web/messages' import { @@ -30,6 +30,7 @@ const Subpoena: FC = () => { const { workingCase, setWorkingCase, isLoadingWorkingCase, caseNotFound } = useContext(FormContext) const [navigateTo, setNavigateTo] = useState() + const [newSubpoenas, setNewSubpoenas] = useState([]) const { updateDefendantState, updateDefendant } = useDefendants() const { formatMessage } = useIntl() const { @@ -40,10 +41,12 @@ const Subpoena: FC = () => { } = useCourtArrangements(workingCase, setWorkingCase, 'arraignmentDate') const isArraignmentScheduled = Boolean(workingCase.arraignmentDate) + const schedulingArraignmentDate = + !isArraignmentScheduled || newSubpoenas.length > 0 const handleNavigationTo = useCallback( async (destination: keyof stepValidationsType) => { - if (isArraignmentScheduled) { + if (!schedulingArraignmentDate) { router.push(`${destination}/${workingCase.id}`) return } @@ -70,7 +73,7 @@ const Subpoena: FC = () => { router.push(`${destination}/${workingCase.id}`) }, [ - isArraignmentScheduled, + schedulingArraignmentDate, sendCourtDateToServer, workingCase.defendants, workingCase.id, @@ -96,7 +99,27 @@ const Subpoena: FC = () => { { ({ + defendant, + disabled: + isArraignmentScheduled && + !newSubpoenas.includes(defendant.id), + children: isArraignmentScheduled && ( + + ), + }))} workingCase={workingCase} setWorkingCase={setWorkingCase} updateDefendantState={updateDefendantState} @@ -112,8 +135,8 @@ const Subpoena: FC = () => { handleCourtDateChange={handleCourtDateChange} handleCourtRoomChange={handleCourtRoomChange} courtDate={workingCase.arraignmentDate} - dateTimeDisabled={isArraignmentScheduled} - courtRoomDisabled={isArraignmentScheduled} + dateTimeDisabled={!schedulingArraignmentDate} + courtRoomDisabled={!schedulingArraignmentDate} courtRoomRequired /> @@ -147,14 +170,14 @@ const Subpoena: FC = () => { previousUrl={`${constants.INDICTMENTS_RECEPTION_AND_ASSIGNMENT_ROUTE}/${workingCase.id}`} nextIsLoading={isLoadingWorkingCase} onNextButtonClick={() => { - if (isArraignmentScheduled) { + if (!schedulingArraignmentDate) { router.push( `${constants.INDICTMENTS_DEFENDER_ROUTE}/${workingCase.id}`, ) } else setNavigateTo(constants.INDICTMENTS_DEFENDER_ROUTE) }} nextButtonText={ - isArraignmentScheduled + !schedulingArraignmentDate ? undefined : formatMessage(strings.nextButtonText) } diff --git a/apps/judicial-system/web/src/routes/Court/components/SubpoenaType/SubpoenaType.tsx b/apps/judicial-system/web/src/routes/Court/components/SubpoenaType/SubpoenaType.tsx index e30edd9bd129..3a89ef897e87 100644 --- a/apps/judicial-system/web/src/routes/Court/components/SubpoenaType/SubpoenaType.tsx +++ b/apps/judicial-system/web/src/routes/Court/components/SubpoenaType/SubpoenaType.tsx @@ -1,4 +1,4 @@ -import { Dispatch, FC, SetStateAction } from 'react' +import { Dispatch, FC, ReactNode, SetStateAction } from 'react' import { useIntl } from 'react-intl' import { Box, RadioButton, Text } from '@island.is/island-ui/core' @@ -17,7 +17,11 @@ import { strings } from './SubpoenaType.strings' import * as styles from '../../Indictments/Subpoena/Subpoena.css' interface SubpoenaTypeProps { - defendants: Defendant[] + subpoenaItems: { + defendant: Defendant + disabled?: boolean + children?: ReactNode + }[] workingCase: Case setWorkingCase: Dispatch> updateDefendantState: ( @@ -28,70 +32,79 @@ interface SubpoenaTypeProps { } const SubpoenaType: FC = ({ - defendants, + subpoenaItems, workingCase, setWorkingCase, updateDefendantState, required = true, }) => { const { formatMessage } = useIntl() - const isArraignmentDone = Boolean(workingCase.indictmentDecision) + return ( <> - {defendants.map((defendant, index) => ( + {subpoenaItems.map((item, index) => ( - - - {defendant.name} - - - { - updateDefendantState( - { - caseId: workingCase.id, - defendantId: defendant.id, - subpoenaType: SubpoenaTypeEnum.ABSENCE, - }, - setWorkingCase, - ) - }} - disabled={isArraignmentDone} - /> - { - updateDefendantState( - { - caseId: workingCase.id, - defendantId: defendant.id, - subpoenaType: SubpoenaTypeEnum.ARREST, - }, - setWorkingCase, - ) - }} - disabled={isArraignmentDone} - /> - - + + + + {item.defendant.name} + + + { + updateDefendantState( + { + caseId: workingCase.id, + defendantId: item.defendant.id, + subpoenaType: SubpoenaTypeEnum.ABSENCE, + }, + setWorkingCase, + ) + }} + disabled={item.disabled} + /> + { + updateDefendantState( + { + caseId: workingCase.id, + defendantId: item.defendant.id, + subpoenaType: SubpoenaTypeEnum.ARREST, + }, + setWorkingCase, + ) + }} + disabled={item.disabled} + /> + + + + {item.children} ))} From 5f3824ab33cc9356e8a09cd923764424be243be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 26 Sep 2024 13:52:57 +0200 Subject: [PATCH 10/61] Enforces operation ordering --- .../routes/Court/Indictments/Subpoena/Subpoena.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx index 1a7e280c87e6..d5f779a419fe 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx @@ -51,7 +51,7 @@ const Subpoena: FC = () => { return } - const promises: Promise[] = [sendCourtDateToServer()] + const promises: Promise[] = [] if (workingCase.defendants) { workingCase.defendants.forEach((defendant) => { @@ -65,8 +65,16 @@ const Subpoena: FC = () => { }) } - const allDataSentToServer = await Promise.all(promises) - if (!allDataSentToServer.every((result) => result)) { + // Make sure defendants are updated before submitting the court date + const allDefendantsUpdated = await Promise.all(promises) + + if (!allDefendantsUpdated.every((result) => result)) { + return + } + + const courtDateUpdated = await sendCourtDateToServer() + + if (!courtDateUpdated) { return } From 33fe470985c54d5bb946e2a1a223e731075aa8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 26 Sep 2024 15:08:51 +0200 Subject: [PATCH 11/61] Only sends query parameters with new subpoena requests --- .../src/app/modules/file/file.controller.ts | 12 ++++--- .../backend/src/app/formatters/subpoenaPdf.ts | 8 ++--- .../Court/Indictments/Subpoena/Subpoena.tsx | 31 ++++++++++++------- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/apps/judicial-system/api/src/app/modules/file/file.controller.ts b/apps/judicial-system/api/src/app/modules/file/file.controller.ts index 34e61e6ad93c..e705f0e43c4e 100644 --- a/apps/judicial-system/api/src/app/modules/file/file.controller.ts +++ b/apps/judicial-system/api/src/app/modules/file/file.controller.ts @@ -182,22 +182,26 @@ export class FileController { getSubpoenaPdf( @Param('id') id: string, @Param('defendantId') defendantId: string, - @Query('arraignmentDate') arraignmentDate: string, - @Query('location') location: string, - @Query('subpoenaType') subpoenaType: SubpoenaType, @CurrentHttpUser() user: User, @Req() req: Request, @Res() res: Response, + @Query('arraignmentDate') arraignmentDate?: string, + @Query('location') location?: string, + @Query('subpoenaType') subpoenaType?: SubpoenaType, ): Promise { this.logger.debug( `Getting the subpoena for defendant ${defendantId} of case ${id} as a pdf document`, ) + const queryInjection = arraignmentDate + ? `?arraignmentDate=${arraignmentDate}&location=${location}&subpoenaType=${subpoenaType}` + : '' + return this.fileService.tryGetFile( user.id, AuditedAction.GET_SUBPOENA_PDF, id, - `defendant/${defendantId}/subpoena?arraignmentDate=${arraignmentDate}&location=${location}&subpoenaType=${subpoenaType}`, + `defendant/${defendantId}/subpoena${queryInjection}`, req, res, 'pdf', diff --git a/apps/judicial-system/backend/src/app/formatters/subpoenaPdf.ts b/apps/judicial-system/backend/src/app/formatters/subpoenaPdf.ts index 4af8001053e8..b2b3096a0eb6 100644 --- a/apps/judicial-system/backend/src/app/formatters/subpoenaPdf.ts +++ b/apps/judicial-system/backend/src/app/formatters/subpoenaPdf.ts @@ -43,9 +43,9 @@ export const createSubpoena = ( }) const sinc: Buffer[] = [] - const dateLog = theCase.dateLogs?.find( - (d) => d.dateType === DateType.ARRAIGNMENT_DATE, - ) + const dateLog = arraignmentDate + ? undefined + : theCase.dateLogs?.find((d) => d.dateType === DateType.ARRAIGNMENT_DATE) doc.on('data', (chunk) => sinc.push(chunk)) @@ -159,7 +159,7 @@ export const createSubpoena = ( actor: theCase.judge?.name || '', title: theCase.judge?.title, institution: theCase.judge?.institution?.name || '', - date: dateLog.created, + date: dateLog.modified, }) } diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx index d5f779a419fe..e2cbe58c2604 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx @@ -41,12 +41,16 @@ const Subpoena: FC = () => { } = useCourtArrangements(workingCase, setWorkingCase, 'arraignmentDate') const isArraignmentScheduled = Boolean(workingCase.arraignmentDate) - const schedulingArraignmentDate = + const isSchedulingArraignmentDate = !isArraignmentScheduled || newSubpoenas.length > 0 + const isSchedulingArraignmentDateForDefendant = (defendantId: string) => + !isArraignmentScheduled || + (isArraignmentScheduled && newSubpoenas.includes(defendantId)) + const handleNavigationTo = useCallback( async (destination: keyof stepValidationsType) => { - if (!schedulingArraignmentDate) { + if (!isSchedulingArraignmentDate) { router.push(`${destination}/${workingCase.id}`) return } @@ -81,7 +85,7 @@ const Subpoena: FC = () => { router.push(`${destination}/${workingCase.id}`) }, [ - schedulingArraignmentDate, + isSchedulingArraignmentDate, sendCourtDateToServer, workingCase.defendants, workingCase.id, @@ -143,8 +147,8 @@ const Subpoena: FC = () => { handleCourtDateChange={handleCourtDateChange} handleCourtRoomChange={handleCourtRoomChange} courtDate={workingCase.arraignmentDate} - dateTimeDisabled={!schedulingArraignmentDate} - courtRoomDisabled={!schedulingArraignmentDate} + dateTimeDisabled={!isSchedulingArraignmentDate} + courtRoomDisabled={!isSchedulingArraignmentDate} courtRoomRequired /> @@ -161,12 +165,17 @@ const Subpoena: FC = () => { title={`Fyrirkall - ${defendant.name} - PDF`} pdfType="subpoena" disabled={ - !courtDate?.date || - !courtDate?.location || - !defendant.subpoenaType + isSchedulingArraignmentDateForDefendant(defendant.id) && + (!courtDate?.date || + !courtDate?.location || + !defendant.subpoenaType) } elementId={defendant.id} - queryParameters={`arraignmentDate=${courtDate?.date}&location=${courtDate?.location}&subpoenaType=${defendant.subpoenaType}`} + queryParameters={ + isSchedulingArraignmentDateForDefendant(defendant.id) + ? `arraignmentDate=${courtDate?.date}&location=${courtDate?.location}&subpoenaType=${defendant.subpoenaType}` + : undefined + } /> ))} @@ -178,14 +187,14 @@ const Subpoena: FC = () => { previousUrl={`${constants.INDICTMENTS_RECEPTION_AND_ASSIGNMENT_ROUTE}/${workingCase.id}`} nextIsLoading={isLoadingWorkingCase} onNextButtonClick={() => { - if (!schedulingArraignmentDate) { + if (!isSchedulingArraignmentDate) { router.push( `${constants.INDICTMENTS_DEFENDER_ROUTE}/${workingCase.id}`, ) } else setNavigateTo(constants.INDICTMENTS_DEFENDER_ROUTE) }} nextButtonText={ - !schedulingArraignmentDate + !isSchedulingArraignmentDate ? undefined : formatMessage(strings.nextButtonText) } From e5d9426dafb56171689da25055ba521074658c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 26 Sep 2024 13:39:53 +0000 Subject: [PATCH 12/61] Service to defender --- .../defendant/models/subpoena.model.ts | 3 + .../20240925130059-update-subpoena.js | 14 +++ .../modules/subpoena/models/subpoena.model.ts | 4 + .../src/components/FormProvider/case.graphql | 1 + .../Court/Indictments/Overview/Overview.tsx | 102 +++++++++++------- 5 files changed, 86 insertions(+), 38 deletions(-) diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts index 48250bc32368..10b3ef759f34 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts @@ -35,4 +35,7 @@ export class Subpoena { @Field(() => String, { nullable: true }) readonly comment?: string + + @Field(() => String, { nullable: true }) + readonly defenderNationalId?: string } diff --git a/apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js b/apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js index 204c50fd25bf..43ae74f21010 100644 --- a/apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js +++ b/apps/judicial-system/backend/migrations/20240925130059-update-subpoena.js @@ -32,6 +32,16 @@ module.exports = { 'served_by', { transaction }, ), + + queryInterface.addColumn( + 'subpoena', + 'defender_national_id', + { + type: Sequelize.STRING, + allowNull: true, + }, + { transaction }, + ), ), ) }, @@ -67,6 +77,10 @@ module.exports = { 'registered_by', { transaction }, ), + + queryInterface.removeColumn('subpoena', 'defender_national_id', { + transaction, + }), ), ) }, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts index 1a9531eb49ef..778b05cefaf8 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts @@ -78,4 +78,8 @@ export class Subpoena extends Model { @Column({ type: DataType.TEXT, allowNull: true }) @ApiPropertyOptional({ type: String }) comment?: string + + @Column({ type: DataType.STRING, allowNull: true }) + @ApiPropertyOptional({ type: String }) + defenderNationalId?: string } diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index 1146e3bfae7e..86ba091ad011 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -31,6 +31,7 @@ query Case($input: CaseQueryInput!) { serviceDate servedBy comment + defenderNationalId } } defenderName diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index d7971feee131..229eadda4b27 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -1,4 +1,4 @@ -import { useCallback, useContext, useState } from 'react' +import { FC, useCallback, useContext, useState } from 'react' import { MessageDescriptor, useIntl } from 'react-intl' import { useRouter } from 'next/router' @@ -24,8 +24,12 @@ import { import { IndictmentDecision, ServiceStatus, + Subpoena, } from '@island.is/judicial-system-web/src/graphql/schema' -import { useDefendants } from '@island.is/judicial-system-web/src/utils/hooks' +import { + useDefendants, + useGetLawyer, +} from '@island.is/judicial-system-web/src/utils/hooks' import { SubpoenaType } from '../../components' import ReturnIndictmentModal from '../ReturnIndictmentCaseModal/ReturnIndictmentCaseModal' @@ -66,6 +70,60 @@ const mapComment = ( } } +interface ServiceAnnouncement { + subpoena: Subpoena + defendantName?: string | null +} + +const ServiceAnnouncement: FC = (props) => { + const { subpoena, defendantName } = props + + const { formatMessage } = useIntl() + + const lawyer = useGetLawyer( + subpoena.defenderNationalId, + subpoena.serviceStatus === ServiceStatus.DEFENDER, + ) + + return !defendantName ? null : ( + + + + {`${subpoena.servedBy} - ${formatDate( + subpoena.serviceDate, + 'Pp', + )}`} + + + {subpoena.serviceStatus === ServiceStatus.DEFENDER + ? formatMessage(strings.servedToDefender, { + lawyerName: lawyer?.name, + practice: lawyer?.practice, + }) + : subpoena.serviceStatus === ServiceStatus.ELECTRONICALLY + ? formatMessage(strings.servedToElectronically, { + date: 'asdasd', + }) + : subpoena.comment} + + + } + type={ + subpoena.serviceStatus === ServiceStatus.FAILED || + subpoena.serviceStatus === ServiceStatus.EXPIRED + ? 'warning' + : 'success' + } + /> + + ) +} + const IndictmentOverview = () => { const router = useRouter() const { workingCase, isLoadingWorkingCase, caseNotFound, setWorkingCase } = @@ -116,42 +174,10 @@ const IndictmentOverview = () => { {workingCase.defendants?.map((defendant) => defendant.subpoenas?.map((subpoena) => ( - - - - {`${subpoena.servedBy} - ${formatDate( - subpoena.serviceDate, - 'Pp', - )}`} - - - {subpoena.serviceStatus === ServiceStatus.DEFENDER - ? formatMessage(strings.servedToDefender, { - lawyerName: 'asd', - practice: 'asdasd', - }) - : subpoena.serviceStatus === - ServiceStatus.ELECTRONICALLY - ? formatMessage(strings.servedToElectronically, { - date: 'asdasd', - }) - : subpoena.comment} - - - } - type={ - subpoena.serviceStatus === ServiceStatus.FAILED || - subpoena.serviceStatus === ServiceStatus.EXPIRED - ? 'warning' - : 'success' - } - /> - + )), )} {workingCase.court && From b8e5d0f58fe470c720e621dcaad5e73407438526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 26 Sep 2024 14:48:36 +0000 Subject: [PATCH 13/61] Sort in asc order --- .../src/app/modules/case/case.service.ts | 2 +- .../Indictments/Overview/Overview.strings.ts | 5 ++ .../Court/Indictments/Overview/Overview.tsx | 74 ++++++++++--------- 3 files changed, 45 insertions(+), 36 deletions(-) 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 e7261151e999..579194e823da 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 @@ -276,7 +276,7 @@ export const include: Includeable[] = [ model: Defendant, as: 'defendants', include: [ - { model: Subpoena, as: 'subpoenas', order: [['created', 'DESC']] }, + { model: Subpoena, as: 'subpoenas', order: [['created', 'ASC']] }, ], }, { model: CivilClaimant, as: 'civilClaimants' }, diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts index a396c451d4e3..580e935364e3 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts @@ -22,6 +22,11 @@ export const strings = defineMessages({ defaultMessage: 'Birting tókst ekki', description: 'Notaður sem texti þegar birting rann út á tíma.', }, + serviceStatusExpiredMessage: { + id: 'judicial.system.core:indictment_overview.service_status_expired_message', + defaultMessage: 'Ekki tókst að birta fyrir þingfestingu.', + description: 'Notaður sem texti þegar birting rann út á tíma.', + }, serviceStatusFailed: { id: 'judicial.system.core:indictment_overview.service_status_failed', defaultMessage: 'Árangurslaus birting', diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 229eadda4b27..1cc6e3e5281a 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -1,10 +1,11 @@ import { FC, useCallback, useContext, useState } from 'react' -import { MessageDescriptor, useIntl } from 'react-intl' +import { IntlShape, MessageDescriptor, useIntl } from 'react-intl' import { useRouter } from 'next/router' import { Accordion, AlertMessage, Box, Text } from '@island.is/island-ui/core' import * as constants from '@island.is/judicial-system/consts' import { formatDate } from '@island.is/judicial-system/formatters' +import { type Lawyer } from '@island.is/judicial-system/types' import { core, titles } from '@island.is/judicial-system-web/messages' import { ConnectedCaseFilesAccordionItem, @@ -30,14 +31,15 @@ import { useDefendants, useGetLawyer, } from '@island.is/judicial-system-web/src/utils/hooks' +import { FormatMessage } from '@island.is/localization' import { SubpoenaType } from '../../components' import ReturnIndictmentModal from '../ReturnIndictmentCaseModal/ReturnIndictmentCaseModal' import { strings } from './Overview.strings' -const mapServiceStatus = ( +const mapServiceStatusTitle = ( serviceStatus?: ServiceStatus | null, -): MessageDescriptor => { +): MessageDescriptor | null => { switch (serviceStatus) { case ServiceStatus.DEFENDER: case ServiceStatus.ELECTRONICALLY: @@ -49,24 +51,40 @@ const mapServiceStatus = ( return strings.serviceStatusFailed // Should not happen default: - return strings.inProgressTitle + return null } } -const mapComment = ( - serviceStatus?: ServiceStatus | null, -): MessageDescriptor | null => { - switch (serviceStatus) { +const mapServiceStatusMessages = ( + subpoena: Subpoena, + formatMessage: IntlShape['formatMessage'], + lawyer?: Lawyer, +) => { + switch (subpoena.serviceStatus) { case ServiceStatus.DEFENDER: - return strings.servedToDefender + return [ + `${subpoena.servedBy} - ${formatDate(subpoena.serviceDate, 'Pp')}`, + formatMessage(strings.servedToDefender, { + lawyerName: lawyer?.name, + practice: lawyer?.practice, + }), + ] case ServiceStatus.ELECTRONICALLY: - return strings.servedToElectronically - case ServiceStatus.EXPIRED: - case ServiceStatus.FAILED: + return [ + formatMessage(strings.servedToElectronically, { + date: formatDate(subpoena.serviceDate, 'Pp'), + }), + ] case ServiceStatus.IN_PERSON: - return null + case ServiceStatus.FAILED: + return [ + `${subpoena.servedBy} - ${formatDate(subpoena.serviceDate, 'Pp')}`, + subpoena.comment, + ] + case ServiceStatus.EXPIRED: + return [formatMessage(strings.serviceStatusExpiredMessage)] default: - return null + return [] } } @@ -85,32 +103,18 @@ const ServiceAnnouncement: FC = (props) => { subpoena.serviceStatus === ServiceStatus.DEFENDER, ) + const title = mapServiceStatusTitle(subpoena.serviceStatus) + const messages = mapServiceStatusMessages(subpoena, formatMessage, lawyer) + return !defendantName ? null : ( - - {`${subpoena.servedBy} - ${formatDate( - subpoena.serviceDate, - 'Pp', - )}`} - - - {subpoena.serviceStatus === ServiceStatus.DEFENDER - ? formatMessage(strings.servedToDefender, { - lawyerName: lawyer?.name, - practice: lawyer?.practice, - }) - : subpoena.serviceStatus === ServiceStatus.ELECTRONICALLY - ? formatMessage(strings.servedToElectronically, { - date: 'asdasd', - }) - : subpoena.comment} - + {messages.map((msg) => ( + {msg} + ))} } type={ From 332031fbcbd882b912f438eef9ac4584cdcb4a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 26 Sep 2024 15:13:07 +0000 Subject: [PATCH 14/61] Cleanup --- .../api/src/app/modules/defendant/models/subpoena.model.ts | 2 +- .../web/src/routes/Court/Indictments/Overview/Overview.tsx | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts index 10b3ef759f34..8327e57a4646 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts @@ -16,7 +16,7 @@ export class Subpoena { readonly modified?: string @Field(() => ID, { nullable: true }) - readonly subpoenaId!: string + readonly subpoenaId?: string @Field(() => ID, { nullable: true }) readonly defendantId?: string diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 1cc6e3e5281a..1a33bd05746e 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -31,7 +31,6 @@ import { useDefendants, useGetLawyer, } from '@island.is/judicial-system-web/src/utils/hooks' -import { FormatMessage } from '@island.is/localization' import { SubpoenaType } from '../../components' import ReturnIndictmentModal from '../ReturnIndictmentCaseModal/ReturnIndictmentCaseModal' @@ -39,7 +38,7 @@ import { strings } from './Overview.strings' const mapServiceStatusTitle = ( serviceStatus?: ServiceStatus | null, -): MessageDescriptor | null => { +): MessageDescriptor => { switch (serviceStatus) { case ServiceStatus.DEFENDER: case ServiceStatus.ELECTRONICALLY: @@ -51,7 +50,7 @@ const mapServiceStatusTitle = ( return strings.serviceStatusFailed // Should not happen default: - return null + return strings.serviceStatusUnknown } } @@ -109,7 +108,7 @@ const ServiceAnnouncement: FC = (props) => { return !defendantName ? null : ( {messages.map((msg) => ( From 9fd338dd133f4f9e2df8f382311dd3888e228aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 26 Sep 2024 15:26:21 +0000 Subject: [PATCH 15/61] Checkpoint --- .../src/app/modules/police/police.service.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index c4659a31c6de..3cbd581d89ad 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -30,6 +30,7 @@ import { AwsS3Service } from '../aws-s3' import { Case } from '../case' import { Defendant } from '../defendant/models/defendant.model' import { EventService } from '../event' +import { Subpoena } from '../subpoena/models/subpoena.model' import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { CreateSubpoenaResponse } from './models/createSubpoena.response' import { PoliceCaseFile } from './models/policeCaseFile.model' @@ -122,6 +123,17 @@ export class PoliceService { skjol: z.optional(z.array(this.policeCaseFileStructure)), malseinings: z.optional(z.array(this.crimeSceneStructure)), }) + private subpoenaStructure = z.object({ + Acknowledged: z.boolean(), + Comment: z.string(), + DefenderChoice: z.string(), + DefenderNationalId: z.string(), + ProsecutedConfirmedSubpoenaThroughIslandis: z.boolean(), + ServedBy: z.string(), + Delivered: z.boolean(), + DeliveredOnPaper: z.boolean(), + DeliveredToLawyer: z.boolean(), + }) constructor( @Inject(policeModuleConfig.KEY) @@ -316,6 +328,19 @@ export class PoliceService { }) } + async getSubpoenaStatus(subpoenaId: string): Promise { + return this.fetchPoliceDocumentApi( + `${this.xRoadPath}/GetSubpoenaStatus?id=${subpoenaId}`, + ).then(async (res: Response) => { + if (res.ok) { + const response: z.infer = + await res.json() + + return response + } + }) + } + async getPoliceCaseInfo( caseId: string, user: User, From 414ec712a6a00efec0c723432c17d2f77f69becd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 27 Sep 2024 15:24:49 +0200 Subject: [PATCH 16/61] Supports multiple subpoenas per defendant --- .../defendant/models/defendant.model.ts | 5 + .../defendant/models/subpoena.model.ts | 31 ++++ .../src/app/modules/file/file.controller.ts | 6 +- .../20240926131706-update-subpoena.js | 61 +++++++ .../backend/src/app/formatters/subpoenaPdf.ts | 20 +-- .../src/app/modules/case/case.module.ts | 2 + .../src/app/modules/case/case.service.ts | 149 ++++++++++++------ .../app/modules/case/internalCase.service.ts | 2 +- .../modules/case/limitedAccessCase.service.ts | 19 ++- .../src/app/modules/case/pdf.service.ts | 3 + .../modules/defendant/defendant.controller.ts | 60 +------ .../app/modules/defendant/defendant.module.ts | 5 +- .../src/app/modules/defendant/index.ts | 2 + .../subpoena/guards/subpoenaExists.guard.ts | 39 ++++- .../backend/src/app/modules/subpoena/index.ts | 2 + .../subpoena/internalSubpoena.controller.ts | 21 +-- .../limitedAccessSubpoena.controller.ts} | 22 ++- .../modules/subpoena/models/subpoena.model.ts | 12 +- .../modules/subpoena/subpoena.controller.ts | 109 +++++++++++++ .../app/modules/subpoena/subpoena.module.ts | 11 +- .../app/modules/subpoena/subpoena.service.ts | 36 ++++- .../src/app/messageHandler.service.ts | 8 +- .../src/components/FormProvider/case.graphql | 4 + .../FormProvider/limitedAccessCase.graphql | 4 + .../IndictmentCaseFilesList.strings.ts | 4 +- .../IndictmentCaseFilesList.tsx | 37 +++-- .../src/components/PdfButton/PdfButton.tsx | 6 +- .../Court/Indictments/Subpoena/Subpoena.tsx | 78 ++++++--- .../message/src/lib/message.ts | 2 +- 29 files changed, 552 insertions(+), 208 deletions(-) create mode 100644 apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts create mode 100644 apps/judicial-system/backend/migrations/20240926131706-update-subpoena.js create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/index.ts rename apps/judicial-system/backend/src/app/modules/{defendant/limitedAccessDefendant.controller.ts => subpoena/limitedAccessSubpoena.controller.ts} (70%) create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts index cc761d4b363a..595b696e2c28 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts @@ -8,6 +8,8 @@ import { SubpoenaType, } from '@island.is/judicial-system/types' +import { Subpoena } from './subpoena.model' + registerEnumType(Gender, { name: 'Gender' }) registerEnumType(DefendantPlea, { name: 'DefendantPlea' }) registerEnumType(ServiceRequirement, { name: 'ServiceRequirement' }) @@ -75,4 +77,7 @@ export class Defendant { @Field(() => SubpoenaType, { nullable: true }) readonly subpoenaType?: SubpoenaType + + @Field(() => [Subpoena], { nullable: true }) + readonly subpoenas?: Subpoena[] } diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts new file mode 100644 index 000000000000..9ba54e366b30 --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts @@ -0,0 +1,31 @@ +import { Field, ID, ObjectType } from '@nestjs/graphql' + +@ObjectType() +export class Subpoena { + @Field(() => ID) + readonly id!: string + + @Field(() => String, { nullable: true }) + created?: string + + @Field(() => String, { nullable: true }) + modified?: string + + @Field(() => String, { nullable: true }) + subpoenaId?: string + + @Field(() => String, { nullable: true }) + acknowledged?: string + + @Field(() => String, { nullable: true }) + registeredBy?: string + + @Field(() => String, { nullable: true }) + comment?: string + + @Field(() => String, { nullable: true }) + arraignmentDate?: string + + @Field(() => String, { nullable: true }) + location?: string +} diff --git a/apps/judicial-system/api/src/app/modules/file/file.controller.ts b/apps/judicial-system/api/src/app/modules/file/file.controller.ts index e705f0e43c4e..d278438871f3 100644 --- a/apps/judicial-system/api/src/app/modules/file/file.controller.ts +++ b/apps/judicial-system/api/src/app/modules/file/file.controller.ts @@ -177,11 +177,12 @@ export class FileController { ) } - @Get('subpoena/:defendantId') + @Get(['subpoena/:defendantId', 'subpoena/:defendantId/:subpoenaId']) @Header('Content-Type', 'application/pdf') getSubpoenaPdf( @Param('id') id: string, @Param('defendantId') defendantId: string, + @Param('subpoenaId') subpoenaId: string, @CurrentHttpUser() user: User, @Req() req: Request, @Res() res: Response, @@ -193,6 +194,7 @@ export class FileController { `Getting the subpoena for defendant ${defendantId} of case ${id} as a pdf document`, ) + const subpoenaIdInjection = subpoenaId ? `/${subpoenaId}` : '' const queryInjection = arraignmentDate ? `?arraignmentDate=${arraignmentDate}&location=${location}&subpoenaType=${subpoenaType}` : '' @@ -201,7 +203,7 @@ export class FileController { user.id, AuditedAction.GET_SUBPOENA_PDF, id, - `defendant/${defendantId}/subpoena${queryInjection}`, + `defendant/${defendantId}/subpoena${subpoenaIdInjection}${queryInjection}`, req, res, 'pdf', diff --git a/apps/judicial-system/backend/migrations/20240926131706-update-subpoena.js b/apps/judicial-system/backend/migrations/20240926131706-update-subpoena.js new file mode 100644 index 000000000000..f4e74d657c49 --- /dev/null +++ b/apps/judicial-system/backend/migrations/20240926131706-update-subpoena.js @@ -0,0 +1,61 @@ +'use strict' + +module.exports = { + up(queryInterface, Sequelize) { + return queryInterface.sequelize.transaction((transaction) => + Promise.all([ + queryInterface.addColumn( + 'subpoena', + 'arraignment_date', + { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + { transaction }, + ), + queryInterface.addColumn( + 'subpoena', + 'location', + { type: Sequelize.STRING, allowNull: false, defaultValue: 'óþekkt' }, + { transaction }, + ), + queryInterface.changeColumn( + 'subpoena', + 'case_id', + { + type: Sequelize.UUID, + allowNull: false, + }, + { transaction }, + ), + ]).then(() => + queryInterface.sequelize.query( + `ALTER TABLE subpoena ALTER COLUMN arraignment_date DROP DEFAULT; + ALTER TABLE subpoena ALTER COLUMN location DROP DEFAULT;`, + { transaction }, + ), + ), + ) + }, + + down(queryInterface, Sequelize) { + return queryInterface.sequelize.transaction((transaction) => + Promise.all([ + queryInterface.removeColumn('subpoena', 'arraignment_date', { + transaction, + }), + queryInterface.removeColumn('subpoena', 'location', { transaction }), + queryInterface.changeColumn( + 'subpoena', + 'case_id', + { + type: Sequelize.UUID, + allowNull: true, + }, + { transaction }, + ), + ]), + ) + }, +} diff --git a/apps/judicial-system/backend/src/app/formatters/subpoenaPdf.ts b/apps/judicial-system/backend/src/app/formatters/subpoenaPdf.ts index b2b3096a0eb6..95567e0bd7a4 100644 --- a/apps/judicial-system/backend/src/app/formatters/subpoenaPdf.ts +++ b/apps/judicial-system/backend/src/app/formatters/subpoenaPdf.ts @@ -7,11 +7,13 @@ import { formatDOB, lowercase, } from '@island.is/judicial-system/formatters' -import { DateType, SubpoenaType } from '@island.is/judicial-system/types' +import { SubpoenaType } from '@island.is/judicial-system/types' +import { nowFactory } from '../factories/date.factory' import { subpoena as strings } from '../messages' import { Case } from '../modules/case' import { Defendant } from '../modules/defendant' +import { Subpoena } from '../modules/subpoena' import { addConfirmation, addEmptyLines, @@ -27,6 +29,7 @@ export const createSubpoena = ( theCase: Case, defendant: Defendant, formatMessage: FormatMessage, + subpoena?: Subpoena, arraignmentDate?: Date, location?: string, subpoenaType?: SubpoenaType, @@ -43,15 +46,12 @@ export const createSubpoena = ( }) const sinc: Buffer[] = [] - const dateLog = arraignmentDate - ? undefined - : theCase.dateLogs?.find((d) => d.dateType === DateType.ARRAIGNMENT_DATE) doc.on('data', (chunk) => sinc.push(chunk)) setTitle(doc, formatMessage(strings.title)) - if (dateLog) { + if (subpoena) { addEmptyLines(doc, 5) } @@ -59,12 +59,12 @@ export const createSubpoena = ( addNormalRightAlignedText( doc, - `${formatDate(new Date(dateLog?.modified ?? new Date()), 'PPP')}`, + `${formatDate(new Date(subpoena?.created ?? nowFactory()), 'PPP')}`, 'Times-Roman', ) - arraignmentDate = arraignmentDate ?? dateLog?.date - location = location ?? dateLog?.location + arraignmentDate = arraignmentDate ?? subpoena?.arraignmentDate + location = location ?? subpoena?.location subpoenaType = subpoenaType ?? defendant.subpoenaType if (theCase.court?.name) { @@ -154,12 +154,12 @@ export const createSubpoena = ( addFooter(doc) - if (dateLog) { + if (subpoena) { addConfirmation(doc, { actor: theCase.judge?.name || '', title: theCase.judge?.title, institution: theCase.judge?.institution?.name || '', - date: dateLog.modified, + date: subpoena.created, }) } diff --git a/apps/judicial-system/backend/src/app/modules/case/case.module.ts b/apps/judicial-system/backend/src/app/modules/case/case.module.ts index 10d099b3068b..139bff373cfe 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.module.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.module.ts @@ -15,6 +15,7 @@ import { FileModule, IndictmentCountModule, PoliceModule, + SubpoenaModule, UserModule, } from '../index' import { Case } from './models/case.model' @@ -35,6 +36,7 @@ import { PdfService } from './pdf.service' CmsTranslationsModule, MessageModule, forwardRef(() => DefendantModule), + forwardRef(() => SubpoenaModule), forwardRef(() => UserModule), forwardRef(() => FileModule), forwardRef(() => IndictmentCountModule), 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 65ad33c4d2ee..eb56a53fbe28 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 @@ -62,6 +62,7 @@ import { CaseFile, FileService } from '../file' import { IndictmentCount } from '../indictment-count' import { Institution } from '../institution' import { Notification } from '../notification' +import { Subpoena, SubpoenaService } from '../subpoena' import { User } from '../user' import { CreateCaseDto } from './dto/createCase.dto' import { getCasesQueryFilter } from './filters/cases.filter' @@ -271,7 +272,22 @@ export const include: Includeable[] = [ ], }, { model: Case, as: 'childCase' }, - { model: Defendant, as: 'defendants' }, + { + model: Defendant, + as: 'defendants', + required: false, + order: [['created', 'ASC']], + include: [ + { + model: Subpoena, + as: 'subpoenas', + required: false, + order: [['created', 'DESC']], + separate: true, + }, + ], + separate: true, + }, { model: CivilClaimant, as: 'civilClaimants' }, { model: IndictmentCount, as: 'indictmentCounts' }, { @@ -340,7 +356,6 @@ export const include: Includeable[] = [ ] export const order: OrderItem[] = [ - [{ model: Defendant, as: 'defendants' }, 'created', 'ASC'], [{ model: CivilClaimant, as: 'civilClaimants' }, 'created', 'ASC'], [{ model: IndictmentCount, as: 'indictmentCounts' }, 'created', 'ASC'], [{ model: DateLog, as: 'dateLogs' }, 'created', 'DESC'], @@ -412,6 +427,7 @@ export class CaseService { @Inject(caseModuleConfig.KEY) private readonly config: ConfigType, private readonly defendantService: DefendantService, + private readonly subpoenaService: SubpoenaService, private readonly fileService: FileService, private readonly awsS3Service: AwsS3Service, private readonly courtService: CourtService, @@ -1146,29 +1162,42 @@ export class CaseService { private addMessagesForNewCourtDateToQueue( theCase: Case, user: TUser, - arraignmentDateChanged: boolean, ): Promise { - const messages: Message[] = [ + return this.messageService.sendMessagesToQueue([ { type: MessageType.NOTIFICATION, user, caseId: theCase.id, body: { type: NotificationType.COURT_DATE }, }, - ] + ]) + } - if (arraignmentDateChanged) { - theCase.defendants?.forEach((defendant) => { - messages.push({ - type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, - user, - caseId: theCase.id, - elementId: defendant.id, - }) - }) + private addMessagesForNewSubpoenasToQueue( + theCase: Case, + updatedCase: Case, + user: TUser, + ) { + const messages = updatedCase.defendants + ?.filter( + (updatedDefendant) => + theCase.defendants?.find( + (defendant) => defendant.id === updatedDefendant.id, + )?.subpoenas?.[0].id !== updatedDefendant.subpoenas?.[0].id, + ) + .map((updatedDefendant) => ({ + type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, + user, + caseId: theCase.id, + elementId: [ + updatedDefendant.id, + updatedDefendant.subpoenas?.[0].id ?? '', + ], + })) + + if (messages && messages.length > 0) { + return this.messageService.sendMessagesToQueue(messages) } - - return this.messageService.sendMessagesToQueue(messages) } private async addMessagesForUpdatedCaseToQueue( @@ -1344,12 +1373,10 @@ export class CaseService { if (arraignmentDateChanged || courtDateChanged) { // New arraignment date or new court date - await this.addMessagesForNewCourtDateToQueue( - updatedCase, - user, - Boolean(arraignmentDateChanged), - ) + await this.addMessagesForNewCourtDateToQueue(updatedCase, user) } + + await this.addMessagesForNewSubpoenasToQueue(theCase, updatedCase, user) } } @@ -1628,8 +1655,18 @@ export class CaseService { isIndictmentCase(theCase.type) && update.state === CaseState.DRAFT && theCase.state === CaseState.RECEIVED - const completingIndictmentCase = - isIndictmentCase(theCase.type) && update.state === CaseState.COMPLETED + const completingIndictmentCaseWithoutRuling = + isIndictmentCase(theCase.type) && + update.state === CaseState.COMPLETED && + theCase.indictmentRulingDecision && + [ + CaseIndictmentRulingDecision.FINE, + CaseIndictmentRulingDecision.CANCELLATION, + CaseIndictmentRulingDecision.MERGE, + ].includes(theCase.indictmentRulingDecision) + const updatedArraignmentDate = update.arraignmentDate + const schedulingNewArraignmentDateForIndictmentCase = + isIndictmentCase(theCase.type) && Boolean(updatedArraignmentDate) return this.sequelize .transaction(async (transaction) => { @@ -1646,24 +1683,22 @@ export class CaseService { await this.handleCommentUpdates(theCase, update, transaction) await this.handleEventLogs(theCase, update, user, transaction) - if (Object.keys(update).length === 0) { - return - } - - const [numberOfAffectedRows] = await this.caseModel.update(update, { - where: { id: theCase.id }, - transaction, - }) + if (Object.keys(update).length > 0) { + const [numberOfAffectedRows] = await this.caseModel.update(update, { + where: { id: theCase.id }, + transaction, + }) - if (numberOfAffectedRows > 1) { - // Tolerate failure, but log error - this.logger.error( - `Unexpected number of rows (${numberOfAffectedRows}) affected when updating case ${theCase.id}`, - ) - } else if (numberOfAffectedRows < 1) { - throw new InternalServerErrorException( - `Could not update case ${theCase.id}`, - ) + if (numberOfAffectedRows > 1) { + // Tolerate failure, but log error + this.logger.error( + `Unexpected number of rows (${numberOfAffectedRows}) affected when updating case ${theCase.id}`, + ) + } else if (numberOfAffectedRows < 1) { + throw new InternalServerErrorException( + `Could not update case ${theCase.id}`, + ) + } } // Update police case numbers of case files if necessary @@ -1689,22 +1724,34 @@ export class CaseService { ) } - if ( - completingIndictmentCase && - theCase.indictmentRulingDecision && - [ - CaseIndictmentRulingDecision.FINE, - CaseIndictmentRulingDecision.CANCELLATION, - ].includes(theCase.indictmentRulingDecision) - ) { + // Remove uploaded ruling files if an indictment case is completed without a ruling + if (completingIndictmentCaseWithoutRuling && theCase.caseFiles) { await Promise.all( theCase.caseFiles - ?.filter( + .filter( (caseFile) => caseFile.category === CaseFileCategory.RULING, ) - ?.map((caseFile) => + .map((caseFile) => this.fileService.deleteCaseFile(theCase, caseFile, transaction), - ) ?? [], + ), + ) + } + + // Create new subpoeans if scheduling a new arraignment date for an indictment case + if ( + schedulingNewArraignmentDateForIndictmentCase && + theCase.defendants + ) { + await Promise.all( + theCase.defendants.map((defendant) => + this.subpoenaService.createSubpoena( + defendant.id, + theCase.id, + updatedArraignmentDate?.date, + updatedArraignmentDate?.location, + transaction, + ), + ), ) } }) diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts index 4ef8b7b5a247..26377502db54 100644 --- a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts @@ -58,7 +58,7 @@ import { CaseFile, FileService } from '../file' import { IndictmentCount, IndictmentCountService } from '../indictment-count' import { Institution } from '../institution' import { PoliceDocument, PoliceDocumentType, PoliceService } from '../police' -import { Subpoena } from '../subpoena/models/subpoena.model' +import { Subpoena } from '../subpoena' import { User, UserService } from '../user' import { InternalCreateCaseDto } from './dto/internalCreateCase.dto' import { archiveFilter } from './filters/case.archiveFilter' diff --git a/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts b/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts index fa2e9b5bbaeb..efeaf459c19a 100644 --- a/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts @@ -38,6 +38,7 @@ import { } from '../file' import { IndictmentCount } from '../indictment-count' import { Institution } from '../institution' +import { Subpoena } from '../subpoena' import { User } from '../user' import { Case } from './models/case.model' import { CaseString } from './models/caseString.model' @@ -170,7 +171,22 @@ export const include: Includeable[] = [ }, { model: Case, as: 'parentCase', attributes }, { model: Case, as: 'childCase', attributes }, - { model: Defendant, as: 'defendants' }, + { + model: Defendant, + as: 'defendants', + required: false, + order: [['created', 'ASC']], + include: [ + { + model: Subpoena, + as: 'subpoenas', + required: false, + order: [['created', 'DESC']], + separate: true, + }, + ], + separate: true, + }, { model: IndictmentCount, as: 'indictmentCounts' }, { model: CivilClaimant, as: 'civilClaimants' }, { @@ -255,7 +271,6 @@ export const include: Includeable[] = [ ] export const order: OrderItem[] = [ - [{ model: Defendant, as: 'defendants' }, 'created', 'ASC'], [{ model: IndictmentCount, as: 'indictmentCounts' }, 'created', 'ASC'], [{ model: CivilClaimant, as: 'civilClaimants' }, 'created', 'ASC'], [{ model: DateLog, as: 'dateLogs' }, 'created', 'DESC'], diff --git a/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts b/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts index 6b956856ebb1..140501e17c59 100644 --- a/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts @@ -33,6 +33,7 @@ import { } from '../../formatters' import { AwsS3Service } from '../aws-s3' import { Defendant } from '../defendant' +import { Subpoena } from '../subpoena' import { UserService } from '../user' import { Case } from './models/case.model' @@ -292,6 +293,7 @@ export class PdfService { async getSubpoenaPdf( theCase: Case, defendant: Defendant, + subpoena: Subpoena, arraignmentDate?: Date, location?: string, subpoenaType?: SubpoenaType, @@ -302,6 +304,7 @@ export class PdfService { theCase, defendant, this.formatMessage, + subpoena, arraignmentDate, location, subpoenaType, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts b/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts index cb7ab62524ed..94bfed1fc023 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts @@ -1,17 +1,11 @@ -import { Response } from 'express' - import { Body, Controller, Delete, - Get, - Header, Inject, Param, Patch, Post, - Query, - Res, UseGuards, } from '@nestjs/common' import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger' @@ -25,12 +19,7 @@ import { RolesGuard, RolesRules, } from '@island.is/judicial-system/auth' -import { - indictmentCases, - ServiceRequirement, - SubpoenaType, - type User, -} from '@island.is/judicial-system/types' +import { ServiceRequirement, type User } from '@island.is/judicial-system/types' import { districtCourtAssistantRule, @@ -43,8 +32,6 @@ import { import { Case, CaseExistsGuard, - CaseReadGuard, - CaseTypeGuard, CaseWriteGuard, CurrentCase, PdfService, @@ -148,49 +135,4 @@ export class DefendantController { return { deleted } } - - @UseGuards( - CaseExistsGuard, - new CaseTypeGuard(indictmentCases), - CaseReadGuard, - DefendantExistsGuard, - ) - @RolesRules( - prosecutorRule, - prosecutorRepresentativeRule, - publicProsecutorStaffRule, - districtCourtJudgeRule, - districtCourtRegistrarRule, - districtCourtAssistantRule, - ) - @Get(':defendantId/subpoena') - @Header('Content-Type', 'application/pdf') - @ApiOkResponse({ - content: { 'application/pdf': {} }, - description: 'Gets the subpoena for a given defendant as a pdf document', - }) - async getSubpoenaPdf( - @Param('caseId') caseId: string, - @Param('defendantId') defendantId: string, - @CurrentCase() theCase: Case, - @CurrentDefendant() defendant: Defendant, - @Res() res: Response, - @Query('arraignmentDate') arraignmentDate?: Date, - @Query('location') location?: string, - @Query('subpoenaType') subpoenaType?: SubpoenaType, - ): Promise { - this.logger.debug( - `Getting the subpoena for defendant ${defendantId} of case ${caseId} as a pdf document`, - ) - - const pdf = await this.pdfService.getSubpoenaPdf( - theCase, - defendant, - arraignmentDate, - location, - subpoenaType, - ) - - res.end(pdf) - } } diff --git a/apps/judicial-system/backend/src/app/modules/defendant/defendant.module.ts b/apps/judicial-system/backend/src/app/modules/defendant/defendant.module.ts index 7fc42f0f16fa..a7cb4fca9e0f 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/defendant.module.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/defendant.module.ts @@ -5,7 +5,6 @@ import { MessageModule } from '@island.is/judicial-system/message' import { CaseModule } from '../case/case.module' import { CourtModule } from '../court/court.module' -import { Subpoena } from '../subpoena/models/subpoena.model' import { CivilClaimant } from './models/civilClaimant.model' import { Defendant } from './models/defendant.model' import { CivilClaimantController } from './civilClaimant.controller' @@ -13,19 +12,17 @@ import { CivilClaimantService } from './civilClaimant.service' import { DefendantController } from './defendant.controller' import { DefendantService } from './defendant.service' import { InternalDefendantController } from './internalDefendant.controller' -import { LimitedAccessDefendantController } from './limitedAccessDefendant.controller' @Module({ imports: [ MessageModule, forwardRef(() => CourtModule), forwardRef(() => CaseModule), - SequelizeModule.forFeature([Defendant, CivilClaimant, Subpoena]), + SequelizeModule.forFeature([Defendant, CivilClaimant]), ], controllers: [ DefendantController, InternalDefendantController, - LimitedAccessDefendantController, CivilClaimantController, ], providers: [DefendantService, CivilClaimantService], diff --git a/apps/judicial-system/backend/src/app/modules/defendant/index.ts b/apps/judicial-system/backend/src/app/modules/defendant/index.ts index 3839cdb96469..ed6753c1f764 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/index.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/index.ts @@ -1,3 +1,5 @@ export { Defendant } from './models/defendant.model' export { DefendantService } from './defendant.service' export { CivilClaimant } from './models/civilClaimant.model' +export { DefendantExistsGuard } from './guards/defendantExists.guard' +export { CurrentDefendant } from './guards/defendant.decorator' diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts index 0280c3f51f99..71a5672010c6 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts @@ -5,6 +5,7 @@ import { Injectable, } from '@nestjs/common' +import { Defendant } from '../../defendant' import { SubpoenaService } from '../subpoena.service' @Injectable() @@ -20,8 +21,44 @@ export class SubpoenaExistsGuard implements CanActivate { throw new BadRequestException('Missing subpoena id') } - request.subpoena = await this.subpoenaService.findBySubpoenaId(subpoenaId) + const defendant: Defendant = request.defendant + + if (!defendant) { + request.subpoena = await this.subpoenaService.findBySubpoenaId(subpoenaId) + + return true + } + + const subpoena = defendant.subpoenas?.find( + (subpoena) => subpoena.id === subpoenaId, + ) + + if (!subpoena) { + throw new BadRequestException( + `Subpoena ${subpoenaId} of defendant ${defendant.id} does not exist`, + ) + } + + request.subpoena = subpoena return true } } + +@Injectable() +export class SubpoenaExistsOptionalGuard extends SubpoenaExistsGuard { + constructor(subpoenaService: SubpoenaService) { + super(subpoenaService) + } + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest() + + const subpoenaId = request.params.subpoenaId + + if (!subpoenaId) { + return true + } + + return super.canActivate(context) + } +} diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/index.ts b/apps/judicial-system/backend/src/app/modules/subpoena/index.ts new file mode 100644 index 000000000000..828b617f4f30 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/index.ts @@ -0,0 +1,2 @@ +export { SubpoenaService } from './subpoena.service' +export { Subpoena } from './models/subpoena.model' diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts index a0f909375a80..aef0e88065b6 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts @@ -1,5 +1,3 @@ -import { Base64 } from 'js-base64' - import { Body, Controller, @@ -22,12 +20,7 @@ import { } from '@island.is/judicial-system/message' import { indictmentCases } from '@island.is/judicial-system/types' -import { - CaseExistsGuard, - CaseTypeGuard, - CurrentCase, - PdfService, -} from '../case' +import { CaseExistsGuard, CaseTypeGuard, CurrentCase } from '../case' import { Case } from '../case/models/case.model' import { CurrentDefendant } from '../defendant/guards/defendant.decorator' import { DefendantExistsGuard } from '../defendant/guards/defendantExists.guard' @@ -46,7 +39,6 @@ import { SubpoenaService } from './subpoena.service' export class InternalSubpoenaController { constructor( private readonly subpoenaService: SubpoenaService, - private readonly pdfService: PdfService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} @@ -77,11 +69,12 @@ export class InternalSubpoenaController { CaseExistsGuard, new CaseTypeGuard(indictmentCases), DefendantExistsGuard, + SubpoenaExistsGuard, ) @Post( `case/:caseId/${ messageEndpoint[MessageType.DELIVERY_TO_POLICE_SUBPOENA] - }/:defendantId`, + }/:defendantId/:subpoenaId`, ) @ApiOkResponse({ type: DeliverResponse, @@ -90,20 +83,20 @@ export class InternalSubpoenaController { async deliverSubpoenaToPolice( @Param('caseId') caseId: string, @Param('defendantId') defendantId: string, + @Param('subpoenaId') subpoenaId: string, @CurrentCase() theCase: Case, @CurrentDefendant() defendant: Defendant, + @CurrentSubpoena() subpoena: Subpoena, @Body() deliverDto: DeliverDto, ): Promise { this.logger.debug( - `Delivering subpoena ${caseId} to police for defendant ${defendantId}`, + `Delivering subpoena ${subpoenaId} to police for defendant ${defendantId} og case ${caseId}`, ) - const pdf = await this.pdfService.getSubpoenaPdf(theCase, defendant) - return await this.subpoenaService.deliverSubpoenaToPolice( theCase, defendant, - Base64.btoa(pdf.toString('binary')), + subpoena, deliverDto.user, ) } diff --git a/apps/judicial-system/backend/src/app/modules/defendant/limitedAccessDefendant.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts similarity index 70% rename from apps/judicial-system/backend/src/app/modules/defendant/limitedAccessDefendant.controller.ts rename to apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts index d2e79ab2ef40..9a4c195e465e 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/limitedAccessDefendant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts @@ -31,11 +31,15 @@ import { CurrentCase, PdfService, } from '../case' -import { CurrentDefendant } from './guards/defendant.decorator' -import { DefendantExistsGuard } from './guards/defendantExists.guard' -import { Defendant } from './models/defendant.model' +import { CurrentDefendant, Defendant, DefendantExistsGuard } from '../defendant' +import { CurrentSubpoena } from './guards/subpoena.decorator' +import { SubpoenaExistsOptionalGuard } from './guards/subpoenaExists.guard' +import { Subpoena } from './models/subpoena.model' -@Controller('api/case/:caseId/limitedAccess/defendant/:defendantId/subpoena') +@Controller([ + 'api/case/:caseId/limitedAccess/defendant/:defendantId/subpoena', + 'api/case/:caseId/limitedAccess/defendant/:defendantId/subpoena/:subpoenaId', +]) @UseGuards( JwtAuthGuard, RolesGuard, @@ -43,9 +47,10 @@ import { Defendant } from './models/defendant.model' new CaseTypeGuard(indictmentCases), CaseReadGuard, DefendantExistsGuard, + SubpoenaExistsOptionalGuard, ) @ApiTags('limited access defendants') -export class LimitedAccessDefendantController { +export class LimitedAccessSubpoenaController { constructor( private readonly pdfService: PdfService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, @@ -61,20 +66,25 @@ export class LimitedAccessDefendantController { async getSubpoenaPdf( @Param('caseId') caseId: string, @Param('defendantId') defendantId: string, + @Param('subpoenaId') subpoenaId: string, @CurrentCase() theCase: Case, @CurrentDefendant() defendant: Defendant, + @CurrentSubpoena() subpoena: Subpoena, @Res() res: Response, @Query('arraignmentDate') arraignmentDate?: Date, @Query('location') location?: string, @Query('subpoenaType') subpoenaType?: SubpoenaType, ): Promise { this.logger.debug( - `Getting the subpoena for defendant ${defendantId} of case ${caseId} as a pdf document`, + `Getting subpoena ${ + subpoenaId ?? 'draft' + } for defendant ${defendantId} of case ${caseId} as a pdf document`, ) const pdf = await this.pdfService.getSubpoenaPdf( theCase, defendant, + subpoena, arraignmentDate, location, subpoenaType, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts index 5a540fd3af27..933b1633e1bb 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts @@ -49,9 +49,9 @@ export class Subpoena extends Model { defendant?: Defendant @ForeignKey(() => Case) - @Column({ type: DataType.UUID, allowNull: true }) + @Column({ type: DataType.UUID }) @ApiProperty({ type: String }) - caseId?: string + caseId!: string @BelongsTo(() => Case, 'caseId') @ApiPropertyOptional({ type: Case }) @@ -68,4 +68,12 @@ export class Subpoena extends Model { @Column({ type: DataType.TEXT, allowNull: true }) @ApiPropertyOptional({ type: String }) comment?: string + + @Column({ type: DataType.DATE, allowNull: false }) + @ApiProperty({ type: Date }) + arraignmentDate!: Date + + @Column({ type: DataType.STRING, allowNull: false }) + @ApiProperty({ type: String }) + location!: string } diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts new file mode 100644 index 000000000000..e6f4ad8abc51 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts @@ -0,0 +1,109 @@ +import { Response } from 'express' + +import { + Controller, + Get, + Header, + Inject, + Param, + Query, + Res, + UseGuards, +} from '@nestjs/common' +import { ApiOkResponse, ApiTags } from '@nestjs/swagger' + +import { type Logger, LOGGER_PROVIDER } from '@island.is/logging' + +import { + JwtAuthGuard, + RolesGuard, + RolesRules, +} from '@island.is/judicial-system/auth' +import { indictmentCases, SubpoenaType } from '@island.is/judicial-system/types' + +import { + districtCourtAssistantRule, + districtCourtJudgeRule, + districtCourtRegistrarRule, + prosecutorRepresentativeRule, + prosecutorRule, + publicProsecutorStaffRule, +} from '../../guards' +import { + Case, + CaseExistsGuard, + CaseReadGuard, + CaseTypeGuard, + CurrentCase, + PdfService, +} from '../case' +import { Defendant } from '../defendant' +import { CurrentDefendant } from '../defendant/guards/defendant.decorator' +import { DefendantExistsGuard } from '../defendant/guards/defendantExists.guard' +import { CurrentSubpoena } from './guards/subpoena.decorator' +import { SubpoenaExistsOptionalGuard } from './guards/subpoenaExists.guard' +import { Subpoena } from './models/subpoena.model' + +@UseGuards(JwtAuthGuard, RolesGuard) +@Controller([ + 'api/case/:caseId/defendant/:defendantId/subpoena', + 'api/case/:caseId/defendant/:defendantId/subpoena/:subpoenaId', +]) +@ApiTags('subpoenas') +export class SubpoenaController { + constructor( + private readonly pdfService: PdfService, + @Inject(LOGGER_PROVIDER) private readonly logger: Logger, + ) {} + + @UseGuards( + CaseExistsGuard, + new CaseTypeGuard(indictmentCases), + CaseReadGuard, + DefendantExistsGuard, + SubpoenaExistsOptionalGuard, + ) + @RolesRules( + prosecutorRule, + prosecutorRepresentativeRule, + publicProsecutorStaffRule, + districtCourtJudgeRule, + districtCourtRegistrarRule, + districtCourtAssistantRule, + ) + @Get() + @Header('Content-Type', 'application/pdf') + @ApiOkResponse({ + content: { 'application/pdf': {} }, + description: 'Gets the subpoena for a given defendant as a pdf document', + }) + async getSubpoenaPdf( + @Param('caseId') caseId: string, + @Param('defendantId') defendantId: string, + @Param('subpoenaId') subpoenaId: string, + @CurrentCase() theCase: Case, + @CurrentDefendant() defendant: Defendant, + @CurrentSubpoena() subpoena: Subpoena, + @Res() res: Response, + @Query('arraignmentDate') arraignmentDate?: Date, + @Query('location') location?: string, + @Query('subpoenaType') subpoenaType?: SubpoenaType, + ): Promise { + this.logger.debug( + `Getting subpoena ${ + subpoenaId ?? 'draft' + } for defendant ${defendantId} of case ${caseId} as a pdf document`, + ) + + const pdf = await this.pdfService.getSubpoenaPdf( + theCase, + defendant, + subpoena, + arraignmentDate, + location, + subpoenaType, + ) + + res.end(pdf) + } +} diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts index 1f40a7844f60..260e2154cfb5 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts @@ -2,21 +2,26 @@ import { forwardRef, Module } from '@nestjs/common' import { SequelizeModule } from '@nestjs/sequelize' import { CaseModule } from '../case/case.module' -import { DefendantModule } from '../defendant/defendant.module' import { Defendant } from '../defendant/models/defendant.model' import { PoliceModule } from '../police/police.module' import { Subpoena } from './models/subpoena.model' import { InternalSubpoenaController } from './internalSubpoena.controller' +import { LimitedAccessSubpoenaController } from './limitedAccessSubpoena.controller' +import { SubpoenaController } from './subpoena.controller' import { SubpoenaService } from './subpoena.service' @Module({ imports: [ forwardRef(() => CaseModule), - forwardRef(() => DefendantModule), forwardRef(() => PoliceModule), SequelizeModule.forFeature([Subpoena, Defendant]), ], - controllers: [InternalSubpoenaController], + controllers: [ + SubpoenaController, + InternalSubpoenaController, + LimitedAccessSubpoenaController, + ], providers: [SubpoenaService], + exports: [SubpoenaService], }) export class SubpoenaModule {} diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index c28cf4ec4875..08d0c4a83149 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -1,3 +1,4 @@ +import { Base64 } from 'js-base64' import { Includeable, Sequelize } from 'sequelize' import { Transaction } from 'sequelize/types' @@ -10,6 +11,7 @@ import { LOGGER_PROVIDER } from '@island.is/logging' import type { User } from '@island.is/judicial-system/types' import { Case } from '../case/models/case.model' +import { PdfService } from '../case/pdf.service' import { Defendant } from '../defendant/models/defendant.model' import { PoliceService } from '../police' import { UpdateSubpoenaDto } from './dto/updateSubpoena.dto' @@ -26,16 +28,28 @@ export class SubpoenaService { @InjectConnection() private readonly sequelize: Sequelize, @InjectModel(Subpoena) private readonly subpoenaModel: typeof Subpoena, @InjectModel(Defendant) private readonly defendantModel: typeof Defendant, + private readonly pdfService: PdfService, @Inject(forwardRef(() => PoliceService)) private readonly policeService: PoliceService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} - async createSubpoena(defendant: Defendant): Promise { - return await this.subpoenaModel.create({ - defendantId: defendant.id, - caseId: defendant.caseId, - }) + async createSubpoena( + defendantId: string, + caseId: string, + arraignmentDate?: Date, + location?: string, + transaction?: Transaction, + ): Promise { + return this.subpoenaModel.create( + { + defendantId, + caseId, + arraignmentDate, + location, + }, + { transaction }, + ) } async update( @@ -108,21 +122,26 @@ export class SubpoenaService { async deliverSubpoenaToPolice( theCase: Case, defendant: Defendant, - subpoenaFile: string, + subpoena: Subpoena, user: User, ): Promise { try { - const subpoena = await this.createSubpoena(defendant) + const pdf = await this.pdfService.getSubpoenaPdf( + theCase, + defendant, + subpoena, + ) const createdSubpoena = await this.policeService.createSubpoena( theCase, defendant, - subpoenaFile, + Base64.btoa(pdf.toString('binary')), user, ) if (!createdSubpoena) { this.logger.error('Failed to create subpoena file for police') + return { delivered: false } } @@ -135,6 +154,7 @@ export class SubpoenaService { return { delivered: true } } catch (error) { this.logger.error('Error delivering subpoena to police', error) + return { delivered: false } } } diff --git a/apps/judicial-system/message-handler/src/app/messageHandler.service.ts b/apps/judicial-system/message-handler/src/app/messageHandler.service.ts index 98edbe4c608d..56f7488dd2d5 100644 --- a/apps/judicial-system/message-handler/src/app/messageHandler.service.ts +++ b/apps/judicial-system/message-handler/src/app/messageHandler.service.ts @@ -33,7 +33,13 @@ export class MessageHandlerService implements OnModuleDestroy { `${this.config.backendUrl}/api/internal${ message.caseId ? `/case/${message.caseId}` : '' }/${messageEndpoint[message.type]}${ - message.elementId ? `/${message.elementId}` : '' + message.elementId + ? `/${ + Array.isArray(message.elementId) + ? message.elementId.join('/') + : message.elementId + }` + : '' }`, message.user, message.body, diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index f6eeefc5ba75..64acb88c88c8 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -26,6 +26,10 @@ query Case($input: CaseQueryInput!) { verdictViewDate verdictAppealDeadline subpoenaType + subpoenas { + id + created + } } defenderName defenderNationalId diff --git a/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql b/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql index 774715ba84f3..a5ba9f00d180 100644 --- a/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql @@ -35,6 +35,10 @@ query LimitedAccessCase($input: CaseQueryInput!) { defenderPhoneNumber defenderChoice verdictViewDate + subpoenas { + id + created + } } defenderName defenderNationalId diff --git a/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.strings.ts b/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.strings.ts index 8372e2fbbc45..a6e7ee8bee11 100644 --- a/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.strings.ts +++ b/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.strings.ts @@ -40,8 +40,8 @@ export const strings = defineMessages({ description: 'Notaður sem titill á dómskjalaskjá í ákærum.', }, subpoenaButtonText: { - id: 'judicial.system.indictments:indictment_case_files_list.subpoena_button_text', - defaultMessage: 'Fyrirkall {name}.pdf', + id: 'judicial.system.indictments:indictment_case_files_list.subpoena_button_text_v2', + defaultMessage: 'Fyrirkall {name} {date}.pdf', description: 'Notaður sem texti á PDF takka til að sækja firyrkall í ákærum.', }, diff --git a/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx b/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx index 99d714df2a3e..f38145abd551 100644 --- a/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx +++ b/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx @@ -73,7 +73,9 @@ const IndictmentCaseFilesList: FC = ({ }) const showTrafficViolationCaseFiles = isTrafficViolationCase(workingCase) - const showSubpoenaPdf = workingCase.arraignmentDate + const showSubpoenaPdf = workingCase.defendants?.some( + (defendant) => defendant.subpoenas && defendant.subpoenas.length > 0, + ) const cf = workingCase.caseFiles @@ -227,28 +229,35 @@ const IndictmentCaseFilesList: FC = ({ )} - {showSubpoenaPdf && - workingCase.defendants && - workingCase.defendants.length > 0 && ( - - - {formatMessage(strings.subpoenaTitle)} - - {workingCase.defendants.map((defendant) => ( - + {showSubpoenaPdf && ( + + + {formatMessage(strings.subpoenaTitle)} + + {workingCase.defendants?.map((defendant) => + defendant.subpoenas?.map((subpoena) => ( + - ))} - - )} + )), + )} + + )} {fileNotFound && } diff --git a/apps/judicial-system/web/src/components/PdfButton/PdfButton.tsx b/apps/judicial-system/web/src/components/PdfButton/PdfButton.tsx index 8ff7a5bd0fb0..987d1fd4a624 100644 --- a/apps/judicial-system/web/src/components/PdfButton/PdfButton.tsx +++ b/apps/judicial-system/web/src/components/PdfButton/PdfButton.tsx @@ -22,7 +22,7 @@ interface Props { disabled?: boolean renderAs?: 'button' | 'row' handleClick?: () => void - elementId?: string + elementId?: string | string[] queryParameters?: string } @@ -46,7 +46,9 @@ const PdfButton: FC> = ({ const prefix = `${limitedAccess ? 'limitedAccess/' : ''}${ connectedCaseParentId ? `mergedCase/${caseId}/` : '' }` - const postfix = elementId ? `/${elementId}` : '' + const postfix = elementId + ? `/${Array.isArray(elementId) ? elementId.join('/') : elementId}` + : '' const query = queryParameters ? `?${queryParameters}` : '' const url = `${api.apiUrl}/api/case/${ connectedCaseParentId ?? caseId diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx index e2cbe58c2604..77b8cca8d436 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx @@ -153,31 +153,59 @@ const Subpoena: FC = () => { /> - {workingCase.defendants?.map((defendant, index) => ( - - - + {workingCase.defendants?.map((defendant, dIndex) => ( + <> + {isSchedulingArraignmentDateForDefendant(defendant.id) && ( + + + + )} + {defendant.subpoenas?.map((subpoena, sIndex) => ( + + + + ))} + ))} diff --git a/libs/judicial-system/message/src/lib/message.ts b/libs/judicial-system/message/src/lib/message.ts index 99bfc42f74b0..1b5aaf576864 100644 --- a/libs/judicial-system/message/src/lib/message.ts +++ b/libs/judicial-system/message/src/lib/message.ts @@ -66,7 +66,7 @@ export type Message = { type: MessageType user?: User caseId?: string - elementId?: string + elementId?: string | string[] body?: { [key: string]: unknown } numberOfRetries?: number nextRetry?: number From 778daa16883eccfdb25f4211b607f20e4ea0c3c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 27 Sep 2024 19:09:17 +0200 Subject: [PATCH 17/61] Updates unit tests --- .../src/app/modules/case/case.service.ts | 2 +- .../case/test/caseController/update.spec.ts | 12 +- .../modules/defendant/defendant.controller.ts | 9 +- .../test/createTestingDefendantModule.ts | 20 +--- .../app/modules/subpoena/subpoena.service.ts | 2 +- .../test/createTestingSubpoenaModule.ts | 110 ++++++++++++++++++ .../getSubpoenaPdf.spec.ts | 19 +-- .../getSubpoenaPdfRolesRules.spec.ts | 6 +- ...tedAccessSubpoenaControllerGuards.spec.ts} | 12 +- .../getSubpoenaPdf.spec.ts | 20 ++-- .../getSubpoenaPdfGuards.spec.ts | 12 +- .../getSubpoenaPdfRolesRules.spec.ts | 6 +- 12 files changed, 170 insertions(+), 60 deletions(-) create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts rename apps/judicial-system/backend/src/app/modules/{defendant/test/defendantController => subpoena/test/limitedAccessSubpoenaController}/getSubpoenaPdf.spec.ts (68%) rename apps/judicial-system/backend/src/app/modules/{defendant/test/limitedAccessDefendantController => subpoena/test/limitedAccessSubpoenaController}/getSubpoenaPdfRolesRules.spec.ts (59%) rename apps/judicial-system/backend/src/app/modules/{defendant/test/limitedAccessDefendantController/limitedAccessDefendantControllerGuards.spec.ts => subpoena/test/limitedAccessSubpoenaController/limitedAccessSubpoenaControllerGuards.spec.ts} (62%) rename apps/judicial-system/backend/src/app/modules/{defendant/test/limitedAccessDefendantController => subpoena/test/subpoenaController}/getSubpoenaPdf.spec.ts (67%) rename apps/judicial-system/backend/src/app/modules/{defendant/test/defendantController => subpoena/test/subpoenaController}/getSubpoenaPdfGuards.spec.ts (61%) rename apps/judicial-system/backend/src/app/modules/{defendant/test/defendantController => subpoena/test/subpoenaController}/getSubpoenaPdfRolesRules.spec.ts (80%) 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 eb56a53fbe28..1e5011a092ae 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 @@ -1747,9 +1747,9 @@ export class CaseService { this.subpoenaService.createSubpoena( defendant.id, theCase.id, + transaction, updatedArraignmentDate?.date, updatedArraignmentDate?.location, - transaction, ), ), ) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts index 932319cac89e..4939ca40beb3 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts @@ -875,10 +875,16 @@ describe('CaseController - Update', () => { describe('indictment arraignment date updated', () => { const arraignmentDate = { date: new Date(), location: uuid() } const caseToUpdate = { arraignmentDate } + const subpoenaId1 = uuid() + const subpoenaId2 = uuid() const updatedCase = { ...theCase, type: CaseType.INDICTMENT, dateLogs: [{ dateType: DateType.ARRAIGNMENT_DATE, ...arraignmentDate }], + defendants: [ + { id: defendantId1, subpoenas: [{ id: subpoenaId1 }] }, + { id: defendantId2, subpoenas: [{ id: subpoenaId2 }] }, + ], } beforeEach(async () => { @@ -900,17 +906,19 @@ describe('CaseController - Update', () => { caseId, body: { type: NotificationType.COURT_DATE }, }, + ]) + expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([ { type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, user, caseId: theCase.id, - elementId: defendantId1, + elementId: [defendantId1, subpoenaId1], }, { type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, user, caseId: theCase.id, - elementId: defendantId2, + elementId: [defendantId2, subpoenaId2], }, ]) }) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts b/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts index 94bfed1fc023..83edd4cd8b1e 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts @@ -29,13 +29,7 @@ import { prosecutorRule, publicProsecutorStaffRule, } from '../../guards' -import { - Case, - CaseExistsGuard, - CaseWriteGuard, - CurrentCase, - PdfService, -} from '../case' +import { Case, CaseExistsGuard, CaseWriteGuard, CurrentCase } from '../case' import { CreateDefendantDto } from './dto/createDefendant.dto' import { UpdateDefendantDto } from './dto/updateDefendant.dto' import { CurrentDefendant } from './guards/defendant.decorator' @@ -49,7 +43,6 @@ import { DefendantService } from './defendant.service' @ApiTags('defendants') export class DefendantController { constructor( - private readonly pdfService: PdfService, private readonly defendantService: DefendantService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts index 8cb18eb62706..3b18ac9b8c8c 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts @@ -10,36 +10,29 @@ import { } from '@island.is/judicial-system/auth' import { MessageService } from '@island.is/judicial-system/message' -import { CaseService, PdfService } from '../../case' +import { CaseService } from '../../case' import { CourtService } from '../../court' import { UserService } from '../../user' import { DefendantController } from '../defendant.controller' import { DefendantService } from '../defendant.service' import { InternalDefendantController } from '../internalDefendant.controller' -import { LimitedAccessDefendantController } from '../limitedAccessDefendant.controller' import { Defendant } from '../models/defendant.model' jest.mock('@island.is/judicial-system/message') jest.mock('../../user/user.service') jest.mock('../../court/court.service') jest.mock('../../case/case.service') -jest.mock('../../case/pdf.service') export const createTestingDefendantModule = async () => { const defendantModule = await Test.createTestingModule({ imports: [ConfigModule.forRoot({ load: [sharedAuthModuleConfig] })], - controllers: [ - DefendantController, - InternalDefendantController, - LimitedAccessDefendantController, - ], + controllers: [DefendantController, InternalDefendantController], providers: [ SharedAuthModule, MessageService, UserService, CourtService, CaseService, - PdfService, { provide: LOGGER_PROVIDER, useValue: { @@ -69,8 +62,6 @@ export const createTestingDefendantModule = async () => { const courtService = defendantModule.get(CourtService) - const pdfService = defendantModule.get(PdfService) - const defendantModel = await defendantModule.resolve( getModelToken(Defendant), ) @@ -86,22 +77,15 @@ export const createTestingDefendantModule = async () => { InternalDefendantController, ) - const limitedAccessDefendantController = - defendantModule.get( - LimitedAccessDefendantController, - ) - defendantModule.close() return { messageService, userService, courtService, - pdfService, defendantModel, defendantService, defendantController, internalDefendantController, - limitedAccessDefendantController, } } diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 08d0c4a83149..aa2fe114ef7f 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -37,9 +37,9 @@ export class SubpoenaService { async createSubpoena( defendantId: string, caseId: string, + transaction: Transaction, arraignmentDate?: Date, location?: string, - transaction?: Transaction, ): Promise { return this.subpoenaModel.create( { diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts new file mode 100644 index 000000000000..a931f5fd7bfd --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts @@ -0,0 +1,110 @@ +import { Sequelize } from 'sequelize-typescript' + +import { getModelToken } from '@nestjs/sequelize' +import { Test } from '@nestjs/testing' + +import { LOGGER_PROVIDER } from '@island.is/logging' +import { ConfigModule } from '@island.is/nest/config' + +import { + SharedAuthModule, + sharedAuthModuleConfig, +} from '@island.is/judicial-system/auth' + +import { CaseService, PdfService } from '../../case' +import { Defendant } from '../../defendant' +import { PoliceService } from '../../police' +import { UserService } from '../../user' +import { InternalSubpoenaController } from '../internalSubpoena.controller' +import { LimitedAccessSubpoenaController } from '../limitedAccessSubpoena.controller' +import { Subpoena } from '../models/subpoena.model' +import { SubpoenaController } from '../subpoena.controller' +import { SubpoenaService } from '../subpoena.service' + +jest.mock('../../user/user.service') +jest.mock('../../case/case.service') +jest.mock('../../case/pdf.service') +jest.mock('../../police/police.service') + +export const createTestingSubpoenaModule = async () => { + const subpoenaModule = await Test.createTestingModule({ + imports: [ConfigModule.forRoot({ load: [sharedAuthModuleConfig] })], + controllers: [ + SubpoenaController, + InternalSubpoenaController, + LimitedAccessSubpoenaController, + ], + providers: [ + { provide: Sequelize, useValue: { transaction: jest.fn() } }, + SharedAuthModule, + UserService, + CaseService, + PdfService, + PoliceService, + { + provide: LOGGER_PROVIDER, + useValue: { + debug: jest.fn(), + info: jest.fn(), + error: jest.fn(), + }, + }, + { + provide: getModelToken(Subpoena), + useValue: { + findOne: jest.fn(), + findAll: jest.fn(), + create: jest.fn(), + update: jest.fn(), + destroy: jest.fn(), + findByPk: jest.fn(), + }, + }, + { + provide: getModelToken(Defendant), + useValue: { + findOne: jest.fn(), + findAll: jest.fn(), + create: jest.fn(), + update: jest.fn(), + destroy: jest.fn(), + findByPk: jest.fn(), + }, + }, + SubpoenaService, + ], + }).compile() + + const userService = subpoenaModule.get(UserService) + + const pdfService = subpoenaModule.get(PdfService) + + const subpoenaModel = await subpoenaModule.resolve( + getModelToken(Subpoena), + ) + + const subpoenaService = subpoenaModule.get(SubpoenaService) + + const subpoenaController = + subpoenaModule.get(SubpoenaController) + + const internalSubpoenaController = + subpoenaModule.get(InternalSubpoenaController) + + const limitedAccessSubpoenaController = + subpoenaModule.get( + LimitedAccessSubpoenaController, + ) + + subpoenaModule.close() + + return { + userService, + pdfService, + subpoenaModel, + subpoenaService, + subpoenaController, + internalSubpoenaController, + limitedAccessSubpoenaController, + } +} diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/getSubpoenaPdf.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/limitedAccessSubpoenaController/getSubpoenaPdf.spec.ts similarity index 68% rename from apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/getSubpoenaPdf.spec.ts rename to apps/judicial-system/backend/src/app/modules/subpoena/test/limitedAccessSubpoenaController/getSubpoenaPdf.spec.ts index 632fc703f493..cda75ddd2fbe 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/getSubpoenaPdf.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/limitedAccessSubpoenaController/getSubpoenaPdf.spec.ts @@ -1,10 +1,10 @@ import { Response } from 'express' import { uuid } from 'uuidv4' -import { createTestingDefendantModule } from '../createTestingDefendantModule' +import { createTestingSubpoenaModule } from '../createTestingSubpoenaModule' import { Case, PdfService } from '../../../case' -import { Defendant } from '../../models/defendant.model' +import { Subpoena } from '../../models/subpoena.model' interface Then { error: Error @@ -12,10 +12,12 @@ interface Then { type GivenWhenThen = () => Promise -describe('DefendantController - Get subpoena pdf', () => { +describe('LimitedAccessSubpoenaController - Get subpoena pdf', () => { const caseId = uuid() + const subpoenaId = uuid() + const subpoena = { id: subpoenaId } as Subpoena const defendantId = uuid() - const defendant = { id: defendantId } as Defendant + const defendant = { id: defendantId, subpoenas: [subpoena] } as Defendant const theCase = { id: caseId } as Case const res = { end: jest.fn() } as unknown as Response const pdf = Buffer.from(uuid()) @@ -23,8 +25,8 @@ describe('DefendantController - Get subpoena pdf', () => { let givenWhenThen: GivenWhenThen beforeEach(async () => { - const { pdfService, defendantController } = - await createTestingDefendantModule() + const { pdfService, limitedAccessSubpoenaController } = + await createTestingSubpoenaModule() mockPdfService = pdfService const getSubpoenaPdfMock = mockPdfService.getSubpoenaPdf as jest.Mock @@ -34,11 +36,13 @@ describe('DefendantController - Get subpoena pdf', () => { const then = {} as Then try { - await defendantController.getSubpoenaPdf( + await limitedAccessSubpoenaController.getSubpoenaPdf( caseId, defendantId, + subpoenaId, theCase, defendant, + subpoena, res, ) } catch (error) { @@ -58,6 +62,7 @@ describe('DefendantController - Get subpoena pdf', () => { expect(mockPdfService.getSubpoenaPdf).toHaveBeenCalledWith( theCase, defendant, + subpoena, undefined, undefined, undefined, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/getSubpoenaPdfRolesRules.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/limitedAccessSubpoenaController/getSubpoenaPdfRolesRules.spec.ts similarity index 59% rename from apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/getSubpoenaPdfRolesRules.spec.ts rename to apps/judicial-system/backend/src/app/modules/subpoena/test/limitedAccessSubpoenaController/getSubpoenaPdfRolesRules.spec.ts index 87cbfde789f8..125817664eb1 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/getSubpoenaPdfRolesRules.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/limitedAccessSubpoenaController/getSubpoenaPdfRolesRules.spec.ts @@ -1,14 +1,14 @@ import { defenderRule } from '../../../../guards' -import { LimitedAccessDefendantController } from '../../limitedAccessDefendant.controller' +import { LimitedAccessSubpoenaController } from '../../limitedAccessSubpoena.controller' -describe('LimitedAccessDefendantController - Get custody notice pdf rules', () => { +describe('LimitedAccessSubpoenaController - Get custody notice pdf rules', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let rules: any[] beforeEach(() => { rules = Reflect.getMetadata( 'roles-rules', - LimitedAccessDefendantController.prototype.getSubpoenaPdf, + LimitedAccessSubpoenaController.prototype.getSubpoenaPdf, ) }) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/limitedAccessDefendantControllerGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/limitedAccessSubpoenaController/limitedAccessSubpoenaControllerGuards.spec.ts similarity index 62% rename from apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/limitedAccessDefendantControllerGuards.spec.ts rename to apps/judicial-system/backend/src/app/modules/subpoena/test/limitedAccessSubpoenaController/limitedAccessSubpoenaControllerGuards.spec.ts index aee6fdebeb8c..cf4fb036e396 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/limitedAccessDefendantControllerGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/limitedAccessSubpoenaController/limitedAccessSubpoenaControllerGuards.spec.ts @@ -2,19 +2,20 @@ import { JwtAuthGuard, RolesGuard } from '@island.is/judicial-system/auth' import { indictmentCases } from '@island.is/judicial-system/types' import { CaseExistsGuard, CaseReadGuard, CaseTypeGuard } from '../../../case' -import { DefendantExistsGuard } from '../../guards/defendantExists.guard' -import { LimitedAccessDefendantController } from '../../limitedAccessDefendant.controller' +import { DefendantExistsGuard } from '../../../defendant/guards/defendantExists.guard' +import { SubpoenaExistsOptionalGuard } from '../../guards/subpoenaExists.guard' +import { LimitedAccessSubpoenaController } from '../../limitedAccessSubpoena.controller' -describe('LimitedAccessDefendantController - guards', () => { +describe('LimitedAccessSubpoenaController - guards', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let guards: any[] beforeEach(() => { - guards = Reflect.getMetadata('__guards__', LimitedAccessDefendantController) + guards = Reflect.getMetadata('__guards__', LimitedAccessSubpoenaController) }) it('should have the right guard configuration', () => { - expect(guards).toHaveLength(6) + expect(guards).toHaveLength(7) expect(new guards[0]()).toBeInstanceOf(JwtAuthGuard) expect(new guards[1]()).toBeInstanceOf(RolesGuard) expect(new guards[2]()).toBeInstanceOf(CaseExistsGuard) @@ -24,5 +25,6 @@ describe('LimitedAccessDefendantController - guards', () => { }) expect(new guards[4]()).toBeInstanceOf(CaseReadGuard) expect(new guards[5]()).toBeInstanceOf(DefendantExistsGuard) + expect(new guards[6]()).toBeInstanceOf(SubpoenaExistsOptionalGuard) }) }) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/getSubpoenaPdf.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdf.spec.ts similarity index 67% rename from apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/getSubpoenaPdf.spec.ts rename to apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdf.spec.ts index 97ec46dde723..73d2580e33ff 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/getSubpoenaPdf.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdf.spec.ts @@ -1,10 +1,11 @@ import { Response } from 'express' import { uuid } from 'uuidv4' -import { createTestingDefendantModule } from '../createTestingDefendantModule' +import { createTestingSubpoenaModule } from '../createTestingSubpoenaModule' import { Case, PdfService } from '../../../case' -import { Defendant } from '../../models/defendant.model' +import { Defendant } from '../../../defendant/models/defendant.model' +import { Subpoena } from '../../models/subpoena.model' interface Then { error: Error @@ -12,10 +13,12 @@ interface Then { type GivenWhenThen = () => Promise -describe('LimitedAccessDefendantController - Get subpoena pdf', () => { +describe('SubpoenaController - Get subpoena pdf', () => { const caseId = uuid() + const subpoenaId = uuid() + const subpoena = { id: subpoenaId } as Subpoena const defendantId = uuid() - const defendant = { id: defendantId } as Defendant + const defendant = { id: defendantId, subpoenas: [subpoena] } as Defendant const theCase = { id: caseId } as Case const res = { end: jest.fn() } as unknown as Response const pdf = Buffer.from(uuid()) @@ -23,8 +26,8 @@ describe('LimitedAccessDefendantController - Get subpoena pdf', () => { let givenWhenThen: GivenWhenThen beforeEach(async () => { - const { pdfService, limitedAccessDefendantController } = - await createTestingDefendantModule() + const { pdfService, subpoenaController } = + await createTestingSubpoenaModule() mockPdfService = pdfService const getSubpoenaPdfMock = mockPdfService.getSubpoenaPdf as jest.Mock @@ -34,11 +37,13 @@ describe('LimitedAccessDefendantController - Get subpoena pdf', () => { const then = {} as Then try { - await limitedAccessDefendantController.getSubpoenaPdf( + await subpoenaController.getSubpoenaPdf( caseId, defendantId, + subpoenaId, theCase, defendant, + subpoena, res, ) } catch (error) { @@ -58,6 +63,7 @@ describe('LimitedAccessDefendantController - Get subpoena pdf', () => { expect(mockPdfService.getSubpoenaPdf).toHaveBeenCalledWith( theCase, defendant, + subpoena, undefined, undefined, undefined, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/getSubpoenaPdfGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts similarity index 61% rename from apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/getSubpoenaPdfGuards.spec.ts rename to apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts index 7fb9d19876bb..6f96a62ce278 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/getSubpoenaPdfGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts @@ -1,22 +1,23 @@ import { indictmentCases } from '@island.is/judicial-system/types' import { CaseExistsGuard, CaseReadGuard, CaseTypeGuard } from '../../../case' -import { DefendantController } from '../../defendant.controller' -import { DefendantExistsGuard } from '../../guards/defendantExists.guard' +import { DefendantExistsGuard } from '../../../defendant' +import { SubpoenaExistsOptionalGuard } from '../../guards/subpoenaExists.guard' +import { SubpoenaController } from '../../subpoena.controller' -describe('DefendantController - Get custody notice pdf guards', () => { +describe('SubpoenaController - Get custody notice pdf guards', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let guards: any[] beforeEach(() => { guards = Reflect.getMetadata( '__guards__', - DefendantController.prototype.getSubpoenaPdf, + SubpoenaController.prototype.getSubpoenaPdf, ) }) it('should have the right guard configuration', () => { - expect(guards).toHaveLength(4) + expect(guards).toHaveLength(5) expect(new guards[0]()).toBeInstanceOf(CaseExistsGuard) expect(guards[1]).toBeInstanceOf(CaseTypeGuard) expect(guards[1]).toEqual({ @@ -24,5 +25,6 @@ describe('DefendantController - Get custody notice pdf guards', () => { }) expect(new guards[2]()).toBeInstanceOf(CaseReadGuard) expect(new guards[3]()).toBeInstanceOf(DefendantExistsGuard) + expect(new guards[4]()).toBeInstanceOf(SubpoenaExistsOptionalGuard) }) }) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/getSubpoenaPdfRolesRules.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfRolesRules.spec.ts similarity index 80% rename from apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/getSubpoenaPdfRolesRules.spec.ts rename to apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfRolesRules.spec.ts index 286050136fcd..9d114d811d81 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/getSubpoenaPdfRolesRules.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfRolesRules.spec.ts @@ -6,16 +6,16 @@ import { prosecutorRule, publicProsecutorStaffRule, } from '../../../../guards' -import { DefendantController } from '../../defendant.controller' +import { SubpoenaController } from '../../subpoena.controller' -describe('DefendantController - Get custody notice pdf rules', () => { +describe('SubpoenaController - Get custody notice pdf rules', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let rules: any[] beforeEach(() => { rules = Reflect.getMetadata( 'roles-rules', - DefendantController.prototype.getSubpoenaPdf, + SubpoenaController.prototype.getSubpoenaPdf, ) }) From 8331d6116a113af3d6d1a5358f6cb7226d26f5de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 27 Sep 2024 19:15:44 +0200 Subject: [PATCH 18/61] Updates unit test --- .../modules/subpoena/subpoena.controller.ts | 17 ++++++----- .../getSubpoenaPdfGuards.spec.ts | 30 ------------------- .../subpoenaControllerGuards.spec.ts | 30 +++++++++++++++++++ 3 files changed, 39 insertions(+), 38 deletions(-) delete mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/subpoenaControllerGuards.spec.ts diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts index e6f4ad8abc51..2dcdf3848b7e 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts @@ -44,7 +44,15 @@ import { CurrentSubpoena } from './guards/subpoena.decorator' import { SubpoenaExistsOptionalGuard } from './guards/subpoenaExists.guard' import { Subpoena } from './models/subpoena.model' -@UseGuards(JwtAuthGuard, RolesGuard) +@UseGuards( + JwtAuthGuard, + RolesGuard, + CaseExistsGuard, + new CaseTypeGuard(indictmentCases), + CaseReadGuard, + DefendantExistsGuard, + SubpoenaExistsOptionalGuard, +) @Controller([ 'api/case/:caseId/defendant/:defendantId/subpoena', 'api/case/:caseId/defendant/:defendantId/subpoena/:subpoenaId', @@ -56,13 +64,6 @@ export class SubpoenaController { @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} - @UseGuards( - CaseExistsGuard, - new CaseTypeGuard(indictmentCases), - CaseReadGuard, - DefendantExistsGuard, - SubpoenaExistsOptionalGuard, - ) @RolesRules( prosecutorRule, prosecutorRepresentativeRule, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts deleted file mode 100644 index 6f96a62ce278..000000000000 --- a/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { indictmentCases } from '@island.is/judicial-system/types' - -import { CaseExistsGuard, CaseReadGuard, CaseTypeGuard } from '../../../case' -import { DefendantExistsGuard } from '../../../defendant' -import { SubpoenaExistsOptionalGuard } from '../../guards/subpoenaExists.guard' -import { SubpoenaController } from '../../subpoena.controller' - -describe('SubpoenaController - Get custody notice pdf guards', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let guards: any[] - - beforeEach(() => { - guards = Reflect.getMetadata( - '__guards__', - SubpoenaController.prototype.getSubpoenaPdf, - ) - }) - - it('should have the right guard configuration', () => { - expect(guards).toHaveLength(5) - expect(new guards[0]()).toBeInstanceOf(CaseExistsGuard) - expect(guards[1]).toBeInstanceOf(CaseTypeGuard) - expect(guards[1]).toEqual({ - allowedCaseTypes: indictmentCases, - }) - expect(new guards[2]()).toBeInstanceOf(CaseReadGuard) - expect(new guards[3]()).toBeInstanceOf(DefendantExistsGuard) - expect(new guards[4]()).toBeInstanceOf(SubpoenaExistsOptionalGuard) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/subpoenaControllerGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/subpoenaControllerGuards.spec.ts new file mode 100644 index 000000000000..966a64fecfef --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/subpoenaControllerGuards.spec.ts @@ -0,0 +1,30 @@ +import { JwtAuthGuard, RolesGuard } from '@island.is/judicial-system/auth' +import { indictmentCases } from '@island.is/judicial-system/types' + +import { CaseExistsGuard, CaseReadGuard, CaseTypeGuard } from '../../../case' +import { DefendantExistsGuard } from '../../../defendant/guards/defendantExists.guard' +import { SubpoenaExistsOptionalGuard } from '../../guards/subpoenaExists.guard' +import { SubpoenaController } from '../../subpoena.controller' + +describe('SubpoenaController - guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata('__guards__', SubpoenaController) + }) + + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(7) + expect(new guards[0]()).toBeInstanceOf(JwtAuthGuard) + expect(new guards[1]()).toBeInstanceOf(RolesGuard) + expect(new guards[2]()).toBeInstanceOf(CaseExistsGuard) + expect(guards[3]).toBeInstanceOf(CaseTypeGuard) + expect(guards[3]).toEqual({ + allowedCaseTypes: indictmentCases, + }) + expect(new guards[4]()).toBeInstanceOf(CaseReadGuard) + expect(new guards[5]()).toBeInstanceOf(DefendantExistsGuard) + expect(new guards[6]()).toBeInstanceOf(SubpoenaExistsOptionalGuard) + }) +}) From 185d7dd93371e0490cfa57af5d0d7e78fe490db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Mon, 30 Sep 2024 10:07:54 +0200 Subject: [PATCH 19/61] Fixes some typs and null reference guards --- .../api/src/app/modules/defendant/models/subpoena.model.ts | 4 ++-- .../backend/src/app/modules/case/case.service.ts | 2 +- .../backend/src/app/modules/subpoena/models/subpoena.model.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts index 9ba54e366b30..9a810b1edc10 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts @@ -14,8 +14,8 @@ export class Subpoena { @Field(() => String, { nullable: true }) subpoenaId?: string - @Field(() => String, { nullable: true }) - acknowledged?: string + @Field(() => Boolean, { nullable: true }) + acknowledged?: boolean @Field(() => String, { nullable: true }) registeredBy?: string 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 1e5011a092ae..06c0dcee6ea1 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 @@ -1183,7 +1183,7 @@ export class CaseService { (updatedDefendant) => theCase.defendants?.find( (defendant) => defendant.id === updatedDefendant.id, - )?.subpoenas?.[0].id !== updatedDefendant.subpoenas?.[0].id, + )?.subpoenas?.[0]?.id !== updatedDefendant.subpoenas?.[0]?.id, ) .map((updatedDefendant) => ({ type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts index 933b1633e1bb..ee7589da6301 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts @@ -59,7 +59,7 @@ export class Subpoena extends Model { @Column({ type: DataType.BOOLEAN, allowNull: true }) @ApiPropertyOptional({ type: Boolean }) - acknowledged?: string + acknowledged?: boolean @Column({ type: DataType.STRING, allowNull: true }) @ApiPropertyOptional({ type: String }) From 331ea57883d656b3ab0cd6d492fb64f049542bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Mon, 30 Sep 2024 10:10:57 +0200 Subject: [PATCH 20/61] Improved type declaration --- .../backend/src/app/modules/subpoena/models/subpoena.model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts index ee7589da6301..4d4f05a53b67 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/models/subpoena.model.ts @@ -49,7 +49,7 @@ export class Subpoena extends Model { defendant?: Defendant @ForeignKey(() => Case) - @Column({ type: DataType.UUID }) + @Column({ type: DataType.UUID, allowNull: false }) @ApiProperty({ type: String }) caseId!: string From 798c7f063cce058950eccb64a306ef51ab0764aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Mon, 30 Sep 2024 10:19:08 +0200 Subject: [PATCH 21/61] Improved date formatting --- .../IndictmentCaseFilesList.tsx | 9 ++------- .../routes/Court/Indictments/Subpoena/Subpoena.tsx | 13 ++++--------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx b/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx index f38145abd551..29f1738db99f 100644 --- a/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx +++ b/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx @@ -3,6 +3,7 @@ import { useIntl } from 'react-intl' import { AnimatePresence } from 'framer-motion' import { Box, Text } from '@island.is/island-ui/core' +import { formatDate } from '@island.is/judicial-system/formatters' import { isCompletedCase, isDefenceUser, @@ -241,13 +242,7 @@ const IndictmentCaseFilesList: FC = ({ caseId={workingCase.id} title={formatMessage(strings.subpoenaButtonText, { name: defendant.name, - date: `${subpoena.created?.slice( - 8, - 10, - )}.${subpoena.created?.slice( - 5, - 7, - )}.${subpoena.created?.slice(0, 4)}`, + date: formatDate(subpoena.created), })} pdfType="subpoena" elementId={[defendant.id, subpoena.id]} diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx index 77b8cca8d436..676072ff97c2 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx @@ -4,6 +4,7 @@ import router from 'next/router' import { Box, Button } from '@island.is/island-ui/core' import * as constants from '@island.is/judicial-system/consts' +import { formatDate } from '@island.is/judicial-system/formatters' import { titles } from '@island.is/judicial-system-web/messages' import { CourtArrangements, @@ -191,15 +192,9 @@ const Subpoena: FC = () => { > From 0fd6b374cc902d054f31d864e5b79a5b1cc41609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Mon, 30 Sep 2024 10:24:00 +0200 Subject: [PATCH 22/61] Improves indexing --- .../web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx index 676072ff97c2..0eab4a0af95b 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Subpoena/Subpoena.tsx @@ -158,7 +158,7 @@ const Subpoena: FC = () => { <> {isSchedulingArraignmentDateForDefendant(defendant.id) && ( { )} {defendant.subpoenas?.map((subpoena, sIndex) => ( Date: Mon, 30 Sep 2024 10:31:02 +0200 Subject: [PATCH 23/61] Fixes spelling --- .../src/app/modules/subpoena/internalSubpoena.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts index aef0e88065b6..411c8f3e4ef3 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts @@ -90,7 +90,7 @@ export class InternalSubpoenaController { @Body() deliverDto: DeliverDto, ): Promise { this.logger.debug( - `Delivering subpoena ${subpoenaId} to police for defendant ${defendantId} og case ${caseId}`, + `Delivering subpoena ${subpoenaId} to police for defendant ${defendantId} of case ${caseId}`, ) return await this.subpoenaService.deliverSubpoenaToPolice( From 22d975f1e2f48f779fe4d77fb0101d831c30e311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Mon, 30 Sep 2024 10:59:27 +0200 Subject: [PATCH 24/61] Fixes indexing --- .../src/routes/Court/components/SubpoenaType/SubpoenaType.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/judicial-system/web/src/routes/Court/components/SubpoenaType/SubpoenaType.tsx b/apps/judicial-system/web/src/routes/Court/components/SubpoenaType/SubpoenaType.tsx index 3a89ef897e87..4b36142baa59 100644 --- a/apps/judicial-system/web/src/routes/Court/components/SubpoenaType/SubpoenaType.tsx +++ b/apps/judicial-system/web/src/routes/Court/components/SubpoenaType/SubpoenaType.tsx @@ -50,7 +50,7 @@ const SubpoenaType: FC = ({ From cdaffc08632bcfb2a3454aeb6c940a5e65144bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Mon, 30 Sep 2024 11:10:38 +0200 Subject: [PATCH 25/61] Fixes optional arguments --- .../judicial-system/backend/src/app/modules/case/pdf.service.ts | 2 +- .../backend/src/app/modules/subpoena/subpoena.controller.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts b/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts index 140501e17c59..10065ea3bdb6 100644 --- a/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts @@ -293,7 +293,7 @@ export class PdfService { async getSubpoenaPdf( theCase: Case, defendant: Defendant, - subpoena: Subpoena, + subpoena?: Subpoena, arraignmentDate?: Date, location?: string, subpoenaType?: SubpoenaType, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts index 2dcdf3848b7e..0a4eea819bcb 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts @@ -84,8 +84,8 @@ export class SubpoenaController { @Param('subpoenaId') subpoenaId: string, @CurrentCase() theCase: Case, @CurrentDefendant() defendant: Defendant, - @CurrentSubpoena() subpoena: Subpoena, @Res() res: Response, + @CurrentSubpoena() subpoena?: Subpoena, @Query('arraignmentDate') arraignmentDate?: Date, @Query('location') location?: string, @Query('subpoenaType') subpoenaType?: SubpoenaType, From 279f2f43942a7d8986e21c84bb734c4399004ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Mon, 30 Sep 2024 11:16:48 +0200 Subject: [PATCH 26/61] Removes redundant constructor --- .../src/app/modules/subpoena/guards/subpoenaExists.guard.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts index 71a5672010c6..624bd2d361c1 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts @@ -47,9 +47,6 @@ export class SubpoenaExistsGuard implements CanActivate { @Injectable() export class SubpoenaExistsOptionalGuard extends SubpoenaExistsGuard { - constructor(subpoenaService: SubpoenaService) { - super(subpoenaService) - } async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest() From 0cae899c95d37449f15aeeb3fcdd29cd8468dd70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Mon, 30 Sep 2024 09:23:28 +0000 Subject: [PATCH 27/61] Fix key error --- .../src/app/modules/police/police.service.ts | 26 +++++++++---------- .../src/components/FormProvider/case.graphql | 2 ++ .../Court/Indictments/Overview/Overview.tsx | 5 +++- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 3cbd581d89ad..52ce9d8c1060 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -30,7 +30,7 @@ import { AwsS3Service } from '../aws-s3' import { Case } from '../case' import { Defendant } from '../defendant/models/defendant.model' import { EventService } from '../event' -import { Subpoena } from '../subpoena/models/subpoena.model' +// import { Subpoena } from '../subpoena/models/subpoena.model' import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { CreateSubpoenaResponse } from './models/createSubpoena.response' import { PoliceCaseFile } from './models/policeCaseFile.model' @@ -328,18 +328,18 @@ export class PoliceService { }) } - async getSubpoenaStatus(subpoenaId: string): Promise { - return this.fetchPoliceDocumentApi( - `${this.xRoadPath}/GetSubpoenaStatus?id=${subpoenaId}`, - ).then(async (res: Response) => { - if (res.ok) { - const response: z.infer = - await res.json() - - return response - } - }) - } + // async getSubpoenaStatus(subpoenaId: string): Promise { + // return this.fetchPoliceDocumentApi( + // `${this.xRoadPath}/GetSubpoenaStatus?id=${subpoenaId}`, + // ).then(async (res: Response) => { + // if (res.ok) { + // const response: z.infer = + // await res.json() + + // return response + // } + // }) + // } async getPoliceCaseInfo( caseId: string, diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index 86ba091ad011..387d5aec31a3 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -27,6 +27,8 @@ query Case($input: CaseQueryInput!) { verdictAppealDeadline subpoenaType subpoenas { + id + created serviceStatus serviceDate servedBy diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 1a33bd05746e..a7d2c651aa05 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -112,7 +112,9 @@ const ServiceAnnouncement: FC = (props) => { message={ {messages.map((msg) => ( - {msg} + + {msg} + ))} } @@ -178,6 +180,7 @@ const IndictmentOverview = () => { {workingCase.defendants?.map((defendant) => defendant.subpoenas?.map((subpoena) => ( From de016e381aa198a2ff8ab6efa2d878bd9ba2ac07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Mon, 30 Sep 2024 10:22:59 +0000 Subject: [PATCH 28/61] Remove unused import --- .../backend/src/app/modules/case/internalCase.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts index 26377502db54..dd1271f2ba0b 100644 --- a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts @@ -1,7 +1,6 @@ import CryptoJS from 'crypto-js' import format from 'date-fns/format' import { Base64 } from 'js-base64' -import { Op } from 'sequelize' import { Sequelize } from 'sequelize-typescript' import { From e81635281490081399f352d9acda9112e412ec19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Mon, 30 Sep 2024 10:25:13 +0000 Subject: [PATCH 29/61] Remove unused import --- .../case/test/caseController/getIndictmentPdfGuards.spec.ts | 2 -- .../backend/src/app/modules/event/event.service.ts | 1 - .../file/test/fileController/getCaseFileSignedUrlGuards.spec.ts | 2 -- 3 files changed, 5 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getIndictmentPdfGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getIndictmentPdfGuards.spec.ts index d86a9827ce80..b84a86bd3d9d 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getIndictmentPdfGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getIndictmentPdfGuards.spec.ts @@ -1,5 +1,3 @@ -import { CanActivate } from '@nestjs/common' - import { JwtAuthGuard, RolesGuard } from '@island.is/judicial-system/auth' import { indictmentCases } from '@island.is/judicial-system/types' diff --git a/apps/judicial-system/backend/src/app/modules/event/event.service.ts b/apps/judicial-system/backend/src/app/modules/event/event.service.ts index aeac0ff7f26d..56c762f6299a 100644 --- a/apps/judicial-system/backend/src/app/modules/event/event.service.ts +++ b/apps/judicial-system/backend/src/app/modules/event/event.service.ts @@ -18,7 +18,6 @@ import { } from '@island.is/judicial-system/types' import { type Case } from '../case' -import { CaseString } from '../case/models/caseString.model' import { DateLog } from '../case/models/dateLog.model' import { eventModuleConfig } from './event.config' diff --git a/apps/judicial-system/backend/src/app/modules/file/test/fileController/getCaseFileSignedUrlGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/file/test/fileController/getCaseFileSignedUrlGuards.spec.ts index 88c3d0946486..e23a72d4273b 100644 --- a/apps/judicial-system/backend/src/app/modules/file/test/fileController/getCaseFileSignedUrlGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/file/test/fileController/getCaseFileSignedUrlGuards.spec.ts @@ -1,5 +1,3 @@ -import { CanActivate } from '@nestjs/common' - import { RolesGuard } from '@island.is/judicial-system/auth' import { CaseExistsGuard, CaseReadGuard } from '../../../case' From 49b739ced3c5bfa2f5a6dde363032d782522f9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Mon, 30 Sep 2024 10:37:34 +0000 Subject: [PATCH 30/61] Fix tests --- .../subpoena/test/subpoenaController/getSubpoenaPdf.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdf.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdf.spec.ts index 73d2580e33ff..2db39b25f289 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdf.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdf.spec.ts @@ -43,8 +43,8 @@ describe('SubpoenaController - Get subpoena pdf', () => { subpoenaId, theCase, defendant, - subpoena, res, + subpoena, ) } catch (error) { then.error = error as Error From d6a350ebe162ece752b3de747e8999bc49ceb6c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Mon, 30 Sep 2024 15:24:13 +0000 Subject: [PATCH 31/61] Checkpoint --- .../src/app/modules/police/police.service.ts | 89 ++++++++++++++----- 1 file changed, 66 insertions(+), 23 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 52ce9d8c1060..4917895489cc 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -23,14 +23,19 @@ import { import { normalizeAndFormatNationalId } from '@island.is/judicial-system/formatters' import type { User } from '@island.is/judicial-system/types' -import { CaseState, CaseType } from '@island.is/judicial-system/types' +import { + CaseState, + CaseType, + ServiceStatus, +} from '@island.is/judicial-system/types' import { nowFactory } from '../../factories' import { AwsS3Service } from '../aws-s3' import { Case } from '../case' import { Defendant } from '../defendant/models/defendant.model' import { EventService } from '../event' -// import { Subpoena } from '../subpoena/models/subpoena.model' +import { Subpoena } from '../subpoena' +import { UpdateSubpoenaDto } from '../subpoena/dto/updateSubpoena.dto' import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { CreateSubpoenaResponse } from './models/createSubpoena.response' import { PoliceCaseFile } from './models/policeCaseFile.model' @@ -124,15 +129,15 @@ export class PoliceService { malseinings: z.optional(z.array(this.crimeSceneStructure)), }) private subpoenaStructure = z.object({ - Acknowledged: z.boolean(), - Comment: z.string(), - DefenderChoice: z.string(), - DefenderNationalId: z.string(), - ProsecutedConfirmedSubpoenaThroughIslandis: z.boolean(), - ServedBy: z.string(), - Delivered: z.boolean(), - DeliveredOnPaper: z.boolean(), - DeliveredToLawyer: z.boolean(), + acknowledged: z.boolean(), + comment: z.string(), + defenderChoice: z.string(), + defenderNationalId: z.string(), + prosecutedConfirmedSubpoenaThroughIslandis: z.boolean(), + servedBy: z.string(), + delivered: z.boolean(), + deliveredOnPaper: z.boolean(), + deliveredToLawyer: z.boolean(), }) constructor( @@ -328,18 +333,56 @@ export class PoliceService { }) } - // async getSubpoenaStatus(subpoenaId: string): Promise { - // return this.fetchPoliceDocumentApi( - // `${this.xRoadPath}/GetSubpoenaStatus?id=${subpoenaId}`, - // ).then(async (res: Response) => { - // if (res.ok) { - // const response: z.infer = - // await res.json() - - // return response - // } - // }) - // } + async getSubpoenaStatus( + subpoenaId: string, + user: User, + ): Promise { + return this.fetchPoliceDocumentApi( + `${this.xRoadPath}/GetSubpoenaStatus?id=${subpoenaId}`, + ) + .then(async (res: Response) => { + if (res.ok) { + const response: z.infer = + await res.json() + + this.subpoenaStructure.parse(response) + + const a: UpdateSubpoenaDto = { serviceStatus: response.deliveredToLawyer ? ServiceStatus.DEFENDER : response.prosecutedConfirmedSubpoenaThroughIslandis ? ServiceStatus.ELECTRONICALLY : response.,servedBy: response.servedBy } + + return response + } + + const reason = await res.text() + + // The police system does not provide a structured error response. + // When a subpoena does not exist, a stack trace is returned. + throw new NotFoundException({ + message: `Subpoena with id ${subpoenaId} does not exist`, + detail: reason, + }) + }) + .catch((reason) => { + if (reason instanceof ServiceUnavailableException) { + // Do not spam the logs with errors + // Act as if the case was updated + return true + } else { + this.logger.error(`Failed to get subpoena with id ${subpoenaId}`, { + reason, + }) + } + + this.eventService.postErrorEvent( + 'Failed to get subpoena', + { + subpoenaId, + actor: user.name, + institution: user.institution?.name, + }, + reason, + ) + }) + } async getPoliceCaseInfo( caseId: string, From deff5b503770e0ed6f6dee0b091e699440d9634c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Mon, 30 Sep 2024 22:11:48 +0000 Subject: [PATCH 32/61] Checkpoint --- .../src/app/modules/police/police.service.ts | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 4917895489cc..287a3674e214 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -333,10 +333,7 @@ export class PoliceService { }) } - async getSubpoenaStatus( - subpoenaId: string, - user: User, - ): Promise { + async getSubpoenaStatus(subpoenaId: string): Promise { return this.fetchPoliceDocumentApi( `${this.xRoadPath}/GetSubpoenaStatus?id=${subpoenaId}`, ) @@ -347,9 +344,19 @@ export class PoliceService { this.subpoenaStructure.parse(response) - const a: UpdateSubpoenaDto = { serviceStatus: response.deliveredToLawyer ? ServiceStatus.DEFENDER : response.prosecutedConfirmedSubpoenaThroughIslandis ? ServiceStatus.ELECTRONICALLY : response.,servedBy: response.servedBy } - - return response + const subpoena: UpdateSubpoenaDto = { + serviceStatus: response.deliveredToLawyer + ? ServiceStatus.DEFENDER + : response.prosecutedConfirmedSubpoenaThroughIslandis + ? ServiceStatus.ELECTRONICALLY + : undefined, + servedBy: response.servedBy, + comment: response.comment, + // TODO: defenderChoice + defenderNationalId: response.defenderNationalId, + } + + return subpoena } const reason = await res.text() @@ -362,25 +369,18 @@ export class PoliceService { }) }) .catch((reason) => { - if (reason instanceof ServiceUnavailableException) { - // Do not spam the logs with errors - // Act as if the case was updated - return true - } else { - this.logger.error(`Failed to get subpoena with id ${subpoenaId}`, { - reason, - }) - } - - this.eventService.postErrorEvent( - 'Failed to get subpoena', - { - subpoenaId, - actor: user.name, - institution: user.institution?.name, - }, - reason, - ) + // TODO: Handle this + throw reason + + // this.eventService.postErrorEvent( + // 'Failed to get subpoena', + // { + // subpoenaId, + // actor: user.name, + // institution: user.institution?.name, + // }, + // reason, + // ) }) } From df0c842f27ba0952b71d9b460a57983c76fe51cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Tue, 1 Oct 2024 12:51:16 +0000 Subject: [PATCH 33/61] Checkpoint --- .../app/modules/backend/backend.service.ts | 8 ++++++ .../police/dto/subpoenaStatus.input.ts | 14 ++++++++++ .../api/src/app/modules/police/index.ts | 1 + .../police/models/subpoenaStatus.model.ts | 18 +++++++++++++ .../src/app/modules/police/police.resolver.ts | 22 ++++++++++++++++ .../app/modules/police/police.controller.ts | 26 ++++++++++++++++++- .../audit-trail/src/lib/auditTrail.service.ts | 1 + 7 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 apps/judicial-system/api/src/app/modules/police/dto/subpoenaStatus.input.ts create mode 100644 apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts diff --git a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts index 5a42b29ab8a7..ef2161ac3de8 100644 --- a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts +++ b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts @@ -41,6 +41,7 @@ import { Institution } from '../institution' import { PoliceCaseFile, PoliceCaseInfo, + SubpoenaStatus, UploadPoliceCaseFileResponse, } from '../police' import { backendModuleConfig } from './backend.config' @@ -306,6 +307,13 @@ export class BackendService extends DataSource<{ req: Request }> { return this.get(`case/${caseId}/policeFiles`) } + getSubpoenaStatus( + caseId: string, + subpoenaId: string, + ): Promise { + return this.get(`/case/${caseId}/subpoenaStatus/${subpoenaId}`) + } + getPoliceCaseInfo(caseId: string): Promise { return this.get(`case/${caseId}/policeCaseInfo`) } diff --git a/apps/judicial-system/api/src/app/modules/police/dto/subpoenaStatus.input.ts b/apps/judicial-system/api/src/app/modules/police/dto/subpoenaStatus.input.ts new file mode 100644 index 000000000000..dcc11761c0be --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/police/dto/subpoenaStatus.input.ts @@ -0,0 +1,14 @@ +import { Allow } from 'class-validator' + +import { Field, ID, InputType } from '@nestjs/graphql' + +@InputType() +export class SubpoenaStatusQueryInput { + @Allow() + @Field(() => ID) + readonly caseId!: string + + @Allow() + @Field(() => ID) + readonly subpoenaId!: string +} diff --git a/apps/judicial-system/api/src/app/modules/police/index.ts b/apps/judicial-system/api/src/app/modules/police/index.ts index c2c2457a57b5..a1fe72f38c8e 100644 --- a/apps/judicial-system/api/src/app/modules/police/index.ts +++ b/apps/judicial-system/api/src/app/modules/police/index.ts @@ -1,3 +1,4 @@ export { PoliceCaseInfo } from './models/policeCaseInfo.model' +export { SubpoenaStatus } from './models/subpoenaStatus.model' export { PoliceCaseFile } from './models/policeCaseFile.model' export { UploadPoliceCaseFileResponse } from './models/uploadPoliceCaseFile.response' diff --git a/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts b/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts new file mode 100644 index 000000000000..58dc6bc3f172 --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts @@ -0,0 +1,18 @@ +import { Field, ObjectType } from '@nestjs/graphql' + +import { ServiceStatus } from '@island.is/judicial-system/types' + +@ObjectType() +export class SubpoenaStatus { + @Field(() => ServiceStatus) + readonly ServiceStatus!: ServiceStatus + + @Field(() => String, { nullable: true }) + readonly servedBy?: string + + @Field(() => String, { nullable: true }) + readonly comment?: string + + @Field(() => String, { nullable: true }) + readonly serviceDate?: string +} diff --git a/apps/judicial-system/api/src/app/modules/police/police.resolver.ts b/apps/judicial-system/api/src/app/modules/police/police.resolver.ts index 8d4fbf9cd90a..1eef19336e76 100644 --- a/apps/judicial-system/api/src/app/modules/police/police.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/police/police.resolver.ts @@ -17,9 +17,11 @@ import type { User } from '@island.is/judicial-system/types' import { BackendService } from '../backend' import { PoliceCaseFilesQueryInput } from './dto/policeCaseFiles.input' import { PoliceCaseInfoQueryInput } from './dto/policeCaseInfo.input' +import { SubpoenaStatusQueryInput } from './dto/subpoenaStatus.input' import { UploadPoliceCaseFileInput } from './dto/uploadPoliceCaseFile.input' import { PoliceCaseFile } from './models/policeCaseFile.model' import { PoliceCaseInfo } from './models/policeCaseInfo.model' +import { SubpoenaStatus } from './models/subpoenaStatus.model' import { UploadPoliceCaseFileResponse } from './models/uploadPoliceCaseFile.response' @UseGuards(JwtGraphQlAuthGuard) @@ -49,6 +51,26 @@ export class PoliceResolver { ) } + @Query(() => SubpoenaStatus, { nullable: true }) + subpoenaStatus( + @Args('input', { type: () => SubpoenaStatusQueryInput }) + input: SubpoenaStatusQueryInput, + @CurrentGraphQlUser() user: User, + @Context('dataSources') + { backendService }: { backendService: BackendService }, + ): Promise { + this.logger.debug( + `Getting subpoena status for subpoena ${input.subpoenaId} of case ${input.caseId}`, + ) + + return this.auditTrailService.audit( + user.id, + AuditedAction.GET_SUBPOENA_STATUS, + backendService.getSubpoenaStatus(input.caseId, input.subpoenaId), + input.caseId, + ) + } + @Query(() => [PoliceCaseInfo], { nullable: true }) policeCaseInfo( @Args('input', { type: () => PoliceCaseInfoQueryInput }) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts index 2d6321e6bc14..426cf86f2cae 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts @@ -21,7 +21,11 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { prosecutorRepresentativeRule, prosecutorRule } from '../../guards' +import { + districtCourtJudgeRule, + prosecutorRepresentativeRule, + prosecutorRule, +} from '../../guards' import { Case, CaseExistsGuard, @@ -30,6 +34,7 @@ import { CaseReadGuard, CurrentCase, } from '../case' +import { UpdateSubpoenaDto } from '../subpoena/dto/updateSubpoena.dto' import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { PoliceCaseFile } from './models/policeCaseFile.model' import { PoliceCaseInfo } from './models/policeCaseInfo.model' @@ -69,6 +74,25 @@ export class PoliceController { return this.policeService.getAllPoliceCaseFiles(theCase.id, user) } + @RolesRules( + prosecutorRule, + prosecutorRepresentativeRule, + districtCourtJudgeRule, + ) + @Get('subpoenaStatus') + @ApiOkResponse({ + type: UpdateSubpoenaDto, + description: 'Gets subpoena status', + }) + getSubpoenaStatus( + @Param('caseId') caseId: string, + @Param('subpoenaId') subpoenaId: string, + ): Promise { + this.logger.debug(`Gets subpoena status in case ${caseId}`) + + return this.policeService.getSubpoenaStatus(subpoenaId) + } + @RolesRules(prosecutorRule, prosecutorRepresentativeRule) @UseInterceptors(CaseOriginalAncestorInterceptor) @Get('policeCaseInfo') diff --git a/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts b/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts index 91f4de74ca3d..2dba14790572 100644 --- a/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts +++ b/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts @@ -40,6 +40,7 @@ export enum AuditedAction { GET_SUBPOENA_PDF = 'GET_SUBPOENA_PDF', GET_ALL_FILES_ZIP = 'GET_ALL_FILES_ZIP', GET_INSTITUTIONS = 'GET_INSTITUTIONS', + GET_SUBPOENA_STATUS = 'GET_SUBPOENA_STATUS', CREATE_PRESIGNED_POST = 'CREATE_PRESIGNED_POST', CREATE_FILE = 'CREATE_FILE', UPDATE_FILES = 'UPDATE_FILES', From ee3b10188596b675ceb9f54f03d3541c1cdb9c3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Tue, 1 Oct 2024 15:08:16 +0000 Subject: [PATCH 34/61] Save subpoena status in DB --- .../app/modules/backend/backend.service.ts | 2 +- .../police/models/subpoenaStatus.model.ts | 2 +- .../backend/src/app/modules/index.ts | 1 + .../app/modules/police/police.controller.ts | 2 +- .../src/app/modules/police/police.module.ts | 3 +- .../src/app/modules/police/police.service.ts | 54 ++++++++++++------- .../Court/Indictments/Overview/Overview.tsx | 4 +- .../web/src/utils/hooks/index.ts | 1 + .../useSubpoena/getSubpoenaStatus.graphql | 6 +++ .../web/src/utils/hooks/useSubpoena/index.ts | 16 ++++++ 10 files changed, 66 insertions(+), 25 deletions(-) create mode 100644 apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql create mode 100644 apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts diff --git a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts index ef2161ac3de8..099c3bf98b10 100644 --- a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts +++ b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts @@ -311,7 +311,7 @@ export class BackendService extends DataSource<{ req: Request }> { caseId: string, subpoenaId: string, ): Promise { - return this.get(`/case/${caseId}/subpoenaStatus/${subpoenaId}`) + return this.get(`case/${caseId}/subpoenaStatus/${subpoenaId}`) } getPoliceCaseInfo(caseId: string): Promise { diff --git a/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts b/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts index 58dc6bc3f172..94cf00bbdc05 100644 --- a/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts +++ b/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts @@ -5,7 +5,7 @@ import { ServiceStatus } from '@island.is/judicial-system/types' @ObjectType() export class SubpoenaStatus { @Field(() => ServiceStatus) - readonly ServiceStatus!: ServiceStatus + readonly serviceStatus!: ServiceStatus @Field(() => String, { nullable: true }) readonly servedBy?: string diff --git a/apps/judicial-system/backend/src/app/modules/index.ts b/apps/judicial-system/backend/src/app/modules/index.ts index d2aee8e277e6..e6e40ad4a580 100644 --- a/apps/judicial-system/backend/src/app/modules/index.ts +++ b/apps/judicial-system/backend/src/app/modules/index.ts @@ -19,3 +19,4 @@ export { AwsS3Module } from './aws-s3/awsS3.module' export { EventModule } from './event/event.module' export { EventLogModule } from './event-log/eventLog.module' export { SubpoenaModule } from './subpoena/subpoena.module' +export { SubpoenaService } from './subpoena/subpoena.service' diff --git a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts index 426cf86f2cae..09851362db87 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts @@ -79,7 +79,7 @@ export class PoliceController { prosecutorRepresentativeRule, districtCourtJudgeRule, ) - @Get('subpoenaStatus') + @Get('subpoenaStatus/:subpoenaId') @ApiOkResponse({ type: UpdateSubpoenaDto, description: 'Gets subpoena status', diff --git a/apps/judicial-system/backend/src/app/modules/police/police.module.ts b/apps/judicial-system/backend/src/app/modules/police/police.module.ts index 37f05ff5e4dc..1df72c2b8794 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.module.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.module.ts @@ -1,6 +1,6 @@ import { forwardRef, Module } from '@nestjs/common' -import { AwsS3Module, CaseModule, EventModule } from '../index' +import { AwsS3Module, CaseModule, EventModule, SubpoenaModule } from '../index' import { PoliceController } from './police.controller' import { PoliceService } from './police.service' @@ -9,6 +9,7 @@ import { PoliceService } from './police.service' forwardRef(() => CaseModule), forwardRef(() => EventModule), forwardRef(() => AwsS3Module), + forwardRef(() => SubpoenaModule), ], providers: [PoliceService], exports: [PoliceService], diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 287a3674e214..510cfddecb15 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -34,8 +34,7 @@ import { AwsS3Service } from '../aws-s3' import { Case } from '../case' import { Defendant } from '../defendant/models/defendant.model' import { EventService } from '../event' -import { Subpoena } from '../subpoena' -import { UpdateSubpoenaDto } from '../subpoena/dto/updateSubpoena.dto' +import { Subpoena, SubpoenaService } from '../subpoena' import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { CreateSubpoenaResponse } from './models/createSubpoena.response' import { PoliceCaseFile } from './models/policeCaseFile.model' @@ -147,6 +146,8 @@ export class PoliceService { private readonly eventService: EventService, @Inject(forwardRef(() => AwsS3Service)) private readonly awsS3Service: AwsS3Service, + @Inject(forwardRef(() => SubpoenaService)) + private readonly subpoenaService: SubpoenaService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) { this.xRoadPath = createXRoadAPIPath( @@ -333,30 +334,43 @@ export class PoliceService { }) } - async getSubpoenaStatus(subpoenaId: string): Promise { + async getSubpoenaStatus(subpoenaId: string): Promise { return this.fetchPoliceDocumentApi( `${this.xRoadPath}/GetSubpoenaStatus?id=${subpoenaId}`, ) .then(async (res: Response) => { if (res.ok) { - const response: z.infer = - await res.json() + const response = await res.json() + + // TODO: parse this correctly + // this.subpoenaStructure.parse(response) + const parsedResponse = JSON.parse(response) + + const subpoenaToUpdate = await this.subpoenaService.findBySubpoenaId( + subpoenaId, + ) + + const updatedSubpoena = await this.subpoenaService.update( + subpoenaToUpdate, + { + comment: parsedResponse.comment, + servedBy: parsedResponse.servedBy, + defenderNationalId: parsedResponse.defenderNationalId, + serviceStatus: parsedResponse.deliveredToLawyer + ? ServiceStatus.DEFENDER + : parsedResponse.prosecutedConfirmedSubpoenaThroughIslandis + ? ServiceStatus.ELECTRONICALLY + : parsedResponse.deliveredOnPaper + ? ServiceStatus.IN_PERSON + : parsedResponse.acknowledged === false && + parsedResponse.defenderChoice === 'HAS_NOT_ACKNOWLEDGED' + ? ServiceStatus.FAILED + : // TODO: handle expired + undefined, + }, + ) - this.subpoenaStructure.parse(response) - - const subpoena: UpdateSubpoenaDto = { - serviceStatus: response.deliveredToLawyer - ? ServiceStatus.DEFENDER - : response.prosecutedConfirmedSubpoenaThroughIslandis - ? ServiceStatus.ELECTRONICALLY - : undefined, - servedBy: response.servedBy, - comment: response.comment, - // TODO: defenderChoice - defenderNationalId: response.defenderNationalId, - } - - return subpoena + return updatedSubpoena } const reason = await res.text() diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 32d470c01a5a..13d653008e8e 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -1,4 +1,4 @@ -import { FC, useCallback, useContext, useState } from 'react' +import { FC, useCallback, useContext, useEffect, useState } from 'react' import { IntlShape, MessageDescriptor, useIntl } from 'react-intl' import { useRouter } from 'next/router' @@ -30,6 +30,7 @@ import { import { useDefendants, useGetLawyer, + useSubpoena, } from '@island.is/judicial-system-web/src/utils/hooks' import { SubpoenaType } from '../../components' @@ -141,6 +142,7 @@ const IndictmentOverview = () => { const latestDate = workingCase.courtDate ?? workingCase.arraignmentDate const isArraignmentScheduled = Boolean(workingCase.arraignmentDate) + const { subpoenaStatus } = useSubpoena() // const caseHasBeenReceivedByCourt = workingCase.state === CaseState.RECEIVED const handleNavigationTo = useCallback( diff --git a/apps/judicial-system/web/src/utils/hooks/index.ts b/apps/judicial-system/web/src/utils/hooks/index.ts index 3c9d8a29cec0..cc4edd7be94a 100644 --- a/apps/judicial-system/web/src/utils/hooks/index.ts +++ b/apps/judicial-system/web/src/utils/hooks/index.ts @@ -29,3 +29,4 @@ export { default as useSections } from './useSections' export { default as useCaseList } from './useCaseList' export { default as useNationalRegistry } from './useNationalRegistry' export { default as useCivilClaimants } from './useCivilClaimants' +export { default as useSubpoena } from './useSubpoena' diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql b/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql new file mode 100644 index 000000000000..d946e4f549c6 --- /dev/null +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql @@ -0,0 +1,6 @@ +query SubpoenaStatus($input: SubpoenaStatusQueryInput!) { + subpoenaStatus(input: $input) { + serviceStatus + servedBy + } +} diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts new file mode 100644 index 000000000000..d61dce608949 --- /dev/null +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts @@ -0,0 +1,16 @@ +import { useSubpoenaStatusQuery } from './getSubpoenaStatus.generated' + +const useSubpoena = () => { + const { data: subpoenaStatus } = useSubpoenaStatusQuery({ + variables: { + input: { + caseId: '8aee2f76-69cb-4336-9ce7-96e33a6c321b', + subpoenaId: '5cd30560-4a24-4980-b5b4-5150578be755', + }, + }, + }) + + return { subpoenaStatus } +} + +export default useSubpoena From b5c0ba535b9605cc60f6a87683fc99de719e3b78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Wed, 2 Oct 2024 09:01:27 +0000 Subject: [PATCH 35/61] Error handling --- .../src/app/modules/police/police.service.ts | 23 ++++++------ .../Court/Indictments/Overview/Overview.tsx | 36 +++++++++++++------ .../web/src/utils/hooks/useSubpoena/index.ts | 8 +++-- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 510cfddecb15..911a80fa6742 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -340,11 +340,10 @@ export class PoliceService { ) .then(async (res: Response) => { if (res.ok) { - const response = await res.json() + const response: z.infer = + await res.json() - // TODO: parse this correctly - // this.subpoenaStructure.parse(response) - const parsedResponse = JSON.parse(response) + this.subpoenaStructure.parse(response) const subpoenaToUpdate = await this.subpoenaService.findBySubpoenaId( subpoenaId, @@ -353,17 +352,17 @@ export class PoliceService { const updatedSubpoena = await this.subpoenaService.update( subpoenaToUpdate, { - comment: parsedResponse.comment, - servedBy: parsedResponse.servedBy, - defenderNationalId: parsedResponse.defenderNationalId, - serviceStatus: parsedResponse.deliveredToLawyer + comment: response.comment, + servedBy: response.servedBy, + defenderNationalId: response.defenderNationalId, + serviceStatus: response.deliveredToLawyer ? ServiceStatus.DEFENDER - : parsedResponse.prosecutedConfirmedSubpoenaThroughIslandis + : response.prosecutedConfirmedSubpoenaThroughIslandis ? ServiceStatus.ELECTRONICALLY - : parsedResponse.deliveredOnPaper + : response.deliveredOnPaper ? ServiceStatus.IN_PERSON - : parsedResponse.acknowledged === false && - parsedResponse.defenderChoice === 'HAS_NOT_ACKNOWLEDGED' + : response.acknowledged === false && + response.defenderChoice === 'HAS_NOT_ACKNOWLEDGED' ? ServiceStatus.FAILED : // TODO: handle expired undefined, diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 13d653008e8e..e0bbb0b3e296 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -2,7 +2,14 @@ import { FC, useCallback, useContext, useEffect, useState } from 'react' import { IntlShape, MessageDescriptor, useIntl } from 'react-intl' import { useRouter } from 'next/router' -import { Accordion, AlertMessage, Box, Text } from '@island.is/island-ui/core' +import { + Accordion, + AlertBanner, + AlertMessage, + Box, + LoadingDots, + Text, +} from '@island.is/island-ui/core' import * as constants from '@island.is/judicial-system/consts' import { formatDate } from '@island.is/judicial-system/formatters' import { type Lawyer } from '@island.is/judicial-system/types' @@ -142,7 +149,8 @@ const IndictmentOverview = () => { const latestDate = workingCase.courtDate ?? workingCase.arraignmentDate const isArraignmentScheduled = Boolean(workingCase.arraignmentDate) - const { subpoenaStatus } = useSubpoena() + const { subpoenaStatus, subpoenaStatusLoading, subpoenaStatusError } = + useSubpoena() // const caseHasBeenReceivedByCourt = workingCase.state === CaseState.RECEIVED const handleNavigationTo = useCallback( @@ -180,14 +188,22 @@ const IndictmentOverview = () => { {formatMessage(strings.inProgressTitle)} - {workingCase.defendants?.map((defendant) => - defendant.subpoenas?.map((subpoena) => ( - - )), + {subpoenaStatusError ? ( + + + + ) : subpoenaStatusLoading ? ( + + ) : ( + workingCase.defendants?.map((defendant) => + defendant.subpoenas?.map((subpoena) => ( + + )), + ) )} {workingCase.court && latestDate?.date && diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts index d61dce608949..396ec75b56d0 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts @@ -1,7 +1,11 @@ import { useSubpoenaStatusQuery } from './getSubpoenaStatus.generated' const useSubpoena = () => { - const { data: subpoenaStatus } = useSubpoenaStatusQuery({ + const { + data: subpoenaStatus, + loading: subpoenaStatusLoading, + error: subpoenaStatusError, + } = useSubpoenaStatusQuery({ variables: { input: { caseId: '8aee2f76-69cb-4336-9ce7-96e33a6c321b', @@ -10,7 +14,7 @@ const useSubpoena = () => { }, }) - return { subpoenaStatus } + return { subpoenaStatus, subpoenaStatusLoading, subpoenaStatusError } } export default useSubpoena From 99ea3bc264e892d618d2e1c9d17eb60b8b6e12ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Wed, 2 Oct 2024 09:05:50 +0000 Subject: [PATCH 36/61] Move error handling --- .../Court/Indictments/Overview/Overview.tsx | 38 +++++++++---------- .../web/src/utils/hooks/useSubpoena/index.ts | 6 +-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index e0bbb0b3e296..9dfaafc86d57 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -103,6 +103,9 @@ interface ServiceAnnouncement { const ServiceAnnouncement: FC = (props) => { const { subpoena, defendantName } = props + const { subpoenaStatus, subpoenaStatusLoading, subpoenaStatusError } = + useSubpoena(subpoena.caseId, subpoena.subpoenaId) + const { formatMessage } = useIntl() const lawyer = useGetLawyer( @@ -113,7 +116,13 @@ const ServiceAnnouncement: FC = (props) => { const title = mapServiceStatusTitle(subpoena.serviceStatus) const messages = mapServiceStatusMessages(subpoena, formatMessage, lawyer) - return !defendantName ? null : ( + return !defendantName ? null : subpoenaStatusError ? ( + + + + ) : subpoenaStatusLoading ? ( + + ) : ( { const latestDate = workingCase.courtDate ?? workingCase.arraignmentDate const isArraignmentScheduled = Boolean(workingCase.arraignmentDate) - const { subpoenaStatus, subpoenaStatusLoading, subpoenaStatusError } = - useSubpoena() + // const caseHasBeenReceivedByCourt = workingCase.state === CaseState.RECEIVED const handleNavigationTo = useCallback( @@ -188,22 +196,14 @@ const IndictmentOverview = () => { {formatMessage(strings.inProgressTitle)} - {subpoenaStatusError ? ( - - - - ) : subpoenaStatusLoading ? ( - - ) : ( - workingCase.defendants?.map((defendant) => - defendant.subpoenas?.map((subpoena) => ( - - )), - ) + {workingCase.defendants?.map((defendant) => + defendant.subpoenas?.map((subpoena) => ( + + )), )} {workingCase.court && latestDate?.date && diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts index 396ec75b56d0..3d2ab133f847 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts @@ -1,6 +1,6 @@ import { useSubpoenaStatusQuery } from './getSubpoenaStatus.generated' -const useSubpoena = () => { +const useSubpoena = (caseId?: string | null, subpoenaId?: string | null) => { const { data: subpoenaStatus, loading: subpoenaStatusLoading, @@ -8,8 +8,8 @@ const useSubpoena = () => { } = useSubpoenaStatusQuery({ variables: { input: { - caseId: '8aee2f76-69cb-4336-9ce7-96e33a6c321b', - subpoenaId: '5cd30560-4a24-4980-b5b4-5150578be755', + caseId: caseId ?? '', + subpoenaId: subpoenaId ?? '', }, }, }) From 02dcc7f4f26bb558427c3352e8d8646bc581cd27 Mon Sep 17 00:00:00 2001 From: unakb Date: Wed, 2 Oct 2024 15:16:47 +0000 Subject: [PATCH 37/61] Update police.service.ts --- .../src/app/modules/police/police.service.ts | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 911a80fa6742..7d4ccd131b9f 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -128,15 +128,15 @@ export class PoliceService { malseinings: z.optional(z.array(this.crimeSceneStructure)), }) private subpoenaStructure = z.object({ - acknowledged: z.boolean(), - comment: z.string(), - defenderChoice: z.string(), - defenderNationalId: z.string(), - prosecutedConfirmedSubpoenaThroughIslandis: z.boolean(), - servedBy: z.string(), - delivered: z.boolean(), - deliveredOnPaper: z.boolean(), - deliveredToLawyer: z.boolean(), + acknowledged: z.boolean().nullish(), + comment: z.string().nullish(), + defenderChoice: z.string().nullish(), + defenderNationalId: z.string().nullish(), + prosecutedConfirmedSubpoenaThroughIslandis: z.boolean().nullish(), + servedBy: z.string().nullish(), + delivered: z.boolean().nullish(), + deliveredOnPaper: z.boolean().nullish(), + deliveredToLawyer: z.boolean().nullish(), }) constructor( @@ -340,8 +340,14 @@ export class PoliceService { ) .then(async (res: Response) => { if (res.ok) { + let subpoenaResponse = await res.json() + + if (typeof subpoenaResponse === 'string') { + subpoenaResponse = JSON.parse(subpoenaResponse) + } + const response: z.infer = - await res.json() + subpoenaResponse this.subpoenaStructure.parse(response) @@ -352,9 +358,9 @@ export class PoliceService { const updatedSubpoena = await this.subpoenaService.update( subpoenaToUpdate, { - comment: response.comment, - servedBy: response.servedBy, - defenderNationalId: response.defenderNationalId, + comment: response.comment ?? undefined, + servedBy: response.servedBy ?? undefined, + defenderNationalId: response.defenderNationalId ?? undefined, serviceStatus: response.deliveredToLawyer ? ServiceStatus.DEFENDER : response.prosecutedConfirmedSubpoenaThroughIslandis From 6d7286095e41733375c3c13d9c399e1a91e88366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Wed, 2 Oct 2024 15:37:36 +0000 Subject: [PATCH 38/61] Merge --- .../backend/src/app/modules/police/police.service.ts | 3 +++ .../src/app/modules/subpoena/dto/updateSubpoena.dto.ts | 7 ++++++- .../web/src/utils/hooks/useSubpoena/index.ts | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 7d4ccd131b9f..40faf943b008 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -134,6 +134,7 @@ export class PoliceService { defenderNationalId: z.string().nullish(), prosecutedConfirmedSubpoenaThroughIslandis: z.boolean().nullish(), servedBy: z.string().nullish(), + servedAt: z.date().nullish(), delivered: z.boolean().nullish(), deliveredOnPaper: z.boolean().nullish(), deliveredToLawyer: z.boolean().nullish(), @@ -350,6 +351,7 @@ export class PoliceService { subpoenaResponse this.subpoenaStructure.parse(response) + console.log('!!!!!!!!!!!!!!!!!!!', subpoenaResponse) const subpoenaToUpdate = await this.subpoenaService.findBySubpoenaId( subpoenaId, @@ -361,6 +363,7 @@ export class PoliceService { comment: response.comment ?? undefined, servedBy: response.servedBy ?? undefined, defenderNationalId: response.defenderNationalId ?? undefined, + serviceDate: response.servedAt ?? undefined, serviceStatus: response.deliveredToLawyer ? ServiceStatus.DEFENDER : response.prosecutedConfirmedSubpoenaThroughIslandis diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts index 49fce7066ac7..0dec9634cc62 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts @@ -1,4 +1,4 @@ -import { IsEnum, IsOptional, IsString } from 'class-validator' +import { IsDate, IsEnum, IsOptional, IsString } from 'class-validator' import { ApiPropertyOptional } from '@nestjs/swagger' @@ -15,6 +15,11 @@ export class UpdateSubpoenaDto { @ApiPropertyOptional({ type: String }) readonly servedBy?: string + @IsOptional() + @IsDate() + @ApiPropertyOptional({ type: Date }) + readonly serviceDate?: Date + @IsOptional() @IsString() @ApiPropertyOptional({ type: String }) diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts index 3d2ab133f847..64961b907edd 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts @@ -8,8 +8,8 @@ const useSubpoena = (caseId?: string | null, subpoenaId?: string | null) => { } = useSubpoenaStatusQuery({ variables: { input: { - caseId: caseId ?? '', - subpoenaId: subpoenaId ?? '', + caseId: '2857102e-d35f-4726-893e-ff19a6f78127', //caseId ?? '', + subpoenaId: '5cd30560-4a24-4980-b5b4-5150578be755', // subpoenaId ?? '', }, }, }) From b513dbc72e06a85eb8c3c9fb88e2c6d88ffbe085 Mon Sep 17 00:00:00 2001 From: unakb Date: Wed, 2 Oct 2024 16:44:42 +0000 Subject: [PATCH 39/61] Update police.service.ts --- .../backend/src/app/modules/police/police.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 40faf943b008..e5cfc72fde69 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -35,6 +35,7 @@ import { Case } from '../case' import { Defendant } from '../defendant/models/defendant.model' import { EventService } from '../event' import { Subpoena, SubpoenaService } from '../subpoena' +import { UpdateSubpoenaDto } from '../subpoena/dto/updateSubpoena.dto' import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { CreateSubpoenaResponse } from './models/createSubpoena.response' import { PoliceCaseFile } from './models/policeCaseFile.model' @@ -134,7 +135,7 @@ export class PoliceService { defenderNationalId: z.string().nullish(), prosecutedConfirmedSubpoenaThroughIslandis: z.boolean().nullish(), servedBy: z.string().nullish(), - servedAt: z.date().nullish(), + servedAt: z.string().nullish(), delivered: z.boolean().nullish(), deliveredOnPaper: z.boolean().nullish(), deliveredToLawyer: z.boolean().nullish(), @@ -343,15 +344,14 @@ export class PoliceService { if (res.ok) { let subpoenaResponse = await res.json() + //TODO: Remove when RLS has deployed new version without double encoding if (typeof subpoenaResponse === 'string') { subpoenaResponse = JSON.parse(subpoenaResponse) } const response: z.infer = subpoenaResponse - this.subpoenaStructure.parse(response) - console.log('!!!!!!!!!!!!!!!!!!!', subpoenaResponse) const subpoenaToUpdate = await this.subpoenaService.findBySubpoenaId( subpoenaId, @@ -375,7 +375,7 @@ export class PoliceService { ? ServiceStatus.FAILED : // TODO: handle expired undefined, - }, + } as UpdateSubpoenaDto, ) return updatedSubpoena From 856e5c520fe09f72720bb0ad120352ddcabf6b86 Mon Sep 17 00:00:00 2001 From: unakb Date: Wed, 2 Oct 2024 16:48:27 +0000 Subject: [PATCH 40/61] Update police.service.ts --- .../backend/src/app/modules/police/police.service.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index e5cfc72fde69..2194c7cf6234 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -368,10 +368,9 @@ export class PoliceService { ? ServiceStatus.DEFENDER : response.prosecutedConfirmedSubpoenaThroughIslandis ? ServiceStatus.ELECTRONICALLY - : response.deliveredOnPaper + : response.deliveredOnPaper || response.delivered === true ? ServiceStatus.IN_PERSON - : response.acknowledged === false && - response.defenderChoice === 'HAS_NOT_ACKNOWLEDGED' + : response.acknowledged === false ? ServiceStatus.FAILED : // TODO: handle expired undefined, From 4a2145fd6b057e3344100502325da6791280ccfb Mon Sep 17 00:00:00 2001 From: unakb Date: Wed, 2 Oct 2024 16:53:39 +0000 Subject: [PATCH 41/61] Update police.service.ts --- .../backend/src/app/modules/police/police.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 2194c7cf6234..1d098866f155 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -350,8 +350,7 @@ export class PoliceService { } const response: z.infer = - subpoenaResponse - this.subpoenaStructure.parse(response) + this.subpoenaStructure.parse(subpoenaResponse) const subpoenaToUpdate = await this.subpoenaService.findBySubpoenaId( subpoenaId, From 42d7781689847a82c706de5c7e5f92d18f0df74e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Wed, 2 Oct 2024 21:08:00 +0000 Subject: [PATCH 42/61] Error handling in frontend --- apps/judicial-system/web/messages/Core/errors.ts | 12 ++++++++++++ .../routes/Court/Indictments/Overview/Overview.tsx | 12 +++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/apps/judicial-system/web/messages/Core/errors.ts b/apps/judicial-system/web/messages/Core/errors.ts index 3123d4e8745c..2f3a6f63c441 100644 --- a/apps/judicial-system/web/messages/Core/errors.ts +++ b/apps/judicial-system/web/messages/Core/errors.ts @@ -145,4 +145,16 @@ export const errors = defineMessages({ defaultMessage: 'Upp kom villa við að opna skjal', description: 'Notaður sem villuskilaboð þegar ekki gengur að opna skjal', }, + getSubpoenaStatusTitle: { + id: 'judicial.system.core:errors.get_subpoena_status_title', + defaultMessage: 'Ekki tókst að sækja stöðu birtingar', + description: + 'Notaður sem villuskilaboð þegar tekst að sækja stöðu birtingar', + }, + getSubpoenaStatus: { + id: 'judicial.system.core:errors.get_subpoena_status', + defaultMessage: 'Vinsamlegast reyndu aftur síðar', + description: + 'Notaður sem villuskilaboð þegar tekst að sækja stöðu birtingar', + }, }) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 9dfaafc86d57..17c5203ffaf5 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -13,7 +13,7 @@ import { import * as constants from '@island.is/judicial-system/consts' import { formatDate } from '@island.is/judicial-system/formatters' import { type Lawyer } from '@island.is/judicial-system/types' -import { core, titles } from '@island.is/judicial-system-web/messages' +import { core, errors, titles } from '@island.is/judicial-system-web/messages' import { ConnectedCaseFilesAccordionItem, CourtCaseInfo, @@ -118,10 +118,16 @@ const ServiceAnnouncement: FC = (props) => { return !defendantName ? null : subpoenaStatusError ? ( - + ) : subpoenaStatusLoading ? ( - + + + ) : ( Date: Thu, 3 Oct 2024 13:52:36 +0000 Subject: [PATCH 43/61] Cleanup --- apps/judicial-system/backend/src/app/modules/index.ts | 1 - .../web/src/routes/Court/Indictments/Overview/Overview.tsx | 3 +-- .../web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql | 2 ++ 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/index.ts b/apps/judicial-system/backend/src/app/modules/index.ts index e6e40ad4a580..d2aee8e277e6 100644 --- a/apps/judicial-system/backend/src/app/modules/index.ts +++ b/apps/judicial-system/backend/src/app/modules/index.ts @@ -19,4 +19,3 @@ export { AwsS3Module } from './aws-s3/awsS3.module' export { EventModule } from './event/event.module' export { EventLogModule } from './event-log/eventLog.module' export { SubpoenaModule } from './subpoena/subpoena.module' -export { SubpoenaService } from './subpoena/subpoena.service' diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 17c5203ffaf5..e20634500ab5 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -1,10 +1,9 @@ -import { FC, useCallback, useContext, useEffect, useState } from 'react' +import { FC, useCallback, useContext, useState } from 'react' import { IntlShape, MessageDescriptor, useIntl } from 'react-intl' import { useRouter } from 'next/router' import { Accordion, - AlertBanner, AlertMessage, Box, LoadingDots, diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql b/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql index d946e4f549c6..153c00bd0fcd 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql @@ -2,5 +2,7 @@ query SubpoenaStatus($input: SubpoenaStatusQueryInput!) { subpoenaStatus(input: $input) { serviceStatus servedBy + comment + serviceDate } } From f5a40832e68e99cf6d18205c278b41414e9372ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 3 Oct 2024 13:55:16 +0000 Subject: [PATCH 44/61] Cleanup --- .../src/app/modules/police/police.controller.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts index 09851362db87..513ae24678ae 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts @@ -34,7 +34,7 @@ import { CaseReadGuard, CurrentCase, } from '../case' -import { UpdateSubpoenaDto } from '../subpoena/dto/updateSubpoena.dto' +import { Subpoena } from '../subpoena' import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { PoliceCaseFile } from './models/policeCaseFile.model' import { PoliceCaseInfo } from './models/policeCaseInfo.model' @@ -81,14 +81,14 @@ export class PoliceController { ) @Get('subpoenaStatus/:subpoenaId') @ApiOkResponse({ - type: UpdateSubpoenaDto, + type: Subpoena, description: 'Gets subpoena status', }) getSubpoenaStatus( - @Param('caseId') caseId: string, @Param('subpoenaId') subpoenaId: string, - ): Promise { - this.logger.debug(`Gets subpoena status in case ${caseId}`) + @CurrentCase() theCase: Case, + ): Promise { + this.logger.debug(`Gets subpoena status in case ${theCase.id}`) return this.policeService.getSubpoenaStatus(subpoenaId) } From c447ec9f2bf350b019b39aa3c719ac4856b025bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 3 Oct 2024 14:03:36 +0000 Subject: [PATCH 45/61] Cleanup --- .../app/modules/police/police.controller.ts | 3 +- .../src/app/modules/police/police.service.ts | 42 +++++++++++++------ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts index 513ae24678ae..7eaa7773c91b 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts @@ -87,10 +87,11 @@ export class PoliceController { getSubpoenaStatus( @Param('subpoenaId') subpoenaId: string, @CurrentCase() theCase: Case, + @CurrentHttpUser() user: User, ): Promise { this.logger.debug(`Gets subpoena status in case ${theCase.id}`) - return this.policeService.getSubpoenaStatus(subpoenaId) + return this.policeService.getSubpoenaStatus(subpoenaId, user) } @RolesRules(prosecutorRule, prosecutorRepresentativeRule) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 1d098866f155..f3ae5bc999c1 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -336,7 +336,7 @@ export class PoliceService { }) } - async getSubpoenaStatus(subpoenaId: string): Promise { + async getSubpoenaStatus(subpoenaId: string, user: User): Promise { return this.fetchPoliceDocumentApi( `${this.xRoadPath}/GetSubpoenaStatus?id=${subpoenaId}`, ) @@ -389,18 +389,34 @@ export class PoliceService { }) }) .catch((reason) => { - // TODO: Handle this - throw reason - - // this.eventService.postErrorEvent( - // 'Failed to get subpoena', - // { - // subpoenaId, - // actor: user.name, - // institution: user.institution?.name, - // }, - // reason, - // ) + if (reason instanceof NotFoundException) { + throw reason + } + + if (reason instanceof ServiceUnavailableException) { + // Act as if the case does not exist + throw new NotFoundException({ + ...reason, + message: `Subpoena ${subpoenaId} does not exist`, + detail: reason.message, + }) + } + + this.eventService.postErrorEvent( + 'Failed to get police case info', + { + subpoenaId, + actor: user.name, + institution: user.institution?.name, + }, + reason, + ) + + throw new BadGatewayException({ + ...reason, + message: `Failed to get subpoena ${subpoenaId}`, + detail: reason.message, + }) }) } From ce21c013de1c7526a642595337ccd0f1d2263091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 3 Oct 2024 14:11:39 +0000 Subject: [PATCH 46/61] Revert processing --- .../routes/Prosecutor/Indictments/Processing/Processing.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx index 73ead7c9a24d..c76de5e43862 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx @@ -254,11 +254,7 @@ const Processing: FC = () => { ) useEffect(() => { - if (!personData || !personData.items) { - return - } - - if (personData.items.length === 0) { + if (!personData || !personData.items || personData.items.length === 0) { setNationalIdNotFound(true) return } From b2bd1151fc37c53b9b5b2ec98059120a2f317bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 3 Oct 2024 14:20:29 +0000 Subject: [PATCH 47/61] Cleanup --- .../web/src/components/FormProvider/case.graphql | 2 ++ apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index 387d5aec31a3..d45521ec30d9 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -34,6 +34,8 @@ query Case($input: CaseQueryInput!) { servedBy comment defenderNationalId + caseId + subpoenaId } } defenderName diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts index 64961b907edd..3d2ab133f847 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts @@ -8,8 +8,8 @@ const useSubpoena = (caseId?: string | null, subpoenaId?: string | null) => { } = useSubpoenaStatusQuery({ variables: { input: { - caseId: '2857102e-d35f-4726-893e-ff19a6f78127', //caseId ?? '', - subpoenaId: '5cd30560-4a24-4980-b5b4-5150578be755', // subpoenaId ?? '', + caseId: caseId ?? '', + subpoenaId: subpoenaId ?? '', }, }, }) From 0cd6e0593a057ef7a6dd131be90fbb0b52f097a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 3 Oct 2024 14:31:29 +0000 Subject: [PATCH 48/61] Error handling --- .../Court/Indictments/Overview/Overview.tsx | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index e20634500ab5..89f50ee812f5 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -94,6 +94,14 @@ const mapServiceStatusMessages = ( } } +const renderError = (formatMessage: IntlShape['formatMessage']) => ( + +) + interface ServiceAnnouncement { subpoena: Subpoena defendantName?: string | null @@ -112,18 +120,19 @@ const ServiceAnnouncement: FC = (props) => { subpoena.serviceStatus === ServiceStatus.DEFENDER, ) + // Use data from RLS but fallback to local data + const subpoenaServiceStatus = subpoenaStatusError + ? subpoena.serviceStatus + : subpoenaStatus?.subpoenaStatus?.serviceStatus + + if (!subpoenaServiceStatus && !subpoenaStatusLoading) { + return {renderError(formatMessage)} + } + const title = mapServiceStatusTitle(subpoena.serviceStatus) const messages = mapServiceStatusMessages(subpoena, formatMessage, lawyer) - return !defendantName ? null : subpoenaStatusError ? ( - - - - ) : subpoenaStatusLoading ? ( + return !defendantName ? null : subpoenaStatusLoading ? ( From 9c6300f6055b6542126bd524a45a0095fd9f159b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 3 Oct 2024 14:50:25 +0000 Subject: [PATCH 49/61] Add to prosecutor screen --- .../ServiceAnnouncement.strings.ts | 39 +++++ .../ServiceAnnouncement.tsx | 133 ++++++++++++++++ .../web/src/components/index.ts | 1 + .../Indictments/Overview/Overview.strings.ts | 35 ----- .../Court/Indictments/Overview/Overview.tsx | 146 +----------------- .../Indictments/Overview/Overview.tsx | 10 ++ 6 files changed, 190 insertions(+), 174 deletions(-) create mode 100644 apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.strings.ts create mode 100644 apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx diff --git a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.strings.ts b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.strings.ts new file mode 100644 index 000000000000..a145bc5b8307 --- /dev/null +++ b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.strings.ts @@ -0,0 +1,39 @@ +import { defineMessages } from 'react-intl' + +export const strings = defineMessages({ + serviceStatusSuccess: { + id: 'judicial.system.core:service_announcement.service_status_success', + defaultMessage: 'Birting tókst', + description: 'Notaður sem texti þegar birting tókst.', + }, + serviceStatusExpired: { + id: 'judicial.system.core:service_announcement.service_status_expired', + defaultMessage: 'Birting tókst ekki', + description: 'Notaður sem texti þegar birting rann út á tíma.', + }, + serviceStatusExpiredMessage: { + id: 'judicial.system.core:service_announcement.service_status_expired_message', + defaultMessage: 'Ekki tókst að birta fyrir þingfestingu.', + description: 'Notaður sem texti þegar birting rann út á tíma.', + }, + serviceStatusFailed: { + id: 'judicial.system.core:service_announcement.service_status_failed', + defaultMessage: 'Árangurslaus birting', + description: 'Notaður sem texti þegar birting tókst ekki.', + }, + serviceStatusUnknown: { + id: 'judicial.system.core:service_announcement.service_status_unknown', + defaultMessage: 'Birtingastaða óþekkt', + description: 'Notaður sem texti þegar ekki er vitað um stöðu birtingar.', + }, + servedToDefender: { + id: 'judicial.system.core:service_announcement.served_to_defender', + defaultMessage: 'Birt fyrir verjanda - {lawyerName} {practice}', + description: 'Notaður sem texti þegar birti var verjanda.', + }, + servedToElectronically: { + id: 'judicial.system.core:service_announcement.served_electronically', + defaultMessage: 'Rafrænt pósthólf island.is - {date}', + description: 'Notaður sem texti þegar birti var í pósthólfi.', + }, +}) diff --git a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx new file mode 100644 index 000000000000..f71e7a01cfd2 --- /dev/null +++ b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx @@ -0,0 +1,133 @@ +import { FC } from 'react' +import { IntlShape, MessageDescriptor, useIntl } from 'react-intl' + +import { AlertMessage, Box, LoadingDots, Text } from '@island.is/island-ui/core' +import { formatDate } from '@island.is/judicial-system/formatters' +import { type Lawyer } from '@island.is/judicial-system/types' +import { errors } from '@island.is/judicial-system-web/messages' +import { + ServiceStatus, + Subpoena, +} from '@island.is/judicial-system-web/src/graphql/schema' + +import { useGetLawyer, useSubpoena } from '../../utils/hooks' +import { strings } from './ServiceAnnouncement.strings' + +const mapServiceStatusTitle = ( + serviceStatus?: ServiceStatus | null, +): MessageDescriptor => { + switch (serviceStatus) { + case ServiceStatus.DEFENDER: + case ServiceStatus.ELECTRONICALLY: + case ServiceStatus.IN_PERSON: + return strings.serviceStatusSuccess + case ServiceStatus.EXPIRED: + return strings.serviceStatusExpired + case ServiceStatus.FAILED: + return strings.serviceStatusFailed + // Should not happen + default: + return strings.serviceStatusUnknown + } +} + +const mapServiceStatusMessages = ( + subpoena: Subpoena, + formatMessage: IntlShape['formatMessage'], + lawyer?: Lawyer, +) => { + switch (subpoena.serviceStatus) { + case ServiceStatus.DEFENDER: + return [ + `${subpoena.servedBy} - ${formatDate(subpoena.serviceDate, 'Pp')}`, + formatMessage(strings.servedToDefender, { + lawyerName: lawyer?.name, + practice: lawyer?.practice, + }), + ] + case ServiceStatus.ELECTRONICALLY: + return [ + formatMessage(strings.servedToElectronically, { + date: formatDate(subpoena.serviceDate, 'Pp'), + }), + ] + case ServiceStatus.IN_PERSON: + case ServiceStatus.FAILED: + return [ + `${subpoena.servedBy} - ${formatDate(subpoena.serviceDate, 'Pp')}`, + subpoena.comment, + ] + case ServiceStatus.EXPIRED: + return [formatMessage(strings.serviceStatusExpiredMessage)] + default: + return [] + } +} + +const renderError = (formatMessage: IntlShape['formatMessage']) => ( + +) + +interface ServiceAnnouncement { + subpoena: Subpoena + defendantName?: string | null +} + +const ServiceAnnouncement: FC = (props) => { + const { subpoena, defendantName } = props + + const { subpoenaStatus, subpoenaStatusLoading, subpoenaStatusError } = + useSubpoena(subpoena.caseId, subpoena.subpoenaId) + + const { formatMessage } = useIntl() + + const lawyer = useGetLawyer( + subpoena.defenderNationalId, + subpoena.serviceStatus === ServiceStatus.DEFENDER, + ) + + // Use data from RLS but fallback to local data + const subpoenaServiceStatus = subpoenaStatusError + ? subpoena.serviceStatus + : subpoenaStatus?.subpoenaStatus?.serviceStatus + + if (!subpoenaServiceStatus && !subpoenaStatusLoading) { + return {renderError(formatMessage)} + } + + const title = mapServiceStatusTitle(subpoena.serviceStatus) + const messages = mapServiceStatusMessages(subpoena, formatMessage, lawyer) + + return !defendantName ? null : subpoenaStatusLoading ? ( + + + + ) : ( + + + {messages.map((msg) => ( + + {msg} + + ))} + + } + type={ + subpoena.serviceStatus === ServiceStatus.FAILED || + subpoena.serviceStatus === ServiceStatus.EXPIRED + ? 'warning' + : 'success' + } + /> + + ) +} + +export default ServiceAnnouncement diff --git a/apps/judicial-system/web/src/components/index.ts b/apps/judicial-system/web/src/components/index.ts index fa0cfce3c06f..85c50b4f5967 100644 --- a/apps/judicial-system/web/src/components/index.ts +++ b/apps/judicial-system/web/src/components/index.ts @@ -62,6 +62,7 @@ export { default as RestrictionTags } from './RestrictionTags/RestrictionTags' export { default as RulingAccordionItem } from './AccordionItems/RulingAccordionItem/RulingAccordionItem' export { default as RulingInput } from './RulingInput/RulingInput' export { default as SectionHeading } from './SectionHeading/SectionHeading' +export { default as ServiceAnnouncement } from './ServiceAnnouncement/ServiceAnnouncement' export { default as ServiceInterruptionBanner } from './ServiceInterruptionBanner/ServiceInterruptionBanner' export { default as SignedDocument } from './SignedDocument/SignedDocument' export { default as OverviewHeader } from './OverviewHeader/OverviewHeader' diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts index 580e935364e3..b479674bf746 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.strings.ts @@ -12,39 +12,4 @@ export const strings = defineMessages({ defaultMessage: 'Endursenda', description: 'Notaður sem texti á takka til að endursenda ákæru.', }, - serviceStatusSuccess: { - id: 'judicial.system.core:indictment_overview.service_status_success', - defaultMessage: 'Birting tókst', - description: 'Notaður sem texti þegar birting tókst.', - }, - serviceStatusExpired: { - id: 'judicial.system.core:indictment_overview.service_status_expired', - defaultMessage: 'Birting tókst ekki', - description: 'Notaður sem texti þegar birting rann út á tíma.', - }, - serviceStatusExpiredMessage: { - id: 'judicial.system.core:indictment_overview.service_status_expired_message', - defaultMessage: 'Ekki tókst að birta fyrir þingfestingu.', - description: 'Notaður sem texti þegar birting rann út á tíma.', - }, - serviceStatusFailed: { - id: 'judicial.system.core:indictment_overview.service_status_failed', - defaultMessage: 'Árangurslaus birting', - description: 'Notaður sem texti þegar birting tókst ekki.', - }, - serviceStatusUnknown: { - id: 'judicial.system.core:indictment_overview.service_status_unknown', - defaultMessage: 'Birtingastaða óþekkt', - description: 'Notaður sem texti þegar ekki er vitað um stöðu birtingar.', - }, - servedToDefender: { - id: 'judicial.system.core:indictment_overview.served_to_defender', - defaultMessage: 'Birt fyrir verjanda - {lawyerName} {practice}', - description: 'Notaður sem texti þegar birti var verjanda.', - }, - servedToElectronically: { - id: 'judicial.system.core:indictment_overview.served_electronically', - defaultMessage: 'Rafrænt pósthólf island.is - {date}', - description: 'Notaður sem texti þegar birti var í pósthólfi.', - }, }) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 89f50ee812f5..64b604f4e7b1 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -1,18 +1,10 @@ -import { FC, useCallback, useContext, useState } from 'react' -import { IntlShape, MessageDescriptor, useIntl } from 'react-intl' +import { useCallback, useContext, useState } from 'react' +import { useIntl } from 'react-intl' import { useRouter } from 'next/router' -import { - Accordion, - AlertMessage, - Box, - LoadingDots, - Text, -} from '@island.is/island-ui/core' +import { Accordion, Box } from '@island.is/island-ui/core' import * as constants from '@island.is/judicial-system/consts' -import { formatDate } from '@island.is/judicial-system/formatters' -import { type Lawyer } from '@island.is/judicial-system/types' -import { core, errors, titles } from '@island.is/judicial-system-web/messages' +import { core, titles } from '@island.is/judicial-system-web/messages' import { ConnectedCaseFilesAccordionItem, CourtCaseInfo, @@ -26,140 +18,16 @@ import { PageHeader, PageLayout, PageTitle, + ServiceAnnouncement, useIndictmentsLawsBroken, } from '@island.is/judicial-system-web/src/components' -import { - IndictmentDecision, - ServiceStatus, - Subpoena, -} from '@island.is/judicial-system-web/src/graphql/schema' -import { - useDefendants, - useGetLawyer, - useSubpoena, -} from '@island.is/judicial-system-web/src/utils/hooks' +import { IndictmentDecision } from '@island.is/judicial-system-web/src/graphql/schema' +import { useDefendants } from '@island.is/judicial-system-web/src/utils/hooks' import { SubpoenaType } from '../../components' import ReturnIndictmentModal from '../ReturnIndictmentCaseModal/ReturnIndictmentCaseModal' import { strings } from './Overview.strings' -const mapServiceStatusTitle = ( - serviceStatus?: ServiceStatus | null, -): MessageDescriptor => { - switch (serviceStatus) { - case ServiceStatus.DEFENDER: - case ServiceStatus.ELECTRONICALLY: - case ServiceStatus.IN_PERSON: - return strings.serviceStatusSuccess - case ServiceStatus.EXPIRED: - return strings.serviceStatusExpired - case ServiceStatus.FAILED: - return strings.serviceStatusFailed - // Should not happen - default: - return strings.serviceStatusUnknown - } -} - -const mapServiceStatusMessages = ( - subpoena: Subpoena, - formatMessage: IntlShape['formatMessage'], - lawyer?: Lawyer, -) => { - switch (subpoena.serviceStatus) { - case ServiceStatus.DEFENDER: - return [ - `${subpoena.servedBy} - ${formatDate(subpoena.serviceDate, 'Pp')}`, - formatMessage(strings.servedToDefender, { - lawyerName: lawyer?.name, - practice: lawyer?.practice, - }), - ] - case ServiceStatus.ELECTRONICALLY: - return [ - formatMessage(strings.servedToElectronically, { - date: formatDate(subpoena.serviceDate, 'Pp'), - }), - ] - case ServiceStatus.IN_PERSON: - case ServiceStatus.FAILED: - return [ - `${subpoena.servedBy} - ${formatDate(subpoena.serviceDate, 'Pp')}`, - subpoena.comment, - ] - case ServiceStatus.EXPIRED: - return [formatMessage(strings.serviceStatusExpiredMessage)] - default: - return [] - } -} - -const renderError = (formatMessage: IntlShape['formatMessage']) => ( - -) - -interface ServiceAnnouncement { - subpoena: Subpoena - defendantName?: string | null -} - -const ServiceAnnouncement: FC = (props) => { - const { subpoena, defendantName } = props - - const { subpoenaStatus, subpoenaStatusLoading, subpoenaStatusError } = - useSubpoena(subpoena.caseId, subpoena.subpoenaId) - - const { formatMessage } = useIntl() - - const lawyer = useGetLawyer( - subpoena.defenderNationalId, - subpoena.serviceStatus === ServiceStatus.DEFENDER, - ) - - // Use data from RLS but fallback to local data - const subpoenaServiceStatus = subpoenaStatusError - ? subpoena.serviceStatus - : subpoenaStatus?.subpoenaStatus?.serviceStatus - - if (!subpoenaServiceStatus && !subpoenaStatusLoading) { - return {renderError(formatMessage)} - } - - const title = mapServiceStatusTitle(subpoena.serviceStatus) - const messages = mapServiceStatusMessages(subpoena, formatMessage, lawyer) - - return !defendantName ? null : subpoenaStatusLoading ? ( - - - - ) : ( - - - {messages.map((msg) => ( - - {msg} - - ))} - - } - type={ - subpoena.serviceStatus === ServiceStatus.FAILED || - subpoena.serviceStatus === ServiceStatus.EXPIRED - ? 'warning' - : 'success' - } - /> - - ) -} - const IndictmentOverview = () => { const router = useRouter() const { workingCase, isLoadingWorkingCase, caseNotFound, setWorkingCase } = diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx index 3c13b47bef12..fc8d3994709c 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx @@ -29,6 +29,7 @@ import { PageLayout, ProsecutorCaseInfo, SectionHeading, + ServiceAnnouncement, useIndictmentsLawsBroken, UserContext, } from '@island.is/judicial-system-web/src/components' @@ -193,6 +194,15 @@ const Overview: FC = () => { + {workingCase.defendants?.map((defendant) => + defendant.subpoenas?.map((subpoena) => ( + + )), + )} {workingCase.court && latestDate?.date && workingCase.indictmentDecision !== IndictmentDecision.COMPLETING && From 3ea937e35e9a0fef8733a8bc68ce313a043329e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 3 Oct 2024 15:09:11 +0000 Subject: [PATCH 50/61] Remove double encoding code --- .../backend/src/app/modules/police/police.service.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index f3ae5bc999c1..865bbfeaad71 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -342,15 +342,10 @@ export class PoliceService { ) .then(async (res: Response) => { if (res.ok) { - let subpoenaResponse = await res.json() - - //TODO: Remove when RLS has deployed new version without double encoding - if (typeof subpoenaResponse === 'string') { - subpoenaResponse = JSON.parse(subpoenaResponse) - } - const response: z.infer = - this.subpoenaStructure.parse(subpoenaResponse) + await res.json() + + this.subpoenaStructure.parse(response) const subpoenaToUpdate = await this.subpoenaService.findBySubpoenaId( subpoenaId, From 1d9bb2aad3d42d456bb3d4e1c31f342177595f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Fri, 4 Oct 2024 12:50:32 +0000 Subject: [PATCH 51/61] Hide ServiceAnnouncement if no subpoena id --- .../Court/Indictments/Overview/Overview.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index 64b604f4e7b1..bf1f5cfedb8a 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -79,13 +79,16 @@ const IndictmentOverview = () => { {formatMessage(strings.inProgressTitle)} {workingCase.defendants?.map((defendant) => - defendant.subpoenas?.map((subpoena) => ( - - )), + defendant.subpoenas?.map( + (subpoena) => + subpoena.subpoenaId && ( + + ), + ), )} {workingCase.court && latestDate?.date && From 265a601c0868791152b2596ea60df34260af0518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Fri, 4 Oct 2024 14:56:46 +0000 Subject: [PATCH 52/61] Use correct data --- .../ServiceAnnouncement.tsx | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx index f71e7a01cfd2..422a30964d8f 100644 --- a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx +++ b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react' +import { FC, useEffect, useState } from 'react' import { IntlShape, MessageDescriptor, useIntl } from 'react-intl' import { AlertMessage, Box, LoadingDots, Text } from '@island.is/island-ui/core' @@ -11,6 +11,7 @@ import { } from '@island.is/judicial-system-web/src/graphql/schema' import { useGetLawyer, useSubpoena } from '../../utils/hooks' +import { SubpoenaStatusQuery } from '../../utils/hooks/useSubpoena/getSubpoenaStatus.generated' import { strings } from './ServiceAnnouncement.strings' const mapServiceStatusTitle = ( @@ -78,31 +79,42 @@ interface ServiceAnnouncement { } const ServiceAnnouncement: FC = (props) => { - const { subpoena, defendantName } = props + const { subpoena: localSubpoena, defendantName } = props + + const [subpoena, setSubpoena] = useState() const { subpoenaStatus, subpoenaStatusLoading, subpoenaStatusError } = - useSubpoena(subpoena.caseId, subpoena.subpoenaId) + useSubpoena(localSubpoena.caseId, localSubpoena.subpoenaId) const { formatMessage } = useIntl() const lawyer = useGetLawyer( - subpoena.defenderNationalId, - subpoena.serviceStatus === ServiceStatus.DEFENDER, + subpoena?.defenderNationalId, + subpoena?.serviceStatus === ServiceStatus.DEFENDER, ) - // Use data from RLS but fallback to local data - const subpoenaServiceStatus = subpoenaStatusError - ? subpoena.serviceStatus - : subpoenaStatus?.subpoenaStatus?.serviceStatus + const title = mapServiceStatusTitle(subpoena?.serviceStatus) + const messages = subpoena + ? mapServiceStatusMessages(subpoena, formatMessage, lawyer) + : [] - if (!subpoenaServiceStatus && !subpoenaStatusLoading) { - return {renderError(formatMessage)} - } - - const title = mapServiceStatusTitle(subpoena.serviceStatus) - const messages = mapServiceStatusMessages(subpoena, formatMessage, lawyer) + // Use data from RLS but fallback to local data + useEffect(() => { + if (subpoenaStatusError) { + setSubpoena(localSubpoena) + } else { + setSubpoena({ + ...localSubpoena, + servedBy: subpoenaStatus?.subpoenaStatus?.servedBy, + serviceStatus: subpoenaStatus?.subpoenaStatus?.serviceStatus, + serviceDate: subpoenaStatus?.subpoenaStatus?.serviceDate, + }) + } + }, [localSubpoena, subpoenaStatus, subpoenaStatusError]) - return !defendantName ? null : subpoenaStatusLoading ? ( + return !defendantName ? null : !subpoena && !subpoenaStatusLoading ? ( + {renderError(formatMessage)} + ) : subpoenaStatusLoading ? ( @@ -113,15 +125,15 @@ const ServiceAnnouncement: FC = (props) => { message={ {messages.map((msg) => ( - + {msg} ))} } type={ - subpoena.serviceStatus === ServiceStatus.FAILED || - subpoena.serviceStatus === ServiceStatus.EXPIRED + subpoena?.serviceStatus === ServiceStatus.FAILED || + subpoena?.serviceStatus === ServiceStatus.EXPIRED ? 'warning' : 'success' } From 7d0eeb85df6e36ffe68c0cb4ba1146e021a5833b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Fri, 4 Oct 2024 15:02:34 +0000 Subject: [PATCH 53/61] Add defender national id --- .../api/src/app/modules/police/models/subpoenaStatus.model.ts | 3 +++ .../src/components/ServiceAnnouncement/ServiceAnnouncement.tsx | 2 ++ .../web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql | 1 + 3 files changed, 6 insertions(+) diff --git a/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts b/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts index 94cf00bbdc05..b4617bfe58a4 100644 --- a/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts +++ b/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts @@ -15,4 +15,7 @@ export class SubpoenaStatus { @Field(() => String, { nullable: true }) readonly serviceDate?: string + + @Field(() => String, { nullable: true }) + readonly defenderNationalId?: string } diff --git a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx index 422a30964d8f..7dfd8dd4dd7c 100644 --- a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx +++ b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx @@ -108,6 +108,8 @@ const ServiceAnnouncement: FC = (props) => { servedBy: subpoenaStatus?.subpoenaStatus?.servedBy, serviceStatus: subpoenaStatus?.subpoenaStatus?.serviceStatus, serviceDate: subpoenaStatus?.subpoenaStatus?.serviceDate, + comment: subpoenaStatus?.subpoenaStatus?.comment, + defenderNationalId: subpoenaStatus?.subpoenaStatus?.defenderNationalId, }) } }, [localSubpoena, subpoenaStatus, subpoenaStatusError]) diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql b/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql index 153c00bd0fcd..a87df3e15366 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql @@ -4,5 +4,6 @@ query SubpoenaStatus($input: SubpoenaStatusQueryInput!) { servedBy comment serviceDate + defenderNationalId } } From 227b99d5ee76df030aa0fb7d59fe2bdb468aca50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Fri, 4 Oct 2024 15:06:41 +0000 Subject: [PATCH 54/61] Fix Slack log --- .../backend/src/app/modules/police/police.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 4685e0771cc0..b4a9d5e2848a 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -398,7 +398,7 @@ export class PoliceService { } this.eventService.postErrorEvent( - 'Failed to get police case info', + 'Failed to get subpoena', { subpoenaId, actor: user.name, From fa1b71ecb0741373d5f6a9a84a97eecaffc6087b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Fri, 4 Oct 2024 15:08:51 +0000 Subject: [PATCH 55/61] Rename interface --- .../components/ServiceAnnouncement/ServiceAnnouncement.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx index 7dfd8dd4dd7c..3da5a6a2af5a 100644 --- a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx +++ b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx @@ -73,12 +73,12 @@ const renderError = (formatMessage: IntlShape['formatMessage']) => ( /> ) -interface ServiceAnnouncement { +interface ServiceAnnouncementProps { subpoena: Subpoena defendantName?: string | null } -const ServiceAnnouncement: FC = (props) => { +const ServiceAnnouncement: FC = (props) => { const { subpoena: localSubpoena, defendantName } = props const [subpoena, setSubpoena] = useState() From 2ceb3113fe997d29594199332a18f1f07392f4cb Mon Sep 17 00:00:00 2001 From: unakb Date: Fri, 4 Oct 2024 15:23:52 +0000 Subject: [PATCH 56/61] fix(j-s): xrd fixes --- .../xrd-api/src/app/app.service.ts | 69 ++++++++++++------- .../xrd-api/src/app/dto/subpoena.dto.ts | 10 +-- 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/apps/judicial-system/xrd-api/src/app/app.service.ts b/apps/judicial-system/xrd-api/src/app/app.service.ts index c2e33651586e..9a2d64f0da2a 100644 --- a/apps/judicial-system/xrd-api/src/app/app.service.ts +++ b/apps/judicial-system/xrd-api/src/app/app.service.ts @@ -16,7 +16,7 @@ import { AuditTrailService, } from '@island.is/judicial-system/audit-trail' import { LawyersService } from '@island.is/judicial-system/lawyers' -import { DefenderChoice } from '@island.is/judicial-system/types' +import { DefenderChoice, ServiceStatus } from '@island.is/judicial-system/types' import { UpdateSubpoenaDto } from './dto/subpoena.dto' import { SubpoenaResponse } from './models/subpoena.response' @@ -96,46 +96,65 @@ export class AppService { subpoenaId: string, updateSubpoena: UpdateSubpoenaDto, ): Promise { - //TODO: Remove this mix - const { - deliveredOnPaper, - prosecutedConfirmedSubpoenaThroughIslandis, - delivered, - deliveredToLawyer, - deliveryInvalidated, - servedBy, - ...update - } = updateSubpoena - - let updatesToSend = { registeredBy: servedBy, ...update } + let defenderInfo = { + defenderName: '', + defenderEmail: '', + defenderPhoneNumber: '', + } if ( - update.defenderChoice === DefenderChoice.CHOOSE && - !update.defenderNationalId + updateSubpoena.defenderChoice === DefenderChoice.CHOOSE && + !updateSubpoena.defenderNationalId ) { throw new BadRequestException( 'Defender national id is required for choice', ) } - if (update.defenderNationalId) { + if (updateSubpoena.defenderNationalId) { try { const chosenLawyer = await this.lawyersService.getLawyer( - update.defenderNationalId, + updateSubpoena.defenderNationalId, ) - updatesToSend = { - ...updatesToSend, - ...{ - defenderName: chosenLawyer.Name, - defenderEmail: chosenLawyer.Email, - defenderPhoneNumber: chosenLawyer.Phone, - }, + defenderInfo = { + defenderName: chosenLawyer.Name, + defenderEmail: chosenLawyer.Email, + defenderPhoneNumber: chosenLawyer.Phone, } } catch (reason) { + // TODO: Reconsider throwing - what happens if registry is down? + this.logger.error( + `Failed to retrieve lawyer with national id ${updateSubpoena.defenderNationalId}`, + reason, + ) throw new BadRequestException('Lawyer not found') } } + //TODO: move logic to reusable place if this is the data structure we keep + const serviceStatus = updateSubpoena.deliveredToLawyer + ? ServiceStatus.DEFENDER + : updateSubpoena.prosecutedConfirmedSubpoenaThroughIslandis + ? ServiceStatus.ELECTRONICALLY + : updateSubpoena.deliveredOnPaper || updateSubpoena.delivered === true + ? ServiceStatus.IN_PERSON + : updateSubpoena.acknowledged === false + ? ServiceStatus.FAILED + : // TODO: handle expired + undefined + + const updateToSend = { + serviceStatus, + comment: updateSubpoena.comment, + servedBy: updateSubpoena.servedBy, + serviceDate: updateSubpoena.servedAt, + defenderChoice: updateSubpoena.defenderChoice, + defenderNationalId: updateSubpoena.defenderNationalId, + defenderName: defenderInfo.defenderName || undefined, + defenderEmail: defenderInfo.defenderEmail || undefined, + defenderPhoneNumber: defenderInfo.defenderPhoneNumber || undefined, + } + try { const res = await fetch( `${this.config.backend.url}/api/internal/subpoena/${subpoenaId}`, @@ -145,7 +164,7 @@ export class AppService { 'Content-Type': 'application/json', authorization: `Bearer ${this.config.backend.accessToken}`, }, - body: JSON.stringify(updatesToSend), + body: JSON.stringify(updateToSend), }, ) diff --git a/apps/judicial-system/xrd-api/src/app/dto/subpoena.dto.ts b/apps/judicial-system/xrd-api/src/app/dto/subpoena.dto.ts index c1a7a319c1a9..edb6ff7db4d6 100644 --- a/apps/judicial-system/xrd-api/src/app/dto/subpoena.dto.ts +++ b/apps/judicial-system/xrd-api/src/app/dto/subpoena.dto.ts @@ -20,6 +20,11 @@ export class UpdateSubpoenaDto { @ApiProperty({ type: String, required: false }) servedBy?: string + @IsOptional() + @IsString() + @ApiProperty({ type: String, required: false }) + servedAt?: string + @IsOptional() @IsEnum(DefenderChoice) @ApiProperty({ enum: DefenderChoice, required: false }) @@ -49,9 +54,4 @@ export class UpdateSubpoenaDto { @IsBoolean() @ApiProperty({ type: Boolean, required: false }) deliveredToLawyer?: boolean - - @IsOptional() - @IsBoolean() - @ApiProperty({ type: Boolean, required: false }) - deliveryInvalidated?: boolean } From 995e2bc5c4de9d389389e19f0c9459abdc2c55e2 Mon Sep 17 00:00:00 2001 From: unakb Date: Fri, 4 Oct 2024 17:03:47 +0000 Subject: [PATCH 57/61] Update app.service.ts --- .../xrd-api/src/app/app.service.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/apps/judicial-system/xrd-api/src/app/app.service.ts b/apps/judicial-system/xrd-api/src/app/app.service.ts index 9a2d64f0da2a..202e2dbaf644 100644 --- a/apps/judicial-system/xrd-api/src/app/app.service.ts +++ b/apps/judicial-system/xrd-api/src/app/app.service.ts @@ -96,10 +96,14 @@ export class AppService { subpoenaId: string, updateSubpoena: UpdateSubpoenaDto, ): Promise { - let defenderInfo = { - defenderName: '', - defenderEmail: '', - defenderPhoneNumber: '', + let defenderInfo: { + defenderName: string | undefined + defenderEmail: string | undefined + defenderPhoneNumber: string | undefined + } = { + defenderName: undefined, + defenderEmail: undefined, + defenderPhoneNumber: undefined, } if ( @@ -150,9 +154,9 @@ export class AppService { serviceDate: updateSubpoena.servedAt, defenderChoice: updateSubpoena.defenderChoice, defenderNationalId: updateSubpoena.defenderNationalId, - defenderName: defenderInfo.defenderName || undefined, - defenderEmail: defenderInfo.defenderEmail || undefined, - defenderPhoneNumber: defenderInfo.defenderPhoneNumber || undefined, + defenderName: defenderInfo.defenderName, + defenderEmail: defenderInfo.defenderEmail, + defenderPhoneNumber: defenderInfo.defenderPhoneNumber, } try { From 57ca30c63aeaf50fc11f7442310ed675de0e9039 Mon Sep 17 00:00:00 2001 From: unakb Date: Fri, 4 Oct 2024 18:09:15 +0000 Subject: [PATCH 58/61] Update updateSubpoena.dto.ts --- .../src/app/modules/subpoena/dto/updateSubpoena.dto.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts index 0dec9634cc62..1b93bdd29b27 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts @@ -16,9 +16,9 @@ export class UpdateSubpoenaDto { readonly servedBy?: string @IsOptional() - @IsDate() - @ApiPropertyOptional({ type: Date }) - readonly serviceDate?: Date + @IsString() + @ApiPropertyOptional({ type: String }) + readonly serviceDate?: string @IsOptional() @IsString() From a11885ca0a325cc18468e97766d29d5e2a6b9346 Mon Sep 17 00:00:00 2001 From: unakb Date: Sun, 6 Oct 2024 21:02:52 +0000 Subject: [PATCH 59/61] fix(j-s): module import --- .../backend/src/app/modules/police/police.module.ts | 3 ++- apps/judicial-system/backend/src/app/modules/subpoena/index.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.module.ts b/apps/judicial-system/backend/src/app/modules/police/police.module.ts index 1df72c2b8794..c9978deaccb3 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.module.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.module.ts @@ -1,6 +1,7 @@ import { forwardRef, Module } from '@nestjs/common' -import { AwsS3Module, CaseModule, EventModule, SubpoenaModule } from '../index' +import { AwsS3Module, CaseModule, EventModule } from '../index' +import { SubpoenaModule } from '../subpoena' import { PoliceController } from './police.controller' import { PoliceService } from './police.service' diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/index.ts b/apps/judicial-system/backend/src/app/modules/subpoena/index.ts index 828b617f4f30..89a9cbfddfa4 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/index.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/index.ts @@ -1,2 +1,3 @@ export { SubpoenaService } from './subpoena.service' export { Subpoena } from './models/subpoena.model' +export { SubpoenaModule } from './subpoena.module' From 684c512bee39682112163a39bc574c48aa62f814 Mon Sep 17 00:00:00 2001 From: unakb Date: Sun, 6 Oct 2024 22:06:25 +0000 Subject: [PATCH 60/61] Revert "fix(j-s): module import" This reverts commit a11885ca0a325cc18468e97766d29d5e2a6b9346. --- .../backend/src/app/modules/police/police.module.ts | 3 +-- apps/judicial-system/backend/src/app/modules/subpoena/index.ts | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/police.module.ts b/apps/judicial-system/backend/src/app/modules/police/police.module.ts index c9978deaccb3..1df72c2b8794 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.module.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.module.ts @@ -1,7 +1,6 @@ import { forwardRef, Module } from '@nestjs/common' -import { AwsS3Module, CaseModule, EventModule } from '../index' -import { SubpoenaModule } from '../subpoena' +import { AwsS3Module, CaseModule, EventModule, SubpoenaModule } from '../index' import { PoliceController } from './police.controller' import { PoliceService } from './police.service' diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/index.ts b/apps/judicial-system/backend/src/app/modules/subpoena/index.ts index 89a9cbfddfa4..828b617f4f30 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/index.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/index.ts @@ -1,3 +1,2 @@ export { SubpoenaService } from './subpoena.service' export { Subpoena } from './models/subpoena.model' -export { SubpoenaModule } from './subpoena.module' From 915d5cdb2e59b3b23a25024bdbca4cb6fa8cd20b Mon Sep 17 00:00:00 2001 From: unakb Date: Mon, 7 Oct 2024 07:49:35 +0000 Subject: [PATCH 61/61] Update createTestingPoliceModule.ts --- .../src/app/modules/police/test/createTestingPoliceModule.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/judicial-system/backend/src/app/modules/police/test/createTestingPoliceModule.ts b/apps/judicial-system/backend/src/app/modules/police/test/createTestingPoliceModule.ts index fb00a32daf9b..c97b49711f23 100644 --- a/apps/judicial-system/backend/src/app/modules/police/test/createTestingPoliceModule.ts +++ b/apps/judicial-system/backend/src/app/modules/police/test/createTestingPoliceModule.ts @@ -12,6 +12,7 @@ import { AwsS3Service } from '../../aws-s3' import { CaseService } from '../../case' import { InternalCaseService } from '../../case/internalCase.service' import { EventService } from '../../event' +import { SubpoenaService } from '../../subpoena' import { policeModuleConfig } from '../police.config' import { PoliceController } from '../police.controller' import { PoliceService } from '../police.service' @@ -20,6 +21,7 @@ jest.mock('../../event/event.service') jest.mock('../../aws-s3/awsS3.service.ts') jest.mock('../../case/case.service.ts') jest.mock('../../case/internalCase.service.ts') +jest.mock('../../subpoena/subpoena.service.ts') export const createTestingPoliceModule = async () => { const policeModule = await Test.createTestingModule({ @@ -36,6 +38,7 @@ export const createTestingPoliceModule = async () => { CaseService, InternalCaseService, PoliceService, + SubpoenaService, { provide: LOGGER_PROVIDER, useValue: {