Skip to content

Commit

Permalink
fix(j-s): Verdict Appeal Deadline (#15786)
Browse files Browse the repository at this point in the history
* Fixes displayed appeal deadline when defendant was present during sentencing

* Fixxes transformer

* Renames parameter

* Updates unit tests
  • Loading branch information
gudjong authored Aug 29, 2024
1 parent c4eba52 commit 77e96d5
Show file tree
Hide file tree
Showing 21 changed files with 213 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,10 @@ export const getIndictmentInfo = (
).toISOString()

if (defendants) {
const verdictViewDates = defendants?.map(
(defendant) => defendant.verdictViewDate,
const verdictViewDates = defendants?.map((defendant) =>
defendant.verdictViewDate
? new Date(defendant.verdictViewDate)
: undefined,
)

const verdictAppealDeadline =
Expand Down
29 changes: 23 additions & 6 deletions apps/judicial-system/backend/src/app/formatters/subpoenaPdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ import {
formatDOB,
lowercase,
} from '@island.is/judicial-system/formatters'
import {
DateType,
DistrictCourtLocation,
DistrictCourts,
SubpoenaType,
} from '@island.is/judicial-system/types'
import { DateType, SubpoenaType } from '@island.is/judicial-system/types'

import { subpoena as strings } from '../messages'
import { Case } from '../modules/case'
Expand All @@ -27,6 +22,28 @@ import {
setTitle,
} from './pdfHelpers'

type DistrictCourts =
| 'Héraðsdómur Reykjavíkur'
| 'Héraðsdómur Reykjaness'
| 'Héraðsdómur Vesturlands'
| 'Héraðsdómur Vestfjarða'
| 'Héraðsdómur Norðurlands vestra'
| 'Héraðsdómur Norðurlands eystra'
| 'Héraðsdómur Austurlands'
| 'Héraðsdómur Suðurlands'

// TODO: Move to databas
const DistrictCourtLocation: Record<DistrictCourts, string> = {
'Héraðsdómur Reykjavíkur': 'Dómhúsið við Lækjartorg, Reykjavík',
'Héraðsdómur Reykjaness': 'Fjarðargata 9, Hafnarfirði',
'Héraðsdómur Vesturlands': 'Bjarnarbraut 8, Borgarnesi',
'Héraðsdómur Vestfjarða': 'Hafnarstræti 9, Ísafirði',
'Héraðsdómur Norðurlands vestra': 'Skagfirðingabraut 21, Sauðárkróki',
'Héraðsdómur Norðurlands eystra': 'Hafnarstræti 107, 4. hæð, Akureyri',
'Héraðsdómur Austurlands': 'Lyngás 15, Egilsstöðum',
'Héraðsdómur Suðurlands': 'Austurvegur 4, Selfossi',
}

export const createSubpoena = (
theCase: Case,
defendant: Defendant,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ const canPrisonSystemUserAccessCase = (
// Check case type access
if (user.institution?.type === InstitutionType.PRISON_ADMIN) {
if (isIndictmentCase(theCase.type)) {
const verdictViewDates = theCase.defendants?.map((defendant) =>
defendant.verdictViewDate?.toISOString(),
const verdictViewDates = theCase.defendants?.map(
(defendant) => defendant.verdictViewDate,
)

const indictmentVerdictAppealDeadline =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
} from '@island.is/judicial-system/auth'
import {
indictmentCases,
ServiceRequirement,
SubpoenaType,
type User,
} from '@island.is/judicial-system/types'
Expand Down Expand Up @@ -108,6 +109,17 @@ export class DefendantController {
): Promise<Defendant> {
this.logger.debug(`Updating defendant ${defendantId} of case ${caseId}`)

// If the defendant was present at the court hearing,
// then set the verdict view date to the case ruling date
if (
defendantToUpdate.serviceRequirement === ServiceRequirement.NOT_APPLICABLE
) {
defendantToUpdate = {
...defendantToUpdate,
verdictViewDate: theCase.rulingDate,
}
}

return this.defendantService.update(
theCase,
defendant,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,61 +13,106 @@ describe('DefendantInfo', () => {
jest.useRealTimers()
})

test('should return the correct string if viewDate is not provided', () => {
const viewDate = undefined
const serviceRequirement = ServiceRequirement.NOT_REQUIRED
test('should return the correct string if serviceRequirement is REQUIRED and verdictAppealDeadline is not provided', () => {
const verdictAppealDeadline = undefined
const serviceRequirement = ServiceRequirement.REQUIRED

const dataSections = getAppealExpirationInfo(viewDate, serviceRequirement)
const dataSections = getAppealExpirationInfo(
verdictAppealDeadline,
serviceRequirement,
)

expect(dataSections.message.id).toStrictEqual(
'judicial.system.core:info_card.defendant_info.appeal_date_not_begun',
)
})

test('should return the correct string if serviceRequirement is NOT_APPLICABLE and verdictAppealDeadline is not provided', () => {
const verdictAppealDeadline = undefined
const serviceRequirement = ServiceRequirement.NOT_APPLICABLE

const dataSections = getAppealExpirationInfo(
verdictAppealDeadline,
serviceRequirement,
)

expect(dataSections.message.id).toStrictEqual(
'judicial.system.core:info_card.defendant_info.appeal_date_not_begun',
)
})

test('should return the correct string if serviceRequirement is NOT_REQUIRED', () => {
const viewDate = '2024-07-08'
const verdictAppealDeadline = undefined
const serviceRequirement = ServiceRequirement.NOT_REQUIRED

const dataSections = getAppealExpirationInfo(viewDate, serviceRequirement)
const dataSections = getAppealExpirationInfo(
verdictAppealDeadline,
serviceRequirement,
)

expect(dataSections.message.id).toStrictEqual(
'judicial.system.core:info_card.defendant_info.service_requirement_not_required',
'judicial.system.core:info_card.defendant_info.service_requirement_not_required_v1',
)
})

test('should return the correct string if serviceRequirement is NOT_APPLICABLE', () => {
const viewDate = '2024-07-08'
const serviceRequirement = ServiceRequirement.NOT_APPLICABLE
test('should return the correct string if serviceRequirement is REQUIRED and appeal expiration date is in the future', () => {
const verdictAppealDeadline = '2024-08-05'
const serviceRequirement = ServiceRequirement.REQUIRED

const dataSections = getAppealExpirationInfo(viewDate, serviceRequirement)
const dataSections = getAppealExpirationInfo(
verdictAppealDeadline,
serviceRequirement,
)

expect(dataSections.message.id).toStrictEqual(
'judicial.system.core:info_card.defendant_info.service_requirement_not_applicable',
'judicial.system.core:info_card.defendant_info.appeal_expiration_date',
)
expect(dataSections.date).toStrictEqual('05.08.2024')
})

test('should return the correct string if appeal expiration date is in the future', () => {
const viewDate = '2024-07-08'
const serviceRequirement = ServiceRequirement.REQUIRED
test('should return the correct string if serviceRequirement is NOT_APPLICABLE and appeal expiration date is in the future', () => {
const verdictAppealDeadline = '2024-08-05'
const serviceRequirement = ServiceRequirement.NOT_APPLICABLE

const dataSections = getAppealExpirationInfo(viewDate, serviceRequirement)
const dataSections = getAppealExpirationInfo(
verdictAppealDeadline,
serviceRequirement,
)

expect(dataSections.message.id).toStrictEqual(
'judicial.system.core:info_card.defendant_info.appeal_expiration_date',
)
expect(dataSections.data).toStrictEqual('05.08.2024')
expect(dataSections.date).toStrictEqual('05.08.2024')
})

test('should return the correct string if appeal expiration date is in the past', () => {
const viewDate = '2024-06-09'
test('should return the correct string if serviceRequirement is REQUIRED and appeal expiration date is in the past', () => {
const verdictAppealDeadline = '2024-07-07'
const serviceRequirement = ServiceRequirement.REQUIRED

const dataSections = getAppealExpirationInfo(viewDate, serviceRequirement)
const dataSections = getAppealExpirationInfo(
verdictAppealDeadline,
serviceRequirement,
)

expect(dataSections.message.id).toStrictEqual(
'judicial.system.core:info_card.defendant_info.appeal_date_expired',
)
expect(dataSections.date).toStrictEqual('07.07.2024')
})

test('should return the correct string if serviceRequirement is NOT_APPLICABLE and appeal expiration date is in the past', () => {
const verdictAppealDeadline = '2024-07-07'
const serviceRequirement = ServiceRequirement.NOT_APPLICABLE

const dataSections = getAppealExpirationInfo(
verdictAppealDeadline,
serviceRequirement,
)

expect(dataSections.message.id).toStrictEqual(
'judicial.system.core:info_card.defendant_info.appeal_date_expired',
)
expect(dataSections.data).toStrictEqual('07.07.2024')
expect(dataSections.date).toStrictEqual('07.07.2024')
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@ export const strings = defineMessages({
'Notaður til að láta vita að áfrýjunarfrestur dómfellda er ekki hafinn.',
},
serviceRequirementNotRequired: {
id: 'judicial.system.core:info_card.defendant_info.service_requirement_not_required',
defaultMessage: 'Dómfelldi var viðstaddur dómþing',
description: 'Notað til að láta vita birting dóms er ekki nauðsynleg.',
},
serviceRequirementNotApplicable: {
id: 'judicial.system.core:info_card.defendant_info.service_requirement_not_applicable',
id: 'judicial.system.core:info_card.defendant_info.service_requirement_not_required_v1',
defaultMessage: 'Birting dóms ekki þörf',
description: 'Notað til að láta vita birting dóms er ekki nauðsynleg.',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,31 +37,27 @@ interface DefendantInfoProps {
}

export const getAppealExpirationInfo = (
viewDate?: string | null,
verdictAppealDeadline?: string | null,
serviceRequirement?: ServiceRequirement | null,
) => {
if (!viewDate) {
return { message: strings.appealDateNotBegun, data: null }
}

if (serviceRequirement === ServiceRequirement.NOT_REQUIRED) {
return { message: strings.serviceRequirementNotRequired, data: null }
}

if (serviceRequirement === ServiceRequirement.NOT_APPLICABLE) {
return { message: strings.serviceRequirementNotApplicable, data: null }
if (!verdictAppealDeadline) {
return { message: strings.appealDateNotBegun, date: null }
}

// TODO: Move to the server as today may not be accurate in the client
const today = new Date()
const expiryDate = new Date(viewDate)
expiryDate.setDate(expiryDate.getDate() + 28)
const expiryDate = new Date(verdictAppealDeadline)

const message =
today < expiryDate
? strings.appealExpirationDate
: strings.appealDateExpired

return { message, data: formatDate(expiryDate) }
return { message, date: formatDate(expiryDate) }
}

export const DefendantInfo: FC<DefendantInfoProps> = (props) => {
Expand All @@ -75,7 +71,7 @@ export const DefendantInfo: FC<DefendantInfoProps> = (props) => {
const { formatMessage } = useIntl()

const appealExpirationInfo = getAppealExpirationInfo(
defendant.verdictViewDate,
defendant.verdictAppealDeadline,
defendant.serviceRequirement,
)

Expand Down Expand Up @@ -123,7 +119,7 @@ export const DefendantInfo: FC<DefendantInfoProps> = (props) => {
<Box>
<Text as="span">
{formatMessage(appealExpirationInfo.message, {
appealExpirationDate: appealExpirationInfo.data,
appealExpirationDate: appealExpirationInfo.date,
})}
</Text>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const Completed: FC = () => {

const handleNextButtonClick = useCallback(async () => {
const allSucceeded = await handleUpload(
uploadFiles.filter((file) => !file.key),
uploadFiles.filter((file) => file.percent === 0),
updateUploadFile,
)
if (!allSucceeded) {
Expand Down Expand Up @@ -93,8 +93,11 @@ const Completed: FC = () => {
)

const handleCriminalRecordUpdateUpload = useCallback(
(files: File[], category: CaseFileCategory) => {
addUploadFiles(files, { category, status: 'done' })
(files: File[]) => {
addUploadFiles(files, {
category: CaseFileCategory.CRIMINAL_RECORD_UPDATE,
status: 'done',
})
},
[addUploadFiles],
)
Expand Down Expand Up @@ -165,13 +168,8 @@ const Completed: FC = () => {
description={formatMessage(core.uploadBoxDescription, {
fileEndings: '.pdf',
})}
onChange={(files) =>
handleCriminalRecordUpdateUpload(
files,
CaseFileCategory.CRIMINAL_RECORD_UPDATE,
)
}
onRemove={(file) => handleRemoveFile(file)}
onChange={handleCriminalRecordUpdateUpload}
onRemove={handleRemoveFile}
/>
</Box>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const ReturnIndictmentModal: FC<Props> = ({
return
}

// TODO: Generally, we cannot trust the client date so we should let the server handle this instead.
// There is already precedent for adding eplanatory strings on the server side when transitioning cases.
const now = new Date()
const prependedReturnedExplanation = `${formatMessage(
strings.prependedReturnedExplanation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { isDefendantInfoActionButtonDisabled } from './Overview'

describe('Overview', () => {
describe('isDefendantInfoActionButtonDisabled', () => {
test('should return true if defendant vervidictViewDate is not null', () => {
test('should return true if defendant service requirement is REQUIRED and defendant vervidictViewDate is not null', () => {
const verdictViewDate = '2024-07-08'
const serviceRequirement = ServiceRequirement.REQUIRED

Expand All @@ -17,8 +17,21 @@ describe('Overview', () => {
expect(res).toEqual(true)
})

test('should return true if defendant service requirement is NOT_APPLICABLE', () => {
test('should return true if defendant service requirement is REQUIRED and defendant vervidictViewDate is null', () => {
const verdictViewDate = null
const serviceRequirement = ServiceRequirement.REQUIRED

const res = isDefendantInfoActionButtonDisabled({
id: 'id',
verdictViewDate,
serviceRequirement,
})

expect(res).toEqual(false)
})

test('should return true if defendant service requirement is NOT_APPLICABLE and defendant vervidictViewDate is not null', () => {
const verdictViewDate = '2024-07-09'
const serviceRequirement = ServiceRequirement.NOT_APPLICABLE

const res = isDefendantInfoActionButtonDisabled({
Expand All @@ -30,6 +43,19 @@ describe('Overview', () => {
expect(res).toEqual(true)
})

test('should return true if defendant service requirement is NOT_APPLICABLE and defendant vervidictViewDate is null', () => {
const verdictViewDate = null
const serviceRequirement = ServiceRequirement.NOT_APPLICABLE

const res = isDefendantInfoActionButtonDisabled({
id: 'id',
verdictViewDate,
serviceRequirement,
})

expect(res).toEqual(false)
})

test('should return true if defendant service requirement is NOT_REQUIRED', () => {
const verdictViewDate = null
const serviceRequirement = ServiceRequirement.NOT_REQUIRED
Expand Down
Loading

0 comments on commit 77e96d5

Please sign in to comment.