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): Judge confirms defender and civil claimant defense choices #16480

Merged
merged 22 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a5ac1dc
feat(j-s): Allow judge to confirm defender and civil claimant choices
unakb Oct 18, 2024
1063161
Merge branch 'main' into j-s/judge-confirms-defense-choices
unakb Oct 18, 2024
8860967
Update SelectDefender.tsx
unakb Oct 18, 2024
c087faf
cleanup
unakb Oct 18, 2024
dddef74
fix(j-s): Cleanup
unakb Oct 18, 2024
451e98d
chore: nx format:write update dirty files
andes-it Oct 18, 2024
f490663
feat(j-s): Add case files shared with defender front end handling
unakb Oct 21, 2024
061cc7d
Merge branch 'main' into j-s/judge-confirms-defense-choices
unakb Oct 21, 2024
2905443
fix(j-s): Control access with new flags
unakb Oct 21, 2024
33ec610
fix(j-s): Feedback
unakb Oct 21, 2024
71b638c
Update SelectDefender.tsx
unakb Oct 21, 2024
feba632
fix(j-s): Tests
unakb Oct 21, 2024
acdf637
Update createDefendant.dto.ts
unakb Oct 21, 2024
3a91e85
Update limitedAccessViewCaseFileGuard.spec.ts
unakb Oct 21, 2024
aaac754
Merge branch 'main' into j-s/judge-confirms-defense-choices
unakb Oct 22, 2024
9e7d46f
fix(j-s): Internal update defendant dto separate from other update
unakb Oct 22, 2024
532e130
fix(j-s): Feedback
unakb Oct 22, 2024
16bb3e2
Update subpoena.service.ts
unakb Oct 23, 2024
74bddce
fix(j-s): Case list filter for defenders
unakb Oct 23, 2024
62f374c
Update cases.filter.spec.ts
unakb Oct 23, 2024
fc41139
Merge branch 'main' into j-s/judge-confirms-defense-choices
kodiakhq[bot] Oct 23, 2024
73f03b3
Merge branch 'main' into j-s/judge-confirms-defense-choices
kodiakhq[bot] Oct 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,9 @@ export class UpdateCivilClaimantInput {
@IsOptional()
@Field(() => Boolean, { nullable: true })
readonly caseFilesSharedWithSpokesperson?: boolean

@Allow()
@IsOptional()
@Field(() => Boolean, { nullable: true })
readonly isSpokespersonConfirmed?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,14 @@ export class UpdateDefendantInput {
@IsOptional()
@Field(() => SubpoenaType, { nullable: true })
readonly subpoenaType?: SubpoenaType

@Allow()
@IsOptional()
@Field(() => Boolean, { nullable: true })
readonly isDefenderChoiceConfirmed?: boolean

@Allow()
@IsOptional()
@Field(() => Boolean, { nullable: true })
readonly caseFilesSharedWithDefender?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,7 @@ export class CivilClaimant {

@Field(() => Boolean, { nullable: true })
readonly caseFilesSharedWithSpokesperson?: boolean

@Field(() => Boolean, { nullable: true })
readonly isSpokespersonConfirmed?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,10 @@ export class Defendant {

@Field(() => [Subpoena], { nullable: true })
readonly subpoenas?: Subpoena[]

@Field(() => Boolean, { nullable: true })
readonly isDefenderChoiceConfirmed?: boolean

@Field(() => Boolean, { nullable: true })
readonly caseFilesSharedWithDefender?: boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction((t) =>
Promise.all([
queryInterface.addColumn(
'defendant',
'is_defender_choice_confirmed',
{
type: Sequelize.BOOLEAN,
allowNull: true,
},
{ transaction: t },
),
queryInterface.addColumn(
'defendant',
'case_files_shared_with_defender',
{
type: Sequelize.BOOLEAN,
allowNull: true,
},
{ transaction: t },
),
]),
)
},
down: (queryInterface) => {
return queryInterface.sequelize.transaction((t) =>
Promise.all([
queryInterface.removeColumn(
'defendant',
'is_defender_choice_confirmed',
{
transaction: t,
},
),
queryInterface.removeColumn(
'defendant',
'case_files_shared_with_defender',
{
transaction: t,
},
),
]),
)
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction((t) =>
queryInterface.addColumn(
'civil_claimant',
'is_spokesperson_confirmed',
{
type: Sequelize.BOOLEAN,
allowNull: true,
},
{ transaction: t },
),
)
},
down: (queryInterface) => {
return queryInterface.sequelize.transaction((t) =>
queryInterface.removeColumn(
'civil_claimant',
'is_spokesperson_confirmed',
{
transaction: t,
},
),
)
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -386,13 +386,18 @@ const canDefenceUserAccessIndictmentCase = (
}

// Check case defender assignment
if (Defendant.isDefenderOfDefendant(user.nationalId, theCase.defendants)) {
if (
Defendant.isConfirmedDefenderOfDefendant(
user.nationalId,
theCase.defendants,
)
) {
return true
}

// Check case spokesperson assignment
if (
CivilClaimant.isSpokespersonOfCivilClaimant(
CivilClaimant.isConfirmedSpokespersonOfCivilClaimant(
user.nationalId,
theCase.civilClaimants,
) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,17 @@ export const defenderGeneratedPdfRule: RolesRule = {
}

// Allow if the user is a defender of a defendant of the case
if (Defendant.isDefenderOfDefendant(user.nationalId, theCase.defendants)) {
if (
Defendant.isConfirmedDefenderOfDefendantWithCaseFileAccess(
user.nationalId,
theCase.defendants,
)
) {
return true
}

if (
CivilClaimant.isSpokespersonOfCivilClaimantWithCaseFileAccess(
CivilClaimant.isConfirmedSpokespersonOfCivilClaimantWithCaseFileAccess(
user.nationalId,
theCase.civilClaimants,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type { User } from '@island.is/judicial-system/types'
import {
CaseState,
CaseType,
isDistrictCourtUser,
NotificationType,
} from '@island.is/judicial-system/types'

Expand Down Expand Up @@ -200,7 +201,16 @@ export class DefendantService {
caseId: string,
defendantNationalId: string,
update: UpdateDefendantDto,
user?: User,
): Promise<Defendant> {
const isDefenderChoiceConfirmed = Boolean(
user &&
isDistrictCourtUser(user) &&
update.isDefenderChoiceConfirmed === true,
)

update = { ...update, isDefenderChoiceConfirmed: isDefenderChoiceConfirmed }

unakb marked this conversation as resolved.
Show resolved Hide resolved
const [numberOfAffectedRows, defendants] = await this.defendantModel.update(
update,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,14 @@ export class CreateDefendantDto {
@IsEnum(DefenderChoice)
@ApiPropertyOptional({ enum: DefenderChoice })
readonly defenderChoice?: DefenderChoice

@IsOptional()
@IsBoolean()
@ApiPropertyOptional({ type: Boolean })
readonly isDefenderChoiceConfirmed?: boolean

@IsOptional()
@IsBoolean()
@ApiPropertyOptional({ type: Boolean })
readonly caseFilesSharedWithDefender?: boolean
}
unakb marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,9 @@ export class UpdateCivilClaimantDto {
@IsBoolean()
@ApiPropertyOptional({ type: Boolean })
readonly caseFilesSharedWithSpokesperson?: boolean

@IsOptional()
@IsBoolean()
@ApiPropertyOptional({ type: Boolean })
readonly isSpokespersonConfirmed?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,14 @@ export class UpdateDefendantDto {
@IsString()
@ApiPropertyOptional({ type: String })
readonly requestedDefenderName?: string

@IsOptional()
@IsBoolean()
@ApiPropertyOptional({ type: Boolean })
readonly isDefenderChoiceConfirmed?: boolean

@IsOptional()
@IsBoolean()
@ApiPropertyOptional({ type: Boolean })
readonly caseFilesSharedWithDefender?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,30 @@ import { Case } from '../../case/models/case.model'
timestamps: false,
})
export class CivilClaimant extends Model {
static isSpokespersonOfCivilClaimant(
static isConfirmedSpokespersonOfCivilClaimant(
spokespersonNationalId: string,
civilClaimants?: CivilClaimant[],
) {
return civilClaimants?.some(
(civilClaimant) =>
civilClaimant.hasSpokesperson &&
civilClaimant.spokespersonNationalId &&
civilClaimant.isSpokespersonConfirmed &&
normalizeAndFormatNationalId(spokespersonNationalId).includes(
civilClaimant.spokespersonNationalId,
),
)
}

static isSpokespersonOfCivilClaimantWithCaseFileAccess(
static isConfirmedSpokespersonOfCivilClaimantWithCaseFileAccess(
spokespersonNationalId: string,
civilClaimants?: CivilClaimant[],
) {
return civilClaimants?.some(
(civilClaimant) =>
civilClaimant.hasSpokesperson &&
civilClaimant.spokespersonNationalId &&
civilClaimant.isSpokespersonConfirmed &&
normalizeAndFormatNationalId(spokespersonNationalId).includes(
civilClaimant.spokespersonNationalId,
) &&
Expand Down Expand Up @@ -141,4 +143,11 @@ export class CivilClaimant extends Model {
})
@ApiPropertyOptional({ type: Boolean })
caseFilesSharedWithSpokesperson?: boolean

@Column({
type: DataType.BOOLEAN,
allowNull: true,
})
@ApiPropertyOptional({ type: Boolean })
isSpokespersonConfirmed?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { Subpoena } from '../../subpoena/models/subpoena.model'
timestamps: true,
})
export class Defendant extends Model {
static isDefenderOfDefendant(
static isConfirmedDefenderOfDefendant(
defenderNationalId: string,
defendants?: Defendant[],
) {
Expand All @@ -38,7 +38,23 @@ export class Defendant extends Model {
defendant.defenderNationalId &&
normalizeAndFormatNationalId(defenderNationalId).includes(
defendant.defenderNationalId,
),
) &&
defendant.isDefenderChoiceConfirmed,
)
}

static isConfirmedDefenderOfDefendantWithCaseFileAccess(
defenderNationalId: string,
defendants?: Defendant[],
) {
return defendants?.some(
(defendant) =>
defendant.defenderNationalId &&
normalizeAndFormatNationalId(defenderNationalId).includes(
defendant.defenderNationalId,
) &&
defendant.isDefenderChoiceConfirmed &&
defendant.caseFilesSharedWithDefender,
)
}

Expand Down Expand Up @@ -167,4 +183,12 @@ export class Defendant extends Model {
@Column({ type: DataType.STRING, allowNull: true })
@ApiPropertyOptional({ type: String })
requestedDefenderName?: string

@Column({ type: DataType.BOOLEAN, allowNull: true })
@ApiPropertyOptional({ type: Boolean })
isDefenderChoiceConfirmed?: boolean

@Column({ type: DataType.BOOLEAN, allowNull: true })
@ApiPropertyOptional({ type: Boolean })
caseFilesSharedWithDefender?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,19 @@ const canDefenceUserViewCaseFileOfIndictmentCase = (
defendants?: Defendant[],
civilClaimants?: CivilClaimant[],
) => {
if (Defendant.isDefenderOfDefendant(nationalId, defendants)) {
if (
Defendant.isConfirmedDefenderOfDefendantWithCaseFileAccess(
nationalId,
defendants,
)
) {
return defenderCaseFileCategoriesForIndictmentCases.includes(
caseFileCategory,
)
}

if (
CivilClaimant.isSpokespersonOfCivilClaimantWithCaseFileAccess(
CivilClaimant.isConfirmedSpokespersonOfCivilClaimantWithCaseFileAccess(
nationalId,
civilClaimants,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IsEnum, IsOptional, IsString } from 'class-validator'
import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator'

import { ApiPropertyOptional } from '@nestjs/swagger'

Expand Down Expand Up @@ -64,4 +64,14 @@ export class UpdateSubpoenaDto {
@IsString()
@ApiPropertyOptional({ type: String })
readonly requestedDefenderName?: string

@IsOptional()
@IsBoolean()
@ApiPropertyOptional({ type: Boolean })
readonly isDefenderChoiceConfirmed?: boolean

@IsOptional()
@IsBoolean()
@ApiPropertyOptional({ type: Boolean })
readonly caseFilesSharedWithDefender?: boolean
}
unakb marked this conversation as resolved.
Show resolved Hide resolved
Loading
Loading