Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(j-s): Additional info for law and order cases and subpoenas #16132

Merged
merged 9 commits into from
Sep 24, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +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 { User, UserService } from '../user'
import { InternalCreateCaseDto } from './dto/internalCreateCase.dto'
import { archiveFilter } from './filters/case.archiveFilter'
Expand Down Expand Up @@ -1204,7 +1205,10 @@ export class InternalCaseService {
async getIndictmentCases(nationalId: string): Promise<Case[]> {
return this.caseModel.findAll({
include: [
{ model: Defendant, as: 'defendants' },
{
model: Defendant,
as: 'defendants',
},
{
model: DateLog,
as: 'dateLogs',
Expand All @@ -1228,11 +1232,25 @@ export class InternalCaseService {
async getIndictmentCase(caseId: string, nationalId: string): Promise<Case> {
const caseById = await this.caseModel.findOne({
include: [
{ model: Defendant, as: 'defendants' },
{
model: Defendant,
as: 'defendants',
include: [
{
model: Subpoena,
as: 'subpoenas',
order: [['created', 'DESC']],
},
],
},
{ model: Institution, as: 'court' },
{ model: Institution, as: 'prosecutorsOffice' },
{ model: User, as: 'judge' },
{ model: User, as: 'prosecutor' },
{
model: User,
as: 'prosecutor',
include: [{ model: Institution, as: 'institution' }],
},
{ model: DateLog, as: 'dateLogs' },
],
attributes: ['courtCaseNumber', 'id'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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'
Expand All @@ -18,7 +19,7 @@ import { InternalDefendantController } from './internalDefendant.controller'
MessageModule,
forwardRef(() => CourtModule),
forwardRef(() => CaseModule),
SequelizeModule.forFeature([Defendant, CivilClaimant]),
SequelizeModule.forFeature([Defendant, CivilClaimant, Subpoena]),
],
controllers: [
DefendantController,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CreatedAt,
DataType,
ForeignKey,
HasMany,
Model,
Table,
UpdatedAt,
Expand All @@ -20,6 +21,7 @@ import {
} from '@island.is/judicial-system/types'

import { Case } from '../../case/models/case.model'
import { Subpoena } from '../../subpoena/models/subpoena.model'

@Table({
tableName: 'defendant',
Expand Down Expand Up @@ -131,4 +133,8 @@ export class Defendant extends Model {
})
@ApiPropertyOptional({ enum: SubpoenaType })
subpoenaType?: SubpoenaType

@HasMany(() => Subpoena, { foreignKey: 'defendantId' })
@ApiPropertyOptional({ type: () => Subpoena, isArray: true })
subpoenas?: Subpoena[]
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
XRoadMemberClass,
} from '@island.is/shared/utils/server'

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'

Expand Down Expand Up @@ -520,6 +521,9 @@ export class PoliceService {
const { nationalId: defendantNationalId } = defendant
const { name: actor } = user

const normalizedNationalId =
normalizeAndFormatNationalId(defendantNationalId)[0]

const documentName = `Fyrirkall í máli ${workingCase.courtCaseNumber}`
const arraignmentInfo = dateLogs?.find(
(dateLog) => dateLog.dateType === 'ARRAIGNMENT_DATE',
Expand All @@ -541,7 +545,7 @@ export class PoliceService {
documentBase64: subpoena,
courtRegistrationDate: arraignmentInfo?.date,
prosecutorSsn: prosecutor?.nationalId,
prosecutedSsn: defendantNationalId,
prosecutedSsn: normalizedNationalId,
courtAddress: court?.address,
courtRoomNumber: arraignmentInfo?.location || '',
courtCeremony: 'Þingfesting',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class IndictmentCaseData {
caseNumber!: string

@ApiProperty({ type: Boolean })
acknowledged?: boolean
hasBeenServed?: boolean

@ApiProperty({ type: [Groups] })
groups!: Groups[]
Expand All @@ -26,21 +26,22 @@ export class CaseResponse {
data!: IndictmentCaseData

static fromInternalCaseResponse(
res: InternalCaseResponse,
internalCase: InternalCaseResponse,
lang?: string,
): CaseResponse {
const t = getTranslations(lang)
const defendant = res.defendants[0]
const subpoenaDateLog = res.dateLogs?.find(
const defendant = internalCase.defendants[0] ?? {}
const subpoenaDateLog = internalCase.dateLogs?.find(
(dateLog) => dateLog.dateType === DateType.ARRAIGNMENT_DATE,
)
const subpoenaCreatedDate = subpoenaDateLog?.created.toString() ?? ''
const subpoenaCreatedDate = subpoenaDateLog?.created?.toString() ?? '' //TODO: Change to created from subpoena db entry?
const subpoenas = defendant.subpoenas ?? []

return {
caseId: res.id,
caseId: internalCase.id,
data: {
caseNumber: `${t.caseNumber} ${res.courtCaseNumber}`,
acknowledged: false, // TODO: Connect to real data
caseNumber: `${t.caseNumber} ${internalCase.courtCaseNumber}`,
hasBeenServed: subpoenas.length > 0 ? subpoenas[0].acknowledged : false,
groups: [
{
label: t.defendant,
Expand Down Expand Up @@ -75,23 +76,23 @@ export class CaseResponse {
},
{
label: t.courtCaseNumber,
value: res.courtCaseNumber,
value: internalCase.courtCaseNumber,
},
{
label: t.court,
value: res.court.name,
value: internalCase.court.name,
},
{
label: t.judge,
value: res.judge.name,
value: internalCase.judge.name,
},
{
label: t.institution,
value: res.prosecutorsOffice.name,
value: internalCase.prosecutorsOffice.name,
},
{
label: t.prosecutor,
value: res.prosecutor.name,
value: internalCase.prosecutor.name,
},
],
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,35 @@
import { IsEnum } from 'class-validator'

import { ApiProperty } from '@nestjs/swagger'

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

import { InternalCasesResponse } from './internal/internalCases.response'
import { getTranslations } from './utils/translations.strings'

enum TagVariant {
BLUE = 'blue',
DARKER_BLUE = 'darkerBlue',
PURPLE = 'purple',
WHITE = 'white',
RED = 'red',
ROSE = 'rose',
BLUEBERRY = 'blueberry',
DARK = 'dark',
MINT = 'mint',
YELLOW = 'yellow',
DISABLED = 'disabled',
WARN = 'warn',
}

class StateTag {
@IsEnum(TagVariant)
@ApiProperty({ enum: TagVariant })
color!: TagVariant

@ApiProperty({ type: String })
label!: string
}
export class CasesResponse {
@ApiProperty({ type: String })
id!: string
Expand All @@ -15,11 +40,8 @@ export class CasesResponse {
@ApiProperty({ type: String })
type!: string

@ApiProperty({ type: Object })
state!: {
color: string
label: string
}
@ApiProperty({ type: StateTag })
state!: StateTag

static fromInternalCasesResponse(
response: InternalCasesResponse[],
Expand All @@ -31,7 +53,9 @@ export class CasesResponse {
return {
id: item.id,
state: {
color: isCompletedCase(item.state) ? 'purple' : 'blue',
color: isCompletedCase(item.state)
? TagVariant.PURPLE
: TagVariant.BLUE,
label: isCompletedCase(item.state) ? t.completed : t.active,
},
caseNumber: `${t.caseNumber} ${item.courtCaseNumber}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ interface Defendant {
defenderEmail?: string
defenderPhoneNumber?: string
defenderChoice?: DefenderChoice
subpoenas?: Subpoena[]
}

interface DateLog {
Expand All @@ -37,3 +38,9 @@ interface DateLog {
date: Date
location?: string
}

interface Subpoena {
id: string
subpoenaId: string
acknowledged: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,53 @@ import { InternalCaseResponse } from './internal/internalCase.response'
import { Groups } from './shared/groups.model'
import { getTranslations } from './utils/translations.strings'

enum AlertMessageType {
ERROR = 'error',
INFO = 'info',
SUCCESS = 'success',
WARNING = 'warning',
DEFAULT = 'default',
}

class DefenderInfo {
@IsEnum(DefenderChoice)
@ApiProperty({ enum: DefenderChoice })
defenderChoice!: DefenderChoice

@ApiProperty({ type: () => String })
defenderName?: string

@ApiProperty({ type: () => Boolean })
canEdit?: boolean

@ApiProperty({ type: () => String })
courtContactInfo?: string
}

class AlertMessage {
@IsEnum(AlertMessageType)
@ApiProperty({ enum: AlertMessageType })
type?: AlertMessageType

@ApiProperty({ type: () => String })
message?: string
}

class SubpoenaData {
@ApiProperty({ type: () => String })
title!: string

@ApiProperty({ type: Boolean })
acknowledged?: boolean
@ApiProperty({ type: String })
subtitle?: string

@ApiProperty({ type: () => [Groups] })
groups!: Groups[]

@ApiProperty({ type: () => [AlertMessage] })
alerts?: AlertMessage[]

@ApiProperty({ type: Boolean })
hasBeenServed?: boolean
}

export class SubpoenaResponse {
Expand Down Expand Up @@ -59,31 +88,52 @@ export class SubpoenaResponse {

const waivedRight = defendantInfo?.defenderChoice === DefenderChoice.WAIVE
const hasDefender = defendantInfo?.defenderName !== undefined
const subpoena = defendantInfo?.subpoenas ?? []
const hasBeenServed = subpoena[0]?.acknowledged ?? false
const canChangeDefenseChoice = !waivedRight && !hasDefender

const subpoenaDateLog = internalCase.dateLogs?.find(
(dateLog) => dateLog.dateType === DateType.ARRAIGNMENT_DATE,
)
const arraignmentDate = subpoenaDateLog?.date ?? ''
const subpoenaCreatedDate = subpoenaDateLog?.created ?? '' //TODO: Change to subpoena created in RLS
const arraignmentLocation = subpoenaDateLog?.location
? `${internalCase.court.name}, Dómsalur ${subpoenaDateLog.location}`
: internalCase.court.name
const courtNameAndAddress = `${internalCase.court.name}, ${internalCase.court.address}`

return {
caseId: internalCase.id,
data: {
title: t.subpoena,
acknowledged: false, // TODO: Connect to real data
subtitle: courtNameAndAddress,
hasBeenServed: hasBeenServed,
alerts: [
...(hasBeenServed
? [
{
type: AlertMessageType.SUCCESS,
message: t.subpoenaServed,
},
]
: []),
],
groups: [
{
label: `${t.caseNumber} ${internalCase.courtCaseNumber}`,
items: [
[t.date, formatDate(subpoenaCreatedDate, 'PP')],
[t.institution, 'Lögreglustjórinn á höfuðborgarsvæðinu'],
[
t.institution,
internalCase.prosecutor?.institution?.name ?? t.notAvailable,
],
[t.prosecutor, internalCase.prosecutor?.name],
[t.accused, defendantInfo?.name],
[
t.arraignmentDate,
formatDate(arraignmentDate, "d.M.yyyy 'kl.' HH:mm"),
],
[t.location, subpoenaDateLog?.location ?? ''],
[t.location, arraignmentLocation],
[t.courtCeremony, t.parliamentaryConfirmation],
].map((item) => ({
label: item[0] ?? '',
Expand All @@ -100,6 +150,10 @@ export class SubpoenaResponse {
!waivedRight && hasDefender
? defendantInfo?.defenderName
: undefined,
canEdit: canChangeDefenseChoice,
courtContactInfo: canChangeDefenseChoice
? undefined
: t.courtContactInfo,
}
: undefined,
}
Expand Down
Loading
Loading