Skip to content

Commit

Permalink
Merge branch 'main' into feature/web-grants-apply-cards
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] authored Dec 20, 2024
2 parents eb57c0f + e146770 commit 8a77673
Show file tree
Hide file tree
Showing 88 changed files with 1,993 additions and 607 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
CourtSessionType,
IndictmentCaseReviewDecision,
IndictmentDecision,
PunishmentType,
} from '@island.is/judicial-system/types'

import { Defendant } from '../../defendant'
Expand Down Expand Up @@ -142,4 +143,10 @@ export class CaseListEntry {

@Field(() => String, { nullable: true })
readonly indictmentCompletedDate?: string

// TEMP: Use with caution! This key will never be populated.
// It was added to bypass table component type checks for a required custom sort key
// until we have a resolution on how to handle multiple defendants in the case list
@Field(() => PunishmentType, { nullable: true })
readonly defendantsPunishmentType?: PunishmentType
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ export class Defendant {
@Field(() => String, { nullable: true })
readonly sentToPrisonAdminDate?: string

@Field(() => String, { nullable: true })
readonly openedByPrisonAdminDate?: string

@Field(() => PunishmentType, { nullable: true })
readonly punishmentType?: PunishmentType
}
46 changes: 35 additions & 11 deletions apps/judicial-system/backend/src/app/modules/case/case.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,16 @@ export const caseListInclude: Includeable[] = [
as: 'defendants',
required: false,
order: [['created', 'ASC']],
include: [
{
model: DefendantEventLog,
as: 'eventLogs',
required: false,
where: { eventType: defendantEventTypes },
order: [['created', 'DESC']],
separate: true,
},
],
separate: true,
},
{
Expand Down Expand Up @@ -1302,18 +1312,29 @@ export class CaseService {
(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 ?? '',
],
}))
.map((updatedDefendant) => [
{
type: MessageType.DELIVERY_TO_POLICE_SUBPOENA,
user,
caseId: theCase.id,
elementId: [
updatedDefendant.id,
updatedDefendant.subpoenas?.[0].id ?? '',
],
},
{
type: MessageType.DELIVERY_TO_COURT_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.flat())
}
}

Expand Down Expand Up @@ -1416,7 +1437,10 @@ export class CaseService {
await this.addMessagesForCourtCaseConnectionToQueue(updatedCase, user)
}
} else {
if (updatedCase.prosecutorId !== theCase.prosecutorId) {
if (
!isIndictment &&
updatedCase.prosecutorId !== theCase.prosecutorId
) {
// New prosecutor
await this.addMessagesForProsecutorChangeToQueue(updatedCase, user)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
NestInterceptor,
} from '@nestjs/common'

import { DefendantEventType } from '@island.is/judicial-system/types'

import { Defendant, DefendantEventLog } from '../../defendant'
import { Case } from '../models/case.model'
import { CaseString } from '../models/caseString.model'
Expand All @@ -15,8 +17,15 @@ export const transformDefendants = (defendants?: Defendant[]) => {
return defendants?.map((defendant) => ({
...defendant.toJSON(),
sentToPrisonAdminDate: defendant.isSentToPrisonAdmin
? DefendantEventLog.sentToPrisonAdminDate(defendant.eventLogs)?.created
? DefendantEventLog.getDefendantEventLogTypeDate({
defendantEventLogs: defendant.eventLogs,
eventType: DefendantEventType.SENT_TO_PRISON_ADMIN,
})
: undefined,
openedByPrisonAdminDate: DefendantEventLog.getDefendantEventLogTypeDate({
defendantEventLogs: defendant.eventLogs,
eventType: DefendantEventType.OPENED_BY_PRISON_ADMIN,
}),
}))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common'

import {
DefendantEventType,
isIndictmentCase,
isPrisonAdminUser,
User,
} from '@island.is/judicial-system/types'

import { DefendantEventLog, DefendantService } from '../../defendant'
import { Case } from '../models/case.model'

const hasValidOpenByPrisonAdminEvent = (
defendantEventLogs: DefendantEventLog[],
) => {
const sentToPrisonAdminDate = DefendantEventLog.getDefendantEventLogTypeDate({
defendantEventLogs,
eventType: DefendantEventType.SENT_TO_PRISON_ADMIN,
})
const openedByPrisonAdminDate =
DefendantEventLog.getDefendantEventLogTypeDate({
defendantEventLogs,
eventType: DefendantEventType.OPENED_BY_PRISON_ADMIN,
})
return (
sentToPrisonAdminDate &&
openedByPrisonAdminDate &&
sentToPrisonAdminDate <= openedByPrisonAdminDate
)
}

@Injectable()
export class DefendantIndictmentAccessedInterceptor implements NestInterceptor {
constructor(private readonly defendantService: DefendantService) {}

intercept(context: ExecutionContext, next: CallHandler) {
const request = context.switchToHttp().getRequest()
const user: User = request.user
const theCase: Case = request.case

if (isIndictmentCase(theCase.type) && isPrisonAdminUser(user)) {
const defendantsIndictmentNotOpened = theCase.defendants?.filter(
({ isSentToPrisonAdmin, eventLogs = [] }) =>
isSentToPrisonAdmin && !hasValidOpenByPrisonAdminEvent(eventLogs),
)

// create new events for all defendants that prison admin has not accessed according to defendant event logs
defendantsIndictmentNotOpened?.forEach((defendant) =>
this.defendantService.createDefendantEvent({
caseId: theCase.id,
defendantId: defendant.id,
eventType: DefendantEventType.OPENED_BY_PRISON_ADMIN,
}),
)
}
return next.handle()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ export class InternalCaseController {
)
}

@UseGuards(CaseExistsGuard)
@UseGuards(
CaseExistsGuard,
new CaseTypeGuard([...restrictionCases, ...investigationCases]),
)
@Post(
`case/:caseId/${messageEndpoint[MessageType.DELIVERY_TO_COURT_PROSECUTOR]}`,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ import type { User as TUser } from '@island.is/judicial-system/types'
import {
CaseState,
CaseType,
DefendantEventType,
indictmentCases,
investigationCases,
restrictionCases,
UserRole,
} from '@island.is/judicial-system/types'

import { nowFactory } from '../../factories'
import { defenderRule, prisonSystemStaffRule } from '../../guards'
import { DefendantService } from '../defendant'
import { EventService } from '../event'
import { User } from '../user'
import { TransitionCaseDto } from './dto/transitionCase.dto'
Expand All @@ -57,6 +60,7 @@ import {
} from './guards/rolesRules'
import { CaseInterceptor } from './interceptors/case.interceptor'
import { CompletedAppealAccessedInterceptor } from './interceptors/completedAppealAccessed.interceptor'
import { DefendantIndictmentAccessedInterceptor } from './interceptors/defendantIndictmentAccessed.interceptor'
import { LimitedAccessCaseFileInterceptor } from './interceptors/limitedAccessCaseFile.interceptor'
import { Case } from './models/case.model'
import { transitionCase } from './state/case.state'
Expand All @@ -73,6 +77,7 @@ export class LimitedAccessCaseController {
private readonly limitedAccessCaseService: LimitedAccessCaseService,
private readonly eventService: EventService,
private readonly pdfService: PdfService,
private readonly defendantService: DefendantService,
@Inject(LOGGER_PROVIDER) private readonly logger: Logger,
) {}

Expand All @@ -84,6 +89,7 @@ export class LimitedAccessCaseController {
)
@RolesRules(prisonSystemStaffRule, defenderRule)
@UseInterceptors(
DefendantIndictmentAccessedInterceptor,
CompletedAppealAccessedInterceptor,
LimitedAccessCaseFileInterceptor,
CaseInterceptor,
Expand All @@ -100,7 +106,7 @@ export class LimitedAccessCaseController {
): Promise<Case> {
this.logger.debug(`Getting limitedAccess case ${caseId} by id`)

if (!theCase.openedByDefender) {
if (user.role === UserRole.DEFENDER && !theCase.openedByDefender) {
const updated = await this.limitedAccessCaseService.update(
theCase,
{ openedByDefender: nowFactory() },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -897,12 +897,24 @@ describe('CaseController - Update', () => {
caseId: theCase.id,
elementId: [defendantId1, subpoenaId1],
},
{
type: MessageType.DELIVERY_TO_COURT_SUBPOENA,
user,
caseId: theCase.id,
elementId: [defendantId1, subpoenaId1],
},
{
type: MessageType.DELIVERY_TO_POLICE_SUBPOENA,
user,
caseId: theCase.id,
elementId: [defendantId2, subpoenaId2],
},
{
type: MessageType.DELIVERY_TO_COURT_SUBPOENA,
user,
caseId: theCase.id,
elementId: [defendantId2, subpoenaId2],
},
])
})
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { CanActivate } from '@nestjs/common'
import {
investigationCases,
restrictionCases,
} from '@island.is/judicial-system/types'

import { CaseExistsGuard } from '../../guards/caseExists.guard'
import { CaseTypeGuard } from '../../guards/caseType.guard'
import { InternalCaseController } from '../../internalCase.controller'

describe('InternalCaseController - Deliver prosecutor to court guards', () => {
Expand All @@ -14,19 +18,12 @@ describe('InternalCaseController - Deliver prosecutor to court guards', () => {
)
})

it('should have one guards', () => {
expect(guards).toHaveLength(1)
})

describe('CaseExistsGuard', () => {
let guard: CanActivate

beforeEach(() => {
guard = new guards[0]()
})

it('should have CaseExistsGuard as guard 1', () => {
expect(guard).toBeInstanceOf(CaseExistsGuard)
it('should have the right guard configuration', () => {
expect(guards).toHaveLength(2)
expect(new guards[0]()).toBeInstanceOf(CaseExistsGuard)
expect(guards[1]).toBeInstanceOf(CaseTypeGuard)
expect(guards[1]).toEqual({
allowedCaseTypes: [...restrictionCases, ...investigationCases],
})
})
})
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { uuid } from 'uuidv4'

import type { User } from '@island.is/judicial-system/types'
import { type User, UserRole } from '@island.is/judicial-system/types'

import { createTestingCaseModule } from '../createTestingCaseModule'

Expand All @@ -14,14 +14,18 @@ interface Then {
error: Error
}

type GivenWhenThen = (caseId: string, theCase: Case) => Promise<Then>
type GivenWhenThen = (
caseId: string,
theCase: Case,
user?: User,
) => Promise<Then>

describe('LimitedAccessCaseController - Get by id', () => {
let givenWhenThen: GivenWhenThen
const openedBeforeDate = randomDate()
const openedNowDate = randomDate()
const caseId = uuid()
const user = { id: uuid() } as User
const defaultUser = { id: uuid() } as User

let mockCaseModel: typeof Case

Expand All @@ -42,7 +46,11 @@ describe('LimitedAccessCaseController - Get by id', () => {
const mockFindOne = mockCaseModel.findOne as jest.Mock
mockFindOne.mockResolvedValue(updatedCase)

givenWhenThen = async (caseId: string, theCase: Case) => {
givenWhenThen = async (
caseId: string,
theCase: Case,
user = defaultUser,
) => {
const then = {} as Then

try {
Expand Down Expand Up @@ -79,11 +87,11 @@ describe('LimitedAccessCaseController - Get by id', () => {

describe('case exists and has not been opened by defender before', () => {
const theCase = { id: caseId } as Case

const user = { ...defaultUser, role: UserRole.DEFENDER } as User
let then: Then

beforeEach(async () => {
then = await givenWhenThen(caseId, theCase)
then = await givenWhenThen(caseId, theCase, user)
})

it('should update openedByDefender and return case', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export enum CourtDocumentFolder {
CASE_DOCUMENTS = 'Gögn málsins',
COURT_DOCUMENTS = 'Dómar, úrskurðir og Þingbók',
APPEAL_DOCUMENTS = 'Kæra til Landsréttar',
SUBPOENA_DOCUMENTS = 'Boðanir',
}

export type Subtype = Exclude<CaseType, CaseType.INDICTMENT> | IndictmentSubtype
Expand Down Expand Up @@ -342,6 +343,7 @@ export class CourtService {

return await this.courtClientService.createCase(courtId, {
caseType: isIndictment ? 'S - Ákærumál' : 'R - Rannsóknarmál',
// TODO: send a list of subtypes when CourtService supports it
subtype: courtSubtype as string,
status: 'Skráð',
receivalDate: formatISO(receivalDate, { representation: 'date' }),
Expand Down
Loading

0 comments on commit 8a77673

Please sign in to comment.