Skip to content

Commit

Permalink
feat(j-s): Defender Updated Notification (#16801)
Browse files Browse the repository at this point in the history
* Adss a defendant national id

* Removes unused imports

* Stops updating defendants via API in indictment cases on court case number assignment as these updates are sent to the robot

* Adds a guard to get a defender by national id

* Rewrites update defendant by national id

* Refactors defendant update messages

* Restricts defendant restricted updates to indictment cases

* Moves defendand defender choice updates and notifications to defendant classes

* Update unit tests and fixxes some issues

* Fixes email sending trigger and guard

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
gudjong and kodiakhq[bot] authored Nov 15, 2024
1 parent fda8787 commit 17ef240
Show file tree
Hide file tree
Showing 26 changed files with 599 additions and 317 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,10 @@ import {
} from '@island.is/judicial-system/formatters'
import type { User } from '@island.is/judicial-system/types'
import {
CaseAppealRulingDecision,
CaseDecision,
CaseState,
CaseTransition,
CaseType,
indictmentCases,
investigationCases,
isRestrictionCase,
restrictionCases,
UserRole,
} from '@island.is/judicial-system/types'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -784,10 +784,9 @@ export class CaseService {
elementId: caseFile.id,
})) ?? []

const messages = this.getDeliverProsecutorToCourtMessages(theCase, user)
.concat(this.getDeliverDefendantToCourtMessages(theCase, user))
.concat(deliverCaseFilesRecordToCourtMessages)
.concat(deliverCaseFileToCourtMessages)
const messages: Message[] = deliverCaseFilesRecordToCourtMessages.concat(
deliverCaseFileToCourtMessages,
)

if (isTrafficViolationCase(theCase)) {
messages.push({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ export class InternalCaseService {
collectEncryptionProperties(defendantEncryptionProperties, defendant)
defendantsArchive.push(defendantArchive)

await this.defendantService.updateForArcive(
await this.defendantService.updateDatabaseDefendant(
theCase.id,
defendant.id,
clearedDefendantProperties,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,11 +283,6 @@ describe('CaseController - Create court case', () => {

it('should post to queue', () => {
expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([
{
type: MessageType.DELIVERY_TO_COURT_PROSECUTOR,
user,
caseId: theCase.id,
},
{
type: MessageType.DELIVERY_TO_COURT_CASE_FILES_RECORD,
user,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,23 +458,6 @@ describe('CaseController - Update', () => {

it('should post to queue', () => {
expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([
{
type: MessageType.DELIVERY_TO_COURT_PROSECUTOR,
user,
caseId,
},
{
type: MessageType.DELIVERY_TO_COURT_DEFENDANT,
user,
caseId,
elementId: defendantId1,
},
{
type: MessageType.DELIVERY_TO_COURT_DEFENDANT,
user,
caseId,
elementId: defendantId2,
},
{
type: MessageType.DELIVERY_TO_COURT_CASE_FILES_RECORD,
user,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,13 +287,13 @@ describe('InternalCaseController - Archive', () => {
],
where: archiveFilter,
})
expect(mockDefendantService.updateForArcive).toHaveBeenCalledWith(
expect(mockDefendantService.updateDatabaseDefendant).toHaveBeenCalledWith(
caseId,
defendantId1,
{ nationalId: '', name: '', address: '' },
transaction,
)
expect(mockDefendantService.updateForArcive).toHaveBeenCalledWith(
expect(mockDefendantService.updateDatabaseDefendant).toHaveBeenCalledWith(
caseId,
defendantId2,
{ nationalId: '', name: '', address: '' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,62 +90,72 @@ export class DefendantService {
return defendants[0]
}

private async sendUpdateDefendantMessages(
private async sendRequestCaseUpdateDefendantMessages(
theCase: Case,
updatedDefendant: Defendant,
oldDefendant: Defendant,
user: User,
) {
): Promise<void> {
if (!theCase.courtCaseNumber) {
return
}

const messages: Message[] = []

// Handling of updates sent to the court system
if (theCase.courtCaseNumber) {
// A defendant is updated after the case has been received by the court.
if (updatedDefendant.noNationalId !== oldDefendant.noNationalId) {
// This should only happen to non-indictment cases.
// A defendant nationalId is added or removed. Attempt to add the defendant to the court case.
// In case there is no national id, the court will be notified.
await this.messageService.sendMessagesToQueue([
this.getMessageForDeliverDefendantToCourt(updatedDefendant, user),
])
} else if (updatedDefendant.nationalId !== oldDefendant.nationalId) {
// This should only happen to non-indictment cases.
// A defendant is replaced. Attempt to add the defendant to the court case,
// but also ask the court to verify defendants.
await this.messageService.sendMessagesToQueue([
this.getMessageForSendDefendantsNotUpdatedAtCourtNotification(
theCase,
user,
),
this.getMessageForDeliverDefendantToCourt(updatedDefendant, user),
])
} else if (
updatedDefendant.defenderEmail !== oldDefendant.defenderEmail
) {
// This should only happen to indictment cases.
// A defendant's defender email is updated.
// Attempt to update the defendant in the court case.
await this.messageService.sendMessagesToQueue([
this.getMessageForDeliverDefendantToCourt(updatedDefendant, user),
])
}
// A defendant is updated after the case has been received by the court.
if (updatedDefendant.noNationalId !== oldDefendant.noNationalId) {
// A defendant nationalId is added or removed. Attempt to add the defendant to the court case.
// In case there is no national id, the court will be notified.
messages.push(
this.getMessageForDeliverDefendantToCourt(updatedDefendant, user),
)
} else if (updatedDefendant.nationalId !== oldDefendant.nationalId) {
// A defendant is replaced. Attempt to add the defendant to the court case,
// but also ask the court to verify defendants.
messages.push(
this.getMessageForSendDefendantsNotUpdatedAtCourtNotification(
theCase,
user,
),
this.getMessageForDeliverDefendantToCourt(updatedDefendant, user),
)
}

// Handling of messages sent to participants for indictment cases
if (isIndictmentCase(theCase.type)) {
// Defender was just confirmed by judge
if (messages.length === 0) {
return
}

return this.messageService.sendMessagesToQueue(messages)
}

private async sendIndictmentCaseUpdateDefendantMessages(
theCase: Case,
updatedDefendant: Defendant,
oldDefendant: Defendant,
): Promise<void> {
if (!theCase.courtCaseNumber) {
return
}

if (updatedDefendant.isDefenderChoiceConfirmed) {
if (
updatedDefendant.isDefenderChoiceConfirmed &&
!oldDefendant.isDefenderChoiceConfirmed &&
(updatedDefendant.defenderChoice === DefenderChoice.CHOOSE ||
updatedDefendant.defenderChoice === DefenderChoice.DELEGATE)
updatedDefendant.defenderChoice === DefenderChoice.CHOOSE ||
updatedDefendant.defenderChoice === DefenderChoice.DELEGATE
) {
await this.messageService.sendMessagesToQueue([
{
type: MessageType.DEFENDANT_NOTIFICATION,
caseId: theCase.id,
body: { type: DefendantNotificationType.DEFENDER_ASSIGNED },
elementId: updatedDefendant.id,
},
])
// TODO: Update defender with robot if needed

// Defender was just confirmed by judge
if (!oldDefendant.isDefenderChoiceConfirmed) {
await this.messageService.sendMessagesToQueue([
{
type: MessageType.DEFENDANT_NOTIFICATION,
caseId: theCase.id,
body: { type: DefendantNotificationType.DEFENDER_ASSIGNED },
elementId: updatedDefendant.id,
},
])
}
}
}
}
Expand Down Expand Up @@ -183,19 +193,15 @@ export class DefendantService {
return defendant
}

async updateForArcive(
async updateDatabaseDefendant(
caseId: string,
defendantId: string,
update: UpdateDefendantDto,
transaction: Transaction,
): Promise<Defendant> {
transaction?: Transaction,
) {
const [numberOfAffectedRows, defendants] = await this.defendantModel.update(
update,
{
where: { id: defendantId, caseId },
returning: true,
transaction,
},
{ where: { id: defendantId, caseId }, returning: true, transaction },
)

return this.getUpdatedDefendant(
Expand All @@ -206,69 +212,99 @@ export class DefendantService {
)
}

async update(
async updateRequestCase(
theCase: Case,
defendant: Defendant,
update: UpdateDefendantDto,
user: User,
): Promise<Defendant> {
const [numberOfAffectedRows, defendants] = await this.defendantModel.update(
const updatedDefendant = await this.updateDatabaseDefendant(
theCase.id,
defendant.id,
update,
{
where: { id: defendant.id, caseId: theCase.id },
returning: true,
},
)

const updatedDefendant = this.getUpdatedDefendant(
numberOfAffectedRows,
defendants,
defendant.id,
await this.sendRequestCaseUpdateDefendantMessages(
theCase,
updatedDefendant,
defendant,
user,
)

return updatedDefendant
}

async updateIndictmentCase(
theCase: Case,
defendant: Defendant,
update: UpdateDefendantDto,
): Promise<Defendant> {
const updatedDefendant = await this.updateDatabaseDefendant(
theCase.id,
defendant.id,
update,
)

await this.sendUpdateDefendantMessages(
await this.sendIndictmentCaseUpdateDefendantMessages(
theCase,
updatedDefendant,
defendant,
user,
)

return updatedDefendant
}

async updateByNationalId(
caseId: string,
defendantNationalId: string,
async update(
theCase: Case,
defendant: Defendant,
update: UpdateDefendantDto,
user: User,
): Promise<Defendant> {
if (isIndictmentCase(theCase.type)) {
return this.updateIndictmentCase(theCase, defendant, update)
} else {
return this.updateRequestCase(theCase, defendant, update, user)
}
}

async updateRestricted(
theCase: Case,
defendant: Defendant,
update: InternalUpdateDefendantDto,
isDefenderChoiceConfirmed = false,
transaction?: Transaction,
): Promise<Defendant> {
// The reason we have a separate dto for this is because requests that end here
// are initiated by outside API's which should not be able to edit other fields
// Defendant updated originating from the judicial system should use the UpdateDefendantDto
// and go through the update method above using the defendantId.
// This is also why we set the isDefenderChoiceConfirmed to false here - the judge needs to confirm all changes.
update = {
...update,
isDefenderChoiceConfirmed: false,
} as UpdateDefendantDto
// This is also why we may set the isDefenderChoiceConfirmed to false here - the judge needs to confirm all changes.

const [numberOfAffectedRows, defendants] = await this.defendantModel.update(
update,
{
where: {
caseId,
national_id: normalizeAndFormatNationalId(defendantNationalId),
},
returning: true,
},
const updatedDefendant = await this.updateDatabaseDefendant(
theCase.id,
defendant.id,
{ ...update, isDefenderChoiceConfirmed },
transaction,
)

const updatedDefendant = this.getUpdatedDefendant(
numberOfAffectedRows,
defendants,
defendants[0].id,
caseId,
)
// Notify the court if the defendant has changed the defender choice
if (
!updatedDefendant.isDefenderChoiceConfirmed &&
updatedDefendant.defenderChoice === DefenderChoice.CHOOSE &&
(updatedDefendant.defenderChoice !== defendant.defenderChoice ||
updatedDefendant.defenderNationalId !== defendant.defenderNationalId)
) {
await this.messageService.sendMessagesToQueue([
{
type: MessageType.DEFENDANT_NOTIFICATION,
caseId: theCase.id,
elementId: updatedDefendant.id,
body: {
type: DefendantNotificationType.DEFENDANT_SELECTED_DEFENDER,
},
},
])
}

return updatedDefendant
}
Expand Down
Loading

0 comments on commit 17ef240

Please sign in to comment.