From 4959514cbc0d7617e6e30a1c03a67cb708bc1825 Mon Sep 17 00:00:00 2001
From: kksteini <77672665+kksteini@users.noreply.github.com>
Date: Wed, 29 May 2024 13:58:26 +0000
Subject: [PATCH 01/82] chore(application-marriage-conditions): Update payments
(#14951)
* chore(application-marriage-conditions): Update payments
* Accidental let changed to const
* Update libs/application/templates/marriage-conditions/src/fields/PaymentInfo/index.tsx
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update libs/application/templates/marriage-conditions/src/lib/utils.ts
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* chore: nx format:write update dirty files
* Update libs/application/templates/marriage-conditions/src/fields/PaymentInfo/index.tsx
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: andes-it
---
.../{jest.config.js => jest.config.ts} | 3 +-
.../marriage-conditions/project.json | 10 +++
.../src/dataProviders/index.ts | 14 +++-
.../src/fields/PaymentInfo/index.tsx | 83 +++++++++++++++++--
.../src/forms/application.ts | 26 ++++--
.../forms/sharedSections/dataCollection.ts | 6 ++
.../src/lib/MarriageConditionsTemplate.ts | 8 +-
.../marriage-conditions/src/lib/utils.spec.ts | 19 +++++
.../marriage-conditions/src/lib/utils.ts | 3 +
9 files changed, 159 insertions(+), 13 deletions(-)
rename libs/application/templates/marriage-conditions/{jest.config.js => jest.config.ts} (91%)
create mode 100644 libs/application/templates/marriage-conditions/src/lib/utils.spec.ts
diff --git a/libs/application/templates/marriage-conditions/jest.config.js b/libs/application/templates/marriage-conditions/jest.config.ts
similarity index 91%
rename from libs/application/templates/marriage-conditions/jest.config.js
rename to libs/application/templates/marriage-conditions/jest.config.ts
index f171115544a5..9cf5dcb16255 100644
--- a/libs/application/templates/marriage-conditions/jest.config.js
+++ b/libs/application/templates/marriage-conditions/jest.config.ts
@@ -1,4 +1,5 @@
-module.exports = {
+/* eslint-disable */
+export default {
preset: './jest.preset.js',
rootDir: '../../../..',
roots: [__dirname],
diff --git a/libs/application/templates/marriage-conditions/project.json b/libs/application/templates/marriage-conditions/project.json
index c43264913c55..9f54467e8a3a 100644
--- a/libs/application/templates/marriage-conditions/project.json
+++ b/libs/application/templates/marriage-conditions/project.json
@@ -23,6 +23,16 @@
"options": {
"command": "graphql-codegen --config libs/application/templates/marriage-conditions/codegen.yml"
}
+ },
+ "test": {
+ "executor": "@nx/jest:jest",
+ "options": {
+ "jestConfig": "libs/application/templates/marriage-conditions/jest.config.ts",
+ "passWithNoTests": true
+ },
+ "outputs": [
+ "{workspaceRoot}/coverage/libs/application/templates/marriage-conditions"
+ ]
}
},
"tags": ["scope:application-system", "lib:application-system"]
diff --git a/libs/application/templates/marriage-conditions/src/dataProviders/index.ts b/libs/application/templates/marriage-conditions/src/dataProviders/index.ts
index 5727b79366b7..9b13cb2ddffc 100644
--- a/libs/application/templates/marriage-conditions/src/dataProviders/index.ts
+++ b/libs/application/templates/marriage-conditions/src/dataProviders/index.ts
@@ -1,4 +1,8 @@
-import { defineTemplateApi } from '@island.is/application/types'
+import {
+ InstitutionNationalIds,
+ PaymentCatalogApi,
+ defineTemplateApi,
+} from '@island.is/application/types'
export {
NationalRegistryUserApi,
@@ -14,3 +18,11 @@ export const ReligionCodesApi = defineTemplateApi({
action: 'religionCodes',
externalDataId: 'religions',
})
+
+export const DistrictCommissionersPaymentCatalogApi =
+ PaymentCatalogApi.configure({
+ params: {
+ organizationId: InstitutionNationalIds.SYSLUMENN,
+ },
+ externalDataId: 'paymentDistrictCommissioners',
+ })
diff --git a/libs/application/templates/marriage-conditions/src/fields/PaymentInfo/index.tsx b/libs/application/templates/marriage-conditions/src/fields/PaymentInfo/index.tsx
index a7b0b29bd0d5..be4a39707fac 100644
--- a/libs/application/templates/marriage-conditions/src/fields/PaymentInfo/index.tsx
+++ b/libs/application/templates/marriage-conditions/src/fields/PaymentInfo/index.tsx
@@ -3,6 +3,7 @@ import { Box, Divider, Text } from '@island.is/island-ui/core'
import { FieldBaseProps } from '@island.is/application/types'
import { useLocale } from '@island.is/localization'
import { m } from '../../lib/messages'
+import { formatIsk } from '../../lib/utils'
export type Individual = {
name: string
@@ -11,9 +12,75 @@ export type Individual = {
email: string
}
-export const PaymentInfo: FC> = () => {
+type Payment = {
+ chargeItemCode: string
+ priceAmount: number
+}
+
+type FakeData = {
+ allowFakeData: boolean
+ fakePayments: Array
+}
+
+type PaymentInfoProps = {
+ field: {
+ props: FakeData
+ }
+}
+
+export const PaymentInfo: FC<
+ React.PropsWithChildren & PaymentInfoProps
+> = ({ field, application }) => {
const { formatMessage } = useLocale()
+ // Get Payment Catalog
+ const paymentCatalog = application.externalData?.paymentDistrictCommissioners
+ ?.data as Array
+
+ // Get Charge Codes
+ let birthCertCode = paymentCatalog?.find(
+ (payment) => payment.chargeItemCode === 'AY153',
+ )
+ let maritalCertCode = paymentCatalog?.find(
+ (payment) => payment.chargeItemCode === 'AY154',
+ )
+
+ const surveyCertCode = paymentCatalog?.find(
+ (payment) => payment.chargeItemCode === 'AY128',
+ )
+
+ const marriageConditionsCode = paymentCatalog?.find(
+ (payment) => payment.chargeItemCode === 'AY129',
+ )
+
+ // Fallback on fake data if on dev
+ // TODO: remove once paymentcatalog is updated on dev to reflect prod
+ if (field.props.allowFakeData) {
+ birthCertCode = field?.props?.fakePayments?.find(
+ (payment) => payment.chargeItemCode === 'AY153',
+ )
+ maritalCertCode = field?.props?.fakePayments?.find(
+ (payment) => payment.chargeItemCode === 'AY154',
+ )
+ }
+
+ // Isolate prices, calculate total and check if total is correct
+ const prices = {
+ birthCertificate: 2 * (birthCertCode?.priceAmount ?? 0),
+ maritalCertificate: 2 * (maritalCertCode?.priceAmount ?? 0),
+ marriageConditions: marriageConditionsCode?.priceAmount ?? 0,
+ surveyCertificate: surveyCertCode?.priceAmount ?? 0,
+ }
+
+ // If correctTotal is false we only display final price
+ // To avoid embarrassing mistakes on production if prices
+ // descynchronize
+ const correctTotal =
+ prices.birthCertificate +
+ prices.maritalCertificate +
+ prices.surveyCertificate ===
+ prices.marriageConditions
+
return (
@@ -21,15 +88,21 @@ export const PaymentInfo: FC> = () => {
{formatMessage(m.maritalStatusCertificates)}
- 5.800 kr.
+
+ {correctTotal ? formatIsk(prices.maritalCertificate) : ''}
+
{formatMessage(m.birthCertificates)}
- 5.800 kr.
+
+ {correctTotal ? formatIsk(prices.birthCertificate) : ''}
+
{formatMessage(m.surveyCertificate)}
- 4.800 kr.
+
+ {correctTotal ? formatIsk(prices.surveyCertificate) : ''}
+
@@ -37,7 +110,7 @@ export const PaymentInfo: FC> = () => {
{formatMessage(m.total)}
- 16.400 kr.
+ {formatIsk(prices.marriageConditions)}
diff --git a/libs/application/templates/marriage-conditions/src/forms/application.ts b/libs/application/templates/marriage-conditions/src/forms/application.ts
index 7587055e08f6..bb0f7e08ff38 100644
--- a/libs/application/templates/marriage-conditions/src/forms/application.ts
+++ b/libs/application/templates/marriage-conditions/src/forms/application.ts
@@ -459,11 +459,27 @@ export const getApplication = ({ allowFakeData = false }): Form => {
id: 'payment',
title: '',
children: [
- buildCustomField({
- id: 'payment',
- title: '',
- component: 'PaymentInfo',
- }),
+ buildCustomField(
+ {
+ id: 'payment',
+ title: '',
+ component: 'PaymentInfo',
+ },
+ {
+ allowFakeData,
+ // TODO: When/if real data enters the payment catalog, remove this
+ fakePayments: [
+ {
+ priceAmount: 2800,
+ chargeItemCode: 'AY153',
+ },
+ {
+ priceAmount: 2700,
+ chargeItemCode: 'AY154',
+ },
+ ],
+ },
+ ),
buildSubmitField({
id: 'submitPayment',
title: '',
diff --git a/libs/application/templates/marriage-conditions/src/forms/sharedSections/dataCollection.ts b/libs/application/templates/marriage-conditions/src/forms/sharedSections/dataCollection.ts
index 1ab7b037a086..d04792950a94 100644
--- a/libs/application/templates/marriage-conditions/src/forms/sharedSections/dataCollection.ts
+++ b/libs/application/templates/marriage-conditions/src/forms/sharedSections/dataCollection.ts
@@ -5,6 +5,7 @@ import {
NationalRegistryUserApi,
UserProfileApi,
ReligionCodesApi,
+ DistrictCommissionersPaymentCatalogApi,
} from '../../dataProviders'
import { m } from '../../lib/messages'
@@ -33,4 +34,9 @@ export const dataCollection = [
title: '',
subTitle: '',
}),
+ buildDataProviderItem({
+ provider: DistrictCommissionersPaymentCatalogApi,
+ title: '',
+ subTitle: '',
+ }),
]
diff --git a/libs/application/templates/marriage-conditions/src/lib/MarriageConditionsTemplate.ts b/libs/application/templates/marriage-conditions/src/lib/MarriageConditionsTemplate.ts
index f5d899778ef3..a5939d749794 100644
--- a/libs/application/templates/marriage-conditions/src/lib/MarriageConditionsTemplate.ts
+++ b/libs/application/templates/marriage-conditions/src/lib/MarriageConditionsTemplate.ts
@@ -23,7 +23,11 @@ import {
getApplicationFeatureFlags,
MarriageCondtionsFeatureFlags,
} from './getApplicationFeatureFlags'
-import { MaritalStatusApi, ReligionCodesApi } from '../dataProviders'
+import {
+ DistrictCommissionersPaymentCatalogApi,
+ MaritalStatusApi,
+ ReligionCodesApi,
+} from '../dataProviders'
import { coreHistoryMessages } from '@island.is/application/core'
import { buildPaymentState } from '@island.is/application/utils'
@@ -82,6 +86,7 @@ const MarriageConditionsTemplate: ApplicationTemplate<
DistrictsApi,
MaritalStatusApi,
ReligionCodesApi,
+ DistrictCommissionersPaymentCatalogApi,
],
delete: true,
},
@@ -148,6 +153,7 @@ const MarriageConditionsTemplate: ApplicationTemplate<
DistrictsApi,
MaritalStatusApi,
ReligionCodesApi,
+ DistrictCommissionersPaymentCatalogApi,
],
},
],
diff --git a/libs/application/templates/marriage-conditions/src/lib/utils.spec.ts b/libs/application/templates/marriage-conditions/src/lib/utils.spec.ts
new file mode 100644
index 000000000000..b3600932e4e0
--- /dev/null
+++ b/libs/application/templates/marriage-conditions/src/lib/utils.spec.ts
@@ -0,0 +1,19 @@
+// Write tests for the utils functions
+import { formatIsk } from './utils'
+
+describe('utils', () => {
+ describe('formatIsk', () => {
+ it('should format a number to Icelandic currency', () => {
+ expect(formatIsk(1)).toBe('1 kr.')
+ expect(formatIsk(22)).toBe('22 kr.')
+ expect(formatIsk(333)).toBe('333 kr.')
+ expect(formatIsk(4_444)).toBe('4.444 kr.')
+ expect(formatIsk(55_555)).toBe('55.555 kr.')
+ expect(formatIsk(666_666)).toBe('666.666 kr.')
+ expect(formatIsk(7_777_777)).toBe('7.777.777 kr.')
+ expect(formatIsk(88_888_888)).toBe('88.888.888 kr.')
+ expect(formatIsk(999_999_999)).toBe('999.999.999 kr.')
+ expect(formatIsk(1_010_101_010)).toBe('1.010.101.010 kr.')
+ })
+ })
+})
diff --git a/libs/application/templates/marriage-conditions/src/lib/utils.ts b/libs/application/templates/marriage-conditions/src/lib/utils.ts
index ccad28919a41..cac1639c3e74 100644
--- a/libs/application/templates/marriage-conditions/src/lib/utils.ts
+++ b/libs/application/templates/marriage-conditions/src/lib/utils.ts
@@ -13,3 +13,6 @@ export const getSpouseNationalId = (answers: FormValue): string =>
export const removeCountryCode = (phone: string) => {
return phone.replace(/(^00354|^\+354|\D)/g, '')
}
+
+export const formatIsk = (value: number): string =>
+ `${value.toLocaleString('is-IS')} kr.`
From 6109b7ca453a757fad343a241a651a692151e657 Mon Sep 17 00:00:00 2001
From: kksteini <77672665+kksteini@users.noreply.github.com>
Date: Wed, 29 May 2024 14:11:24 +0000
Subject: [PATCH 02/82] feat(application-ir): Add casenumber to submit (#14919)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
---
.../modules/templates/inheritance-report/utils/mappers.ts | 6 +++++-
.../templates/inheritance-report/src/lib/dataSchema.ts | 2 ++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts b/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts
index 27a275611ee8..60a03ade6192 100644
--- a/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts
+++ b/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts
@@ -88,7 +88,9 @@ export const estateTransformer = (estate: EstateInfo): InheritanceData => {
export const expandAnswers = (
answers: InheritanceReportSchema,
-): InheritanceReportSchema => {
+): Omit & {
+ caseNumber: string
+} => {
return {
applicant: answers.applicant,
approveExternalData: answers.approveExternalData,
@@ -205,6 +207,7 @@ export const expandAnswers = (
total: answers.assets.vehicles?.total ?? 0,
},
},
+ caseNumber: answers.estateInfoSelection,
confirmAction: answers.confirmAction,
debts: {
debtsTotal: answers.debts.debtsTotal ?? 0,
@@ -223,6 +226,7 @@ export const expandAnswers = (
},
publicCharges: (answers.debts.publicCharges ?? 0).toString(),
},
+ estateInfoSelection: answers.estateInfoSelection,
funeralCost: {
build: answers?.funeralCost?.build ?? '',
cremation: answers?.funeralCost?.cremation ?? '',
diff --git a/libs/application/templates/inheritance-report/src/lib/dataSchema.ts b/libs/application/templates/inheritance-report/src/lib/dataSchema.ts
index 60128a998ceb..cf117d2a58ec 100644
--- a/libs/application/templates/inheritance-report/src/lib/dataSchema.ts
+++ b/libs/application/templates/inheritance-report/src/lib/dataSchema.ts
@@ -377,6 +377,8 @@ export const inheritanceReportSchema = z.object({
debtsTotal: z.number().optional(),
}),
+ estateInfoSelection: z.string().min(1),
+
funeralCost: z
.object({
build: z.string().optional(),
From 3e110b11116bdf826e4533352b11cfa67424e9fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?=
Date: Wed, 29 May 2024 15:07:49 +0000
Subject: [PATCH 03/82] feat(j-s): Store MD5 Sum for Confirmed Indictments
(#14842)
* Sets indictment denied explanation to null rather than the empty string when a case is submitted
* Moves INDICTMENT_CONFIRMED event log to the case service to guarantee that it succeeds if and only if the SUBMIT transition succeeds.
* Tries to upload generated case files record for submitted cases in the pdf service
* Rewrites case files record uploads to police and court
* Rewrites is traffic violation case checks
* Rewrites get indictment pdf
* Delivers traffic violation indictment to court
* Removes unnecessary db call to get event logs
* Rewrites getting generated files from s3
* Rewrites put object
* Rewrites aws s3 methods
* Confirmes indictments when getting signed urls
* Confirms indictments on delivery to court and court of appeals
* Confirms indictments on upload to police
* Fixes time to live handling
* Deletes confirmed indictments
* Archives confirmed indctments
* Removes comment
* Adds TODO
* Removes unused imports
* Stores md5 has for indictment files
* Removes console.log
* Fixes key handling when uploading to s3
* Clears indictment hash when indictment is returned from court
* Only gets confirmed indictment from s3 if indictment hash is null
* Updates unit tests
* Regenerates confirmed indictments on resend
* Rearranges code
---------
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
---
.../api/src/app/modules/auth/auth.service.ts | 2 +-
.../file/models/presignedPost.model.ts | 3 +
.../20240522092213-update-file-keys.js | 67 +++
.../app/formatters/confirmedIndictmentPdf.ts | 2 +-
.../src/app/formatters/formatters.spec.ts | 1 +
.../backend/src/app/formatters/index.ts | 3 +-
.../src/app/formatters/indictmentPdf.ts | 12 +-
.../backend/src/app/messages/courtUpload.ts | 5 +
.../src/app/modules/aws-s3/awsS3.service.ts | 404 +++++++++++++++--
.../src/app/modules/case/case.controller.ts | 15 +-
.../src/app/modules/case/case.service.ts | 95 +++-
.../app/modules/case/filters/cases.filter.ts | 1 -
.../interceptors/transition.interceptor.ts | 92 ----
.../modules/case/internalCase.controller.ts | 21 +
.../app/modules/case/internalCase.service.ts | 418 +++++++-----------
.../modules/case/limitedAccessCase.service.ts | 3 +-
.../src/app/modules/case/models/case.model.ts | 8 +
.../src/app/modules/case/pdf.service.ts | 162 ++++---
.../case/test/caseController/getAll.spec.ts | 2 +-
.../getCaseFilesRecordPdf.spec.ts | 48 +-
.../caseController/getCourtRecordPdf.spec.ts | 29 +-
...etCourtRecordSignatureConfirmation.spec.ts | 23 +-
.../caseController/getIndictmentPdf.spec.ts | 44 +-
.../test/caseController/getRulingPdf.spec.ts | 105 ++---
.../getRulingSignatureConfirmation.spec.ts | 23 +-
.../test/caseController/transition.spec.ts | 12 +-
.../case/test/caseController/update.spec.ts | 1 +
.../archiveCaseFilesRecord.spec.ts | 34 +-
.../deliverAppealToPolice.spec.ts | 18 +-
.../deliverCaseFilesRecordToCourt.spec.ts | 68 +--
...eliverCaseFilesRecordToCourtGuards.spec.ts | 34 +-
.../deliverCaseFilesRecordToPolice.spec.ts | 57 +--
.../deliverCaseToPolice.spec.ts | 8 +-
.../deliverIndictmentCaseToPolice.spec.ts | 18 +-
.../deliverIndictmentToCourt.spec.ts | 166 +++++++
.../deliverIndictmentToCourtGuards.spec.ts | 26 ++
.../deliverIndictmentToPolice.spec.ts | 93 +++-
.../deliverIndictmentToPoliceGuards.spec.ts | 34 +-
.../deliverSignedRulingToCourt.spec.ts | 41 +-
.../deliverSignedRulingToPolice.spec.ts | 18 +-
.../getCaseFilesRecordPdf.spec.ts | 48 +-
.../getCourtRecordPdf.spec.ts | 84 ++--
.../getIndictmentPdf.spec.ts | 43 +-
.../getRulingPdf.spec.ts | 79 ++--
.../app/modules/event-log/eventLog.service.ts | 37 +-
.../src/app/modules/file/file.controller.ts | 5 +-
.../src/app/modules/file/file.module.ts | 3 +-
.../src/app/modules/file/file.service.ts | 283 ++++++------
.../modules/file/internalFile.controller.ts | 4 +-
.../file/limitedAccessFile.controller.ts | 3 +-
.../src/app/modules/file/models/file.model.ts | 4 +
.../file/models/presignedPost.model.ts | 3 +
.../fileController/createCaseFile.spec.ts | 18 +-
.../createPresignedPost.spec.ts | 39 +-
.../fileController/deleteCaseFile.spec.ts | 76 ++--
.../getCaseFileSignedUrl.spec.ts | 118 ++---
.../uploadCaseFileToCourt.spec.ts | 59 +--
.../archiveCaseFile.spec.ts | 40 +-
.../deliverCaseFileToCourt.spec.ts | 38 +-
.../deliverCaseFileToCourtOfAppeals.spec.ts | 10 +-
.../createCaseFile.spec.ts | 18 +-
.../createPresignedPost.spec.ts | 35 +-
.../deleteCaseFile.spec.ts | 78 ++--
.../getCaseFileSignedUrl.spec.ts | 50 ++-
.../modules/institution/test/getAll.spec.ts | 2 +-
.../backend/src/app/modules/police/index.ts | 6 +-
.../app/modules/police/police.controller.ts | 1 +
.../src/app/modules/police/police.service.ts | 24 +-
.../police/test/updatePoliceCase.spec.ts | 6 +-
.../police/test/uploadPoliceCaseFile.spec.ts | 133 ++++--
.../src/app/modules/user/test/create.spec.ts | 2 +-
.../src/app/modules/user/test/getAll.spec.ts | 4 +-
.../src/app/modules/user/test/getById.spec.ts | 2 +-
.../modules/user/test/getByNationalId.spec.ts | 2 +-
.../src/app/modules/user/test/update.spec.ts | 4 +-
.../IndictmentCaseFilesList.tsx | 8 +-
.../CaseOverviewHeader/CaseOverviewHeader.tsx | 4 +-
.../Indictments/CaseFiles/CaseFiles.tsx | 4 +-
.../Indictments/Processing/Processing.tsx | 4 +-
.../web/src/utils/hooks/useCaseList/index.tsx | 4 +-
.../useS3Upload/createPresignedPost.graphql | 1 +
.../limitedAccessCreatePresignedPost.graphql | 1 +
.../utils/hooks/useS3Upload/useS3Upload.ts | 6 +-
.../web/src/utils/hooks/useSections/index.ts | 8 +-
.../web/src/utils/stepHelper.ts | 20 -
.../judicial-system/web/src/utils/validate.ts | 5 +-
.../message/src/lib/message.ts | 3 +
libs/judicial-system/types/src/index.ts | 2 +-
libs/judicial-system/types/src/lib/case.ts | 48 +-
89 files changed, 2113 insertions(+), 1582 deletions(-)
create mode 100644 apps/judicial-system/backend/migrations/20240522092213-update-file-keys.js
delete mode 100644 apps/judicial-system/backend/src/app/modules/case/interceptors/transition.interceptor.ts
create mode 100644 apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToCourt.spec.ts
create mode 100644 apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToCourtGuards.spec.ts
diff --git a/apps/judicial-system/api/src/app/modules/auth/auth.service.ts b/apps/judicial-system/api/src/app/modules/auth/auth.service.ts
index 47dfeaf72b17..9f2ea5e7b278 100644
--- a/apps/judicial-system/api/src/app/modules/auth/auth.service.ts
+++ b/apps/judicial-system/api/src/app/modules/auth/auth.service.ts
@@ -160,7 +160,7 @@ export class AuthService {
nationalId: string
}
} catch (error) {
- console.error('Token verification failed:', error)
+ this.logger.error('Token verification failed:', error)
throw error
}
}
diff --git a/apps/judicial-system/api/src/app/modules/file/models/presignedPost.model.ts b/apps/judicial-system/api/src/app/modules/file/models/presignedPost.model.ts
index b9bbc73f354e..c2d419bdee0e 100644
--- a/apps/judicial-system/api/src/app/modules/file/models/presignedPost.model.ts
+++ b/apps/judicial-system/api/src/app/modules/file/models/presignedPost.model.ts
@@ -9,4 +9,7 @@ export class PresignedPost {
@Field(() => graphqlTypeJson)
readonly fields!: { [key: string]: string }
+
+ @Field(() => String)
+ readonly key!: string
}
diff --git a/apps/judicial-system/backend/migrations/20240522092213-update-file-keys.js b/apps/judicial-system/backend/migrations/20240522092213-update-file-keys.js
new file mode 100644
index 000000000000..49b35862d68a
--- /dev/null
+++ b/apps/judicial-system/backend/migrations/20240522092213-update-file-keys.js
@@ -0,0 +1,67 @@
+'use strict'
+
+module.exports = {
+ async up(queryInterface, Sequelize) {
+ return queryInterface.sequelize.transaction((transaction) =>
+ Promise.all([
+ queryInterface.sequelize.query(
+ `UPDATE case_file
+ SET key = SUBSTRING(key FROM 9)
+ WHERE key LIKE 'uploads/' || '%';
+ UPDATE case_file
+ SET key = SUBSTRING(key FROM 23)
+ WHERE key LIKE 'indictments/completed/' || '%';
+ UPDATE case_file
+ SET key = SUBSTRING(key FROM 13)
+ WHERE key LIKE 'indictments/' || '%';`,
+ { transaction },
+ ),
+ queryInterface.addColumn(
+ 'case',
+ 'indictment_hash',
+ { type: Sequelize.STRING, allowNull: true },
+ { transaction },
+ ),
+ queryInterface.addColumn(
+ 'case_file',
+ 'hash',
+ { type: Sequelize.STRING, allowNull: true },
+ { transaction },
+ ),
+ ]),
+ )
+ },
+
+ async down(queryInterface) {
+ return queryInterface.sequelize.transaction((transaction) =>
+ Promise.all([
+ queryInterface.sequelize.query(
+ `UPDATE case_file
+ SET key = 'uploads/' || key
+ WHERE key IS NOT NULL AND case_id IN (
+ SELECT id
+ FROM "case"
+ WHERE type != 'INDICTMENT'
+ );
+ UPDATE case_file
+ SET key = 'indictments/completed/' || key
+ WHERE key IS NOT NULL AND case_id IN (
+ SELECT id
+ FROM "case"
+ WHERE type = 'INDICTMENT' AND state = 'COMPLETED'
+ );
+ UPDATE case_file
+ SET key = 'indictments/' || key
+ WHERE key IS NOT NULL AND case_id IN (
+ SELECT id
+ FROM "case"
+ WHERE type = 'INDICTMENT' AND state != 'COMPLETED'
+ );`,
+ { transaction },
+ ),
+ queryInterface.removeColumn('case', 'indictment_hash', { transaction }),
+ queryInterface.removeColumn('case_file', 'hash', { transaction }),
+ ]),
+ )
+ },
+}
diff --git a/apps/judicial-system/backend/src/app/formatters/confirmedIndictmentPdf.ts b/apps/judicial-system/backend/src/app/formatters/confirmedIndictmentPdf.ts
index 2f896a54141a..341797e8cfea 100644
--- a/apps/judicial-system/backend/src/app/formatters/confirmedIndictmentPdf.ts
+++ b/apps/judicial-system/backend/src/app/formatters/confirmedIndictmentPdf.ts
@@ -2,8 +2,8 @@ import { applyCase } from 'beygla'
import { PDFDocument, rgb, StandardFonts } from 'pdf-lib'
import { formatDate } from '@island.is/judicial-system/formatters'
-import { IndictmentConfirmation } from '@island.is/judicial-system/types'
+import { IndictmentConfirmation } from './indictmentPdf'
import { drawTextWithEllipsisPDFKit } from './pdfHelpers'
import { PDFKitCoatOfArms } from './PDFKitCoatOfArms'
diff --git a/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts b/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts
index 31042ac3dba0..1e481e244591 100644
--- a/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts
+++ b/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts
@@ -46,6 +46,7 @@ export const makeProsecutor = (): User => {
role: UserRole.PROSECUTOR,
active: true,
title: 'aðstoðarsaksóknari',
+ canConfirmIndictment: true,
institution: {
id: '',
created: '',
diff --git a/apps/judicial-system/backend/src/app/formatters/index.ts b/apps/judicial-system/backend/src/app/formatters/index.ts
index b69fe76741ef..3807e29bbac5 100644
--- a/apps/judicial-system/backend/src/app/formatters/index.ts
+++ b/apps/judicial-system/backend/src/app/formatters/index.ts
@@ -32,4 +32,5 @@ export {
export { getRequestPdfAsBuffer, getRequestPdfAsString } from './requestPdf'
export { getRulingPdfAsBuffer, getRulingPdfAsString } from './rulingPdf'
export { createCaseFilesRecord } from './caseFilesRecordPdf'
-export { createIndictment } from './indictmentPdf'
+export { createIndictment, IndictmentConfirmation } from './indictmentPdf'
+export { createConfirmedIndictment } from './confirmedIndictmentPdf'
diff --git a/apps/judicial-system/backend/src/app/formatters/indictmentPdf.ts b/apps/judicial-system/backend/src/app/formatters/indictmentPdf.ts
index 750b82bd7ce0..19716636f770 100644
--- a/apps/judicial-system/backend/src/app/formatters/indictmentPdf.ts
+++ b/apps/judicial-system/backend/src/app/formatters/indictmentPdf.ts
@@ -8,10 +8,6 @@ import {
formatDate,
lowercase,
} from '@island.is/judicial-system/formatters'
-import {
- CaseState,
- type IndictmentConfirmation,
-} from '@island.is/judicial-system/types'
import { nowFactory } from '../factories'
import { indictment } from '../messages'
@@ -56,6 +52,12 @@ const roman = (num: number) => {
return str
}
+export interface IndictmentConfirmation {
+ actor: string
+ institution: string
+ date: Date
+}
+
export const createIndictment = async (
theCase: Case,
formatMessage: FormatMessage,
@@ -81,7 +83,7 @@ export const createIndictment = async (
setTitle(doc, title)
- if (theCase.state === CaseState.SUBMITTED && confirmation) {
+ if (confirmation) {
addIndictmentConfirmation(
doc,
confirmation.actor,
diff --git a/apps/judicial-system/backend/src/app/messages/courtUpload.ts b/apps/judicial-system/backend/src/app/messages/courtUpload.ts
index c14ed09b7836..060c9cd8cb05 100644
--- a/apps/judicial-system/backend/src/app/messages/courtUpload.ts
+++ b/apps/judicial-system/backend/src/app/messages/courtUpload.ts
@@ -6,6 +6,11 @@ export const courtUpload = defineMessages({
defaultMessage: 'Krafa um {caseType} {date}',
description: 'Notaður sem nafn á kröfuskjali í Auði.',
},
+ indictment: {
+ id: 'judicial.system.backend:court_upload.indictment',
+ defaultMessage: 'Ákæra',
+ description: 'Notaður sem nafn á ákæru í Auði.',
+ },
caseFilesRecord: {
id: 'judicial.system.backend:court_upload.case_files_record',
defaultMessage: 'Skjalaskrá {policeCaseNumber}',
diff --git a/apps/judicial-system/backend/src/app/modules/aws-s3/awsS3.service.ts b/apps/judicial-system/backend/src/app/modules/aws-s3/awsS3.service.ts
index 3ffe4f13c044..1b781380af3b 100644
--- a/apps/judicial-system/backend/src/app/modules/aws-s3/awsS3.service.ts
+++ b/apps/judicial-system/backend/src/app/modules/aws-s3/awsS3.service.ts
@@ -2,10 +2,36 @@ import { S3 } from 'aws-sdk'
import { Inject, Injectable } from '@nestjs/common'
+import { type Logger, LOGGER_PROVIDER } from '@island.is/logging'
import type { ConfigType } from '@island.is/nest/config'
+import {
+ CaseState,
+ CaseType,
+ isCompletedCase,
+ isIndictmentCase,
+} from '@island.is/judicial-system/types'
+
import { awsS3ModuleConfig } from './awsS3.config'
+const requestPrefix = 'uploads/'
+const generatedPrefix = 'generated/'
+const indictmentPrefix = 'indictments/'
+const completedIndictmentPrefix = 'indictments/completed/'
+
+const formatConfirmedKey = (key: string) =>
+ key.replace(/\/([^/]*)$/, '/confirmed/$1')
+const formatS3RequestKey = (key: string) => `${requestPrefix}${key}`
+const formatS3IndictmentKey = (key: string) => `${indictmentPrefix}${key}`
+const formatS3CompletedIndictmentKey = (key: string) =>
+ `${completedIndictmentPrefix}${key}`
+const formatS3Key = (caseType: CaseType, caseState: CaseState, key: string) =>
+ isIndictmentCase(caseType)
+ ? isCompletedCase(caseState)
+ ? formatS3CompletedIndictmentKey(key)
+ : formatS3IndictmentKey(key)
+ : formatS3RequestKey(key)
+
@Injectable()
export class AwsS3Service {
private readonly s3: S3
@@ -13,18 +39,24 @@ export class AwsS3Service {
constructor(
@Inject(awsS3ModuleConfig.KEY)
private readonly config: ConfigType,
+ @Inject(LOGGER_PROVIDER) private readonly logger: Logger,
) {
this.s3 = new S3({ region: this.config.region })
}
- createPresignedPost(key: string, type: string): Promise {
+ createPresignedPost(
+ caseType: CaseType,
+ caseState: CaseState,
+ key: string,
+ type: string,
+ ): Promise {
return new Promise((resolve, reject) => {
this.s3.createPresignedPost(
{
Bucket: this.config.bucket,
Expires: this.config.timeToLivePost,
Fields: {
- key,
+ key: formatS3Key(caseType, caseState, key),
'content-type': type,
'Content-Disposition': 'inline',
},
@@ -40,7 +72,56 @@ export class AwsS3Service {
})
}
- getSignedUrl(key: string, timeToLive?: number): Promise {
+ private objectExistsInS3(key: string): Promise {
+ return this.s3
+ .headObject({
+ Bucket: this.config.bucket,
+ Key: key,
+ })
+ .promise()
+ .then(
+ () => true,
+ () => {
+ // The error is either 404 Not Found or 403 Forbidden.
+ // Normally, we would check if the error is 404 Not Found.
+ // However, to avoid granting the service ListBucket permissions,
+ // we also allow 403 Forbidden.
+ return false
+ },
+ )
+ }
+
+ private requestObjectExists(key: string): Promise {
+ return this.objectExistsInS3(formatS3RequestKey(key))
+ }
+
+ private async indictmentObjectExists(
+ caseSate: CaseState,
+ key: string,
+ ): Promise {
+ if (isCompletedCase(caseSate)) {
+ if (await this.objectExistsInS3(formatS3CompletedIndictmentKey(key))) {
+ return true
+ }
+ }
+
+ return this.objectExistsInS3(formatS3IndictmentKey(key))
+ }
+
+ objectExists(
+ caseType: CaseType,
+ caseState: CaseState,
+ key: string,
+ ): Promise {
+ return isIndictmentCase(caseType)
+ ? this.indictmentObjectExists(caseState, key)
+ : this.requestObjectExists(key)
+ }
+
+ private getSignedUrlFromS3(
+ key: string,
+ timeToLive?: number,
+ ): Promise {
return new Promise((resolve, reject) => {
this.s3.getSignedUrl(
'getObject',
@@ -60,36 +141,89 @@ export class AwsS3Service {
})
}
- async deleteObject(key: string): Promise {
- return this.s3
- .deleteObject({
- Bucket: this.config.bucket,
- Key: key,
- })
- .promise()
- .then(() => true)
+ private getRequestSignedUrl(
+ key: string,
+ timeToLive?: number,
+ ): Promise {
+ return this.getSignedUrlFromS3(formatS3RequestKey(key), timeToLive)
}
- objectExists(key: string): Promise {
- return this.s3
- .headObject({
- Bucket: this.config.bucket,
- Key: key,
- })
- .promise()
- .then(
- () => true,
- () => {
- // The error is either 404 Not Found or 403 Forbidden.
- // Normally, we would check if the error is 404 Not Found.
- // However, to avoid granting the service ListBucket permissions,
- // we also allow 403 Forbidden.
- return false
- },
- )
+ private async getIndictmentSignedUrl(
+ caseSate: CaseState,
+ key: string,
+ timeToLive?: number,
+ ): Promise {
+ if (isCompletedCase(caseSate)) {
+ const completedKey = formatS3CompletedIndictmentKey(key)
+
+ if (await this.objectExistsInS3(completedKey)) {
+ return await this.getSignedUrlFromS3(completedKey, timeToLive)
+ }
+ }
+
+ return this.getSignedUrlFromS3(formatS3IndictmentKey(key), timeToLive)
+ }
+
+ getSignedUrl(
+ caseType: CaseType,
+ caseState: CaseState,
+ key?: string,
+ timeToLive?: number,
+ ): Promise {
+ if (!key) {
+ throw new Error('Key is required')
+ }
+
+ return isIndictmentCase(caseType)
+ ? this.getIndictmentSignedUrl(caseState, key, timeToLive)
+ : this.getRequestSignedUrl(key, timeToLive)
}
- async getObject(key: string): Promise {
+ async getConfirmedSignedUrl(
+ caseType: CaseType,
+ caseState: CaseState,
+ key: string | undefined,
+ force: boolean,
+ confirmContent: (content: Buffer) => Promise,
+ timeToLive?: number,
+ ): Promise {
+ if (!key) {
+ throw new Error('Key is required')
+ }
+
+ if (!isIndictmentCase(caseType)) {
+ throw new Error('Only indictment case objects can be confirmed')
+ }
+
+ const confirmedKey = formatConfirmedKey(key)
+
+ if (
+ !force &&
+ (await this.indictmentObjectExists(caseState, confirmedKey))
+ ) {
+ return this.getIndictmentSignedUrl(caseState, confirmedKey, timeToLive)
+ }
+
+ const confirmedContent = await this.getIndictmentObject(
+ caseState,
+ key,
+ ).then((content) => confirmContent(content))
+
+ if (!confirmedContent) {
+ return this.getIndictmentSignedUrl(caseState, key, timeToLive)
+ }
+
+ return this.putConfirmedObject(
+ caseType,
+ caseState,
+ key,
+ confirmedContent,
+ ).then(() =>
+ this.getIndictmentSignedUrl(caseState, confirmedKey, timeToLive),
+ )
+ }
+
+ private async getObjectFromS3(key: string): Promise {
return this.s3
.getObject({
Bucket: this.config.bucket,
@@ -99,7 +233,88 @@ export class AwsS3Service {
.then((data) => data.Body as Buffer)
}
- async putObject(key: string, content: string): Promise {
+ private getRequestObject(key: string): Promise {
+ return this.getObjectFromS3(formatS3RequestKey(key))
+ }
+
+ private async getIndictmentObject(
+ caseSate: CaseState,
+ key: string,
+ ): Promise {
+ if (isCompletedCase(caseSate)) {
+ const completedKey = formatS3CompletedIndictmentKey(key)
+
+ if (await this.objectExistsInS3(completedKey)) {
+ return await this.getObjectFromS3(completedKey)
+ }
+ }
+
+ return this.getObjectFromS3(formatS3IndictmentKey(key))
+ }
+
+ async getObject(
+ caseType: CaseType,
+ caseState: CaseState,
+ key?: string,
+ ): Promise {
+ if (!key) {
+ throw new Error('Key is required')
+ }
+
+ return isIndictmentCase(caseType)
+ ? this.getIndictmentObject(caseState, key)
+ : this.getRequestObject(key)
+ }
+
+ getGeneratedObject(caseType: CaseType, key: string): Promise {
+ if (isIndictmentCase(caseType)) {
+ throw new Error('Only request case objects can be generated')
+ }
+
+ return this.getObjectFromS3(`${generatedPrefix}${key}`)
+ }
+
+ async getConfirmedObject(
+ caseType: CaseType,
+ caseState: CaseState,
+ key: string | undefined,
+ force: boolean,
+ confirmContent: (content: Buffer) => Promise,
+ ): Promise {
+ if (!key) {
+ throw new Error('Key is required')
+ }
+
+ if (!isIndictmentCase(caseType)) {
+ throw new Error('Only indictment case objects can be confirmed')
+ }
+
+ const confirmedKey = formatConfirmedKey(key)
+
+ if (
+ !force &&
+ (await this.indictmentObjectExists(caseState, confirmedKey))
+ ) {
+ return this.getIndictmentObject(caseState, confirmedKey)
+ }
+
+ const content = await this.getIndictmentObject(caseState, key)
+
+ const confirmedContent = await confirmContent(content)
+
+ if (!confirmedContent) {
+ return this.getIndictmentObject(caseState, key)
+ }
+
+ return this.putConfirmedObject(
+ caseType,
+ caseState,
+ key,
+ confirmedContent,
+ ).then(() => this.getIndictmentObject(caseState, confirmedKey))
+ }
+
+ private async putObjectToS3(key: string, content: string): Promise {
return this.s3
.putObject({
Bucket: this.config.bucket,
@@ -111,7 +326,95 @@ export class AwsS3Service {
.then(() => key)
}
- async copyObject(key: string, newKey: string): Promise {
+ async putObject(
+ caseType: CaseType,
+ caseState: CaseState,
+ key: string,
+ content: string,
+ ): Promise {
+ return this.putObjectToS3(formatS3Key(caseType, caseState, key), content)
+ }
+
+ putGeneratedObject(
+ caseType: CaseType,
+ key: string,
+ content: string,
+ ): Promise {
+ if (isIndictmentCase(caseType)) {
+ throw new Error('Only request case objects can be generated')
+ }
+
+ return this.putObjectToS3(`${generatedPrefix}${key}`, content)
+ }
+
+ putConfirmedObject(
+ caseType: CaseType,
+ caseState: CaseState,
+ key: string,
+ content: string,
+ ): Promise {
+ if (!isIndictmentCase(caseType)) {
+ throw new Error('Only indictment case objects can be confirmed')
+ }
+
+ return this.putObject(caseType, caseState, formatConfirmedKey(key), content)
+ }
+
+ private async deleteObjectFromS3(key: string): Promise {
+ return this.s3
+ .deleteObject({
+ Bucket: this.config.bucket,
+ Key: key,
+ })
+ .promise()
+ .then(() => true)
+ .catch((reason) => {
+ // Tolerate failure, but log error
+ this.logger.error(`Failed to delete object ${key} from AWS S3`, {
+ reason,
+ })
+ return false
+ })
+ }
+
+ private deleteRequestObject(key: string): Promise {
+ return this.deleteObjectFromS3(formatS3RequestKey(key))
+ }
+
+ private async deleteIndictmentObject(
+ caseSate: CaseState,
+ key: string,
+ ): Promise {
+ if (isCompletedCase(caseSate)) {
+ const completedKey = formatS3CompletedIndictmentKey(key)
+
+ if (await this.objectExistsInS3(completedKey)) {
+ // No need to wait for the delete to finish
+ this.deleteObjectFromS3(formatConfirmedKey(completedKey))
+
+ return await this.deleteObjectFromS3(completedKey)
+ }
+ }
+
+ const originalKey = formatS3IndictmentKey(key)
+
+ // No need to wait for the delete to finish
+ this.deleteObjectFromS3(formatConfirmedKey(originalKey))
+
+ return this.deleteObjectFromS3(originalKey)
+ }
+
+ async deleteObject(
+ caseType: CaseType,
+ caseState: CaseState,
+ key: string,
+ ): Promise {
+ return isIndictmentCase(caseType)
+ ? this.deleteIndictmentObject(caseState, key)
+ : this.deleteRequestObject(key)
+ }
+
+ private async copyObject(key: string, newKey: string): Promise {
return this.s3
.copyObject({
Bucket: this.config.bucket,
@@ -121,4 +424,43 @@ export class AwsS3Service {
.promise()
.then(() => newKey)
}
+
+ async archiveObject(
+ caseType: CaseType,
+ caseState: CaseState,
+ key: string,
+ ): Promise {
+ if (!isIndictmentCase(caseType)) {
+ throw new Error('Only indictment case objects can be archived')
+ }
+
+ if (!isCompletedCase(caseState)) {
+ throw new Error('Only completed indictment case objects can be archived')
+ }
+
+ const oldKey = formatS3IndictmentKey(key)
+ const newKey = formatS3CompletedIndictmentKey(key)
+
+ const oldConfirmedKey = formatConfirmedKey(oldKey)
+
+ if (await this.objectExistsInS3(oldConfirmedKey)) {
+ const newConfirmedKey = formatConfirmedKey(newKey)
+
+ if (!(await this.objectExistsInS3(newConfirmedKey))) {
+ await this.copyObject(oldConfirmedKey, newConfirmedKey)
+ }
+
+ // No need to wait for the delete to finish
+ this.deleteObjectFromS3(oldConfirmedKey)
+ }
+
+ if (!(await this.objectExistsInS3(newKey))) {
+ await this.copyObject(oldKey, newKey)
+ }
+
+ // No need to wait for the delete to finish
+ this.deleteObjectFromS3(oldKey)
+
+ return newKey
+ }
}
diff --git a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts
index a06ffadef641..d055e66339fb 100644
--- a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts
@@ -96,7 +96,6 @@ import {
} from './guards/rolesRules'
import { CaseInterceptor } from './interceptors/case.interceptor'
import { CaseListInterceptor } from './interceptors/caseList.interceptor'
-import { TransitionInterceptor } from './interceptors/transition.interceptor'
import { Case } from './models/case.model'
import { SignatureConfirmationResponse } from './models/signatureConfirmation.response'
import { transitionCase } from './state/case.state'
@@ -260,7 +259,6 @@ export class CaseController {
}
@UseGuards(JwtAuthGuard, CaseExistsGuard, RolesGuard, CaseWriteGuard)
- @UseInterceptors(TransitionInterceptor)
@RolesRules(
prosecutorTransitionRule,
prosecutorRepresentativeTransitionRule,
@@ -304,9 +302,8 @@ export class CaseController {
`User ${user.id} does not have permission to confirm indictments`,
)
}
- if (theCase.indictmentDeniedExplanation) {
- update.indictmentDeniedExplanation = ''
- }
+
+ update.indictmentDeniedExplanation = null
}
break
case CaseTransition.ACCEPT:
@@ -339,7 +336,6 @@ export class CaseController {
),
}
}
-
break
case CaseTransition.REOPEN:
update.rulingDate = null
@@ -399,12 +395,11 @@ export class CaseController {
}
break
case CaseTransition.ASK_FOR_CONFIRMATION:
- if (theCase.indictmentReturnedExplanation) {
- update.indictmentReturnedExplanation = ''
- }
+ update.indictmentReturnedExplanation = null
break
case CaseTransition.RETURN_INDICTMENT:
- update.courtCaseNumber = ''
+ update.courtCaseNumber = null
+ update.indictmentHash = null
break
case CaseTransition.REDISTRIBUTE:
update.judgeId = null
diff --git a/apps/judicial-system/backend/src/app/modules/case/case.service.ts b/apps/judicial-system/backend/src/app/modules/case/case.service.ts
index e01cb4ae6e1e..36465195d58d 100644
--- a/apps/judicial-system/backend/src/app/modules/case/case.service.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/case.service.ts
@@ -41,6 +41,7 @@ import {
isCompletedCase,
isIndictmentCase,
isRestrictionCase,
+ isTrafficViolationCase,
NotificationType,
prosecutorCanSelectDefenderForInvestigationCase,
UserRole,
@@ -55,7 +56,7 @@ import { AwsS3Service } from '../aws-s3'
import { CourtService } from '../court'
import { Defendant, DefendantService } from '../defendant'
import { CaseEvent, EventService } from '../event'
-import { EventLog } from '../event-log'
+import { EventLog, EventLogService } from '../event-log'
import { CaseFile, FileService } from '../file'
import { IndictmentCount } from '../indictment-count'
import { Institution } from '../institution'
@@ -104,7 +105,6 @@ export interface UpdateCase
| 'caseFilesComments'
| 'prosecutorId'
| 'sharedWithProsecutorsOfficeId'
- | 'courtCaseNumber'
| 'sessionArrangements'
| 'courtLocation'
| 'courtStartDate'
@@ -153,8 +153,6 @@ export interface UpdateCase
| 'appealValidToDate'
| 'isAppealCustodyIsolation'
| 'appealIsolationToDate'
- | 'indictmentDeniedExplanation'
- | 'indictmentReturnedExplanation'
| 'indictmentRulingDecision'
| 'indictmentReviewerId'
> {
@@ -164,6 +162,7 @@ export interface UpdateCase
defendantWaivesRightToCounsel?: boolean
rulingDate?: Date | null
rulingSignatureDate?: Date | null
+ courtCaseNumber?: string | null
courtRecordSignatoryId?: string | null
courtRecordSignatureDate?: Date | null
parentCaseId?: string | null
@@ -171,6 +170,9 @@ export interface UpdateCase
courtDate?: UpdateDateLog | null
postponedIndefinitelyExplanation?: string | null
judgeId?: string | null
+ indictmentReturnedExplanation?: string | null
+ indictmentDeniedExplanation?: string | null
+ indictmentHash?: string | null
}
type DateLogKeys = keyof Pick
@@ -271,6 +273,7 @@ export const include: Includeable[] = [
as: 'eventLogs',
required: false,
where: { eventType: { [Op.in]: eventTypes } },
+ order: [['created', 'ASC']],
separate: true,
},
{
@@ -364,6 +367,7 @@ export class CaseService {
private readonly signingService: SigningService,
private readonly intlService: IntlService,
private readonly eventService: EventService,
+ private readonly eventLogService: EventLogService,
private readonly messageService: MessageService,
@Inject(LOGGER_PROVIDER) private readonly logger: Logger,
) {}
@@ -388,7 +392,7 @@ export class CaseService {
pdf: string,
): Promise {
return this.awsS3Service
- .putObject(`generated/${theCase.id}/ruling.pdf`, pdf)
+ .putGeneratedObject(theCase.type, `${theCase.id}/ruling.pdf`, pdf)
.then(() => true)
.catch((reason) => {
this.logger.error(
@@ -405,7 +409,7 @@ export class CaseService {
pdf: string,
): Promise {
return this.awsS3Service
- .putObject(`generated/${theCase.id}/courtRecord.pdf`, pdf)
+ .putGeneratedObject(theCase.type, `${theCase.id}/courtRecord.pdf`, pdf)
.then(() => true)
.catch((reason) => {
this.logger.error(
@@ -465,7 +469,11 @@ export class CaseService {
) {
for (const caseFile of theCase.caseFiles) {
if (caseFile.policeCaseNumber === oldPoliceCaseNumbers[i]) {
- await this.fileService.deleteCaseFile(caseFile, transaction)
+ await this.fileService.deleteCaseFile(
+ theCase,
+ caseFile,
+ transaction,
+ )
}
}
@@ -642,6 +650,19 @@ export class CaseService {
elementId: policeCaseNumber,
}))
+ const caseFilesCategories = isTrafficViolationCase(theCase)
+ ? [
+ CaseFileCategory.COVER_LETTER,
+ CaseFileCategory.CRIMINAL_RECORD,
+ CaseFileCategory.COST_BREAKDOWN,
+ ]
+ : [
+ CaseFileCategory.COVER_LETTER,
+ CaseFileCategory.INDICTMENT,
+ CaseFileCategory.CRIMINAL_RECORD,
+ CaseFileCategory.COST_BREAKDOWN,
+ ]
+
const deliverCaseFileToCourtMessages =
theCase.caseFiles
?.filter(
@@ -649,12 +670,7 @@ export class CaseService {
caseFile.state === CaseFileState.STORED_IN_RVG &&
caseFile.key &&
((caseFile.category &&
- [
- CaseFileCategory.COVER_LETTER,
- CaseFileCategory.INDICTMENT,
- CaseFileCategory.CRIMINAL_RECORD,
- CaseFileCategory.COST_BREAKDOWN,
- ].includes(caseFile.category)) ||
+ caseFilesCategories.includes(caseFile.category)) ||
(caseFile.category === CaseFileCategory.CASE_FILE &&
!caseFile.policeCaseNumber)),
)
@@ -665,12 +681,20 @@ export class CaseService {
elementId: caseFile.id,
})) ?? []
- return this.messageService.sendMessagesToQueue(
- this.getDeliverProsecutorToCourtMessages(theCase, user)
- .concat(this.getDeliverDefendantToCourtMessages(theCase, user))
- .concat(deliverCaseFilesRecordToCourtMessages)
- .concat(deliverCaseFileToCourtMessages),
- )
+ const messages = this.getDeliverProsecutorToCourtMessages(theCase, user)
+ .concat(this.getDeliverDefendantToCourtMessages(theCase, user))
+ .concat(deliverCaseFilesRecordToCourtMessages)
+ .concat(deliverCaseFileToCourtMessages)
+
+ if (isTrafficViolationCase(theCase)) {
+ messages.push({
+ type: MessageType.DELIVERY_TO_COURT_INDICTMENT,
+ user,
+ caseId: theCase.id,
+ })
+ }
+
+ return this.messageService.sendMessagesToQueue(messages)
}
private addMessagesForDefenderEmailChangeToQueue(
@@ -1343,6 +1367,29 @@ export class CaseService {
}
}
+ handleEventLogs(
+ theCase: Case,
+ update: UpdateCase,
+ user: TUser,
+ transaction: Transaction,
+ ) {
+ if (
+ isIndictmentCase(theCase.type) &&
+ update.state === CaseState.SUBMITTED &&
+ theCase.state === CaseState.WAITING_FOR_CONFIRMATION
+ ) {
+ return this.eventLogService.create(
+ {
+ eventType: EventType.INDICTMENT_CONFIRMED,
+ caseId: theCase.id,
+ nationalId: user.nationalId,
+ userRole: user.role,
+ },
+ transaction,
+ )
+ }
+ }
+
async update(
theCase: Case,
update: UpdateCase,
@@ -1351,6 +1398,8 @@ export class CaseService {
): Promise {
const receivingCase =
update.courtCaseNumber && theCase.state === CaseState.SUBMITTED
+ const returningIndictmentCase =
+ update.state === CaseState.DRAFT && theCase.state === CaseState.RECEIVED
return this.sequelize
.transaction(async (transaction) => {
@@ -1365,6 +1414,7 @@ export class CaseService {
await this.handleDateUpdates(theCase, update, transaction)
await this.handleCommentUpdates(theCase, update, transaction)
+ await this.handleEventLogs(theCase, update, user, transaction)
if (Object.keys(update).length === 0) {
return
@@ -1401,6 +1451,13 @@ export class CaseService {
) {
await this.fileService.resetCaseFileStates(theCase.id, transaction)
}
+
+ if (returningIndictmentCase) {
+ await this.fileService.resetIndictmentCaseFileHashes(
+ theCase.id,
+ transaction,
+ )
+ }
})
.then(async () => {
const updatedCase = await this.findById(theCase.id, true)
diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts b/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts
index f4c2d84aaa4c..838275876e6d 100644
--- a/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts
@@ -263,7 +263,6 @@ const getDefenceUserCasesQueryFilter = (user: User): WhereOptions => {
}
export const getCasesQueryFilter = (user: User): WhereOptions => {
- // TODO: Convert to switch
if (isProsecutionUser(user)) {
return getProsecutionUserCasesQueryFilter(user)
}
diff --git a/apps/judicial-system/backend/src/app/modules/case/interceptors/transition.interceptor.ts b/apps/judicial-system/backend/src/app/modules/case/interceptors/transition.interceptor.ts
deleted file mode 100644
index d95e51de802e..000000000000
--- a/apps/judicial-system/backend/src/app/modules/case/interceptors/transition.interceptor.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { Observable } from 'rxjs'
-import { map } from 'rxjs/operators'
-
-import {
- CallHandler,
- ExecutionContext,
- Injectable,
- NestInterceptor,
-} from '@nestjs/common'
-
-import {
- CaseFileCategory,
- CaseState,
- CaseTransition,
- EventType,
- isIndictmentCase,
- isTrafficViolationCase,
- User,
-} from '@island.is/judicial-system/types'
-
-import { nowFactory } from '../../../factories'
-import { formatConfirmedIndictmentKey } from '../../../formatters/formatters'
-import { AwsS3Service } from '../../aws-s3'
-import { EventLogService } from '../../event-log'
-import { TransitionCaseDto } from '../dto/transitionCase.dto'
-import { Case } from '../models/case.model'
-import { PDFService } from '../pdf.service'
-
-@Injectable()
-export class TransitionInterceptor implements NestInterceptor {
- constructor(
- private readonly eventLogService: EventLogService,
- private readonly awsService: AwsS3Service,
- private readonly pdfService: PDFService,
- ) {}
-
- async intercept(
- context: ExecutionContext,
- next: CallHandler,
- ): Promise> {
- const request = context.switchToHttp().getRequest()
- const theCase: Case = request.case
- const dto: TransitionCaseDto = request.body
- const user: User = request.user
-
- if (
- isIndictmentCase(theCase.type) &&
- !isTrafficViolationCase(theCase.indictmentSubtypes, theCase.type) &&
- theCase.state === CaseState.WAITING_FOR_CONFIRMATION &&
- dto.transition === CaseTransition.SUBMIT
- ) {
- for (const indictment of theCase.caseFiles?.filter(
- (cf) => cf.category === CaseFileCategory.INDICTMENT && cf.key,
- ) ?? []) {
- // Get indictment PDF from S3
- const file = await this.awsService.getObject(indictment.key ?? '')
-
- // Create a stamped indictment PDF
- const confirmedIndictment =
- await this.pdfService.getConfirmedIndictmentPdf(
- {
- actor: user.name,
- institution: user.institution?.name ?? '',
- date: nowFactory(),
- },
- file,
- )
-
- // Save the PDF to S3
- await this.awsService.putObject(
- formatConfirmedIndictmentKey(indictment.key),
- confirmedIndictment.toString('binary'),
- )
- }
- }
-
- return next.handle().pipe(
- map((data: Case) => {
- if (isIndictmentCase(data.type) && data.state === CaseState.SUBMITTED) {
- this.eventLogService.create({
- eventType: EventType.INDICTMENT_CONFIRMED,
- caseId: data.id,
- nationalId: user.nationalId,
- userRole: user.role,
- })
- }
-
- return data
- }),
- )
- }
-}
diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts
index 585a3e7c04f4..b122cb8a7630 100644
--- a/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts
@@ -106,6 +106,27 @@ export class InternalCaseController {
)
}
+ @UseGuards(CaseExistsGuard, new CaseTypeGuard(indictmentCases))
+ @Post(
+ `case/:caseId/${messageEndpoint[MessageType.DELIVERY_TO_COURT_INDICTMENT]}`,
+ )
+ @ApiOkResponse({
+ type: DeliverResponse,
+ description: 'Delivers an indictment to court',
+ })
+ deliverIndictmentToCourt(
+ @Param('caseId') caseId: string,
+ @CurrentCase() theCase: Case,
+ @Body() deliverDto: DeliverDto,
+ ): Promise {
+ this.logger.debug(`Delivering the indictment for case ${caseId} to court`)
+
+ return this.internalCaseService.deliverIndictmentToCourt(
+ theCase,
+ deliverDto.user,
+ )
+ }
+
@UseGuards(CaseExistsGuard, new CaseTypeGuard(indictmentCases))
@Post(
`case/:caseId/${
diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts
index 690b48e5e69e..020a22acc5e8 100644
--- a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts
@@ -23,12 +23,10 @@ import {
CaseOrigin,
CaseState,
CaseType,
- EventType,
- type IndictmentConfirmation,
- isCompletedCase,
isIndictmentCase,
isProsecutionUser,
isRestrictionCase,
+ isTrafficViolationCase,
NotificationType,
restrictionCases,
type User as TUser,
@@ -37,8 +35,6 @@ import {
import { nowFactory, uuidFactory } from '../../factories'
import {
- createCaseFilesRecord,
- createIndictment,
getCourtRecordPdfAsBuffer,
getCourtRecordPdfAsString,
getCustodyNoticePdfAsString,
@@ -53,7 +49,7 @@ import { CaseEvent, EventService } from '../event'
import { EventLogService } from '../event-log'
import { CaseFile, FileService } from '../file'
import { IndictmentCount, IndictmentCountService } from '../indictment-count'
-import { CourtDocumentType, PoliceService } from '../police'
+import { PoliceDocument, PoliceDocumentType, PoliceService } from '../police'
import { UserService } from '../user'
import { InternalCreateCaseDto } from './dto/internalCreateCase.dto'
import { archiveFilter } from './filters/case.archiveFilter'
@@ -63,6 +59,7 @@ import { CaseArchive } from './models/caseArchive.model'
import { DateLog } from './models/dateLog.model'
import { DeliverResponse } from './models/deliver.response'
import { caseModuleConfig } from './case.config'
+import { PDFService } from './pdf.service'
const caseEncryptionProperties: (keyof Case)[] = [
'description',
@@ -160,6 +157,7 @@ export class InternalCaseService {
private readonly defendantService: DefendantService,
@Inject(forwardRef(() => EventLogService))
private readonly eventLogService: EventLogService,
+ private readonly pdfService: PDFService,
@Inject(LOGGER_PROVIDER) private readonly logger: Logger,
) {}
@@ -284,135 +282,11 @@ export class InternalCaseService {
})
}
- private async getCaseFilesRecordPdf(
- theCase: Case,
- policeCaseNumber: string,
- ): Promise {
- if (isCompletedCase(theCase.state)) {
- try {
- return await this.awsS3Service.getObject(
- `indictments/completed/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
- )
- } catch {
- // Ignore the error and try the original key
- }
- }
-
- try {
- return await this.awsS3Service.getObject(
- `indictments/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
- )
- } catch {
- // Ignore the error and generate the pdf
- }
-
- const caseFiles = theCase.caseFiles
- ?.filter(
- (caseFile) =>
- caseFile.policeCaseNumber === policeCaseNumber &&
- caseFile.category === CaseFileCategory.CASE_FILE &&
- caseFile.type === 'application/pdf' &&
- caseFile.key &&
- caseFile.chapter !== null &&
- caseFile.orderWithinChapter !== null,
- )
- ?.sort(
- (caseFile1, caseFile2) =>
- (caseFile1.chapter ?? 0) - (caseFile2.chapter ?? 0) ||
- (caseFile1.orderWithinChapter ?? 0) -
- (caseFile2.orderWithinChapter ?? 0),
- )
- ?.map((caseFile) => async () => {
- const buffer = await this.awsS3Service
- .getObject(caseFile.key ?? '')
- .catch((reason) => {
- // Tolerate failure, but log error
- this.logger.error(
- `Unable to get file ${caseFile.id} of case ${theCase.id} from AWS S3`,
- { reason },
- )
- })
-
- return {
- chapter: caseFile.chapter as number,
- date: caseFile.displayDate ?? caseFile.created,
- name: caseFile.userGeneratedFilename ?? caseFile.name,
- buffer: buffer ?? undefined,
- }
- })
-
- const pdf = await createCaseFilesRecord(
- theCase,
- policeCaseNumber,
- caseFiles ?? [],
- this.formatMessage,
- )
-
- await this.awsS3Service
- .putObject(
- `indictments/${isCompletedCase(theCase.state) ? 'completed/' : ''}${
- theCase.id
- }/${policeCaseNumber}/caseFilesRecord.pdf`,
- pdf.toString('binary'),
- )
- .catch((reason) => {
- this.logger.error(
- `Failed to upload case files record pdf to AWS S3 for case ${theCase.id} and police case ${policeCaseNumber}`,
- { reason },
- )
- })
-
- return pdf
- }
-
- private async throttleUploadCaseFilesRecordPdfToCourt(
- theCase: Case,
- policeCaseNumber: string,
- user: TUser,
- ): Promise {
- // Serialize all case files record pdf deliveries in this process
- await this.throttle.catch((reason) => {
- this.logger.info('Previous case files record pdf delivery failed', {
- reason,
- })
- })
-
- await this.refreshFormatMessage()
-
- return this.getCaseFilesRecordPdf(theCase, policeCaseNumber)
- .then((pdf) => {
- const fileName = this.formatMessage(courtUpload.caseFilesRecord, {
- policeCaseNumber,
- })
-
- return this.courtService.createDocument(
- user,
- theCase.id,
- theCase.courtId,
- theCase.courtCaseNumber,
- CourtDocumentFolder.CASE_DOCUMENTS,
- fileName,
- `${fileName}.pdf`,
- 'application/pdf',
- pdf,
- )
- })
- .then(() => {
- return true
- })
- .catch((error) => {
- // Tolerate failure, but log error
- this.logger.warn(
- `Failed to upload case files record pdf to court for case ${theCase.id}`,
- { error },
- )
-
- return false
- })
- }
-
private getSignedRulingPdf(theCase: Case) {
- return this.awsS3Service.getObject(`generated/${theCase.id}/ruling.pdf`)
+ return this.awsS3Service.getGeneratedObject(
+ theCase.type,
+ `${theCase.id}/ruling.pdf`,
+ )
}
private async deliverSignedRulingPdfToCourt(
@@ -628,20 +502,77 @@ export class InternalCaseService {
})
}
+ async deliverIndictmentToCourt(
+ theCase: Case,
+ user: TUser,
+ ): Promise {
+ return this.pdfService
+ .getIndictmentPdf(theCase)
+ .then(async (pdf) => {
+ await this.refreshFormatMessage()
+
+ const fileName = this.formatMessage(courtUpload.indictment)
+
+ return this.courtService.createDocument(
+ user,
+ theCase.id,
+ theCase.courtId,
+ theCase.courtCaseNumber,
+ CourtDocumentFolder.INDICTMENT_DOCUMENTS,
+ fileName,
+ `${fileName}.pdf`,
+ 'application/pdf',
+ pdf,
+ )
+ })
+ .then(() => ({ delivered: true }))
+ .catch((reason) => {
+ // Tolerate failure, but log error
+ this.logger.warn(
+ `Failed to upload indictment pdf to court for case ${theCase.id}`,
+ { reason },
+ )
+
+ return { delivered: false }
+ })
+ }
+
async deliverCaseFilesRecordToCourt(
theCase: Case,
policeCaseNumber: string,
user: TUser,
): Promise {
- this.throttle = this.throttleUploadCaseFilesRecordPdfToCourt(
- theCase,
- policeCaseNumber,
- user,
- )
+ return this.pdfService
+ .getCaseFilesRecordPdf(theCase, policeCaseNumber)
+ .then(async (pdf) => {
+ await this.refreshFormatMessage()
+
+ const fileName = this.formatMessage(courtUpload.caseFilesRecord, {
+ policeCaseNumber,
+ })
- const delivered = await this.throttle
+ return this.courtService.createDocument(
+ user,
+ theCase.id,
+ theCase.courtId,
+ theCase.courtCaseNumber,
+ CourtDocumentFolder.CASE_DOCUMENTS,
+ fileName,
+ `${fileName}.pdf`,
+ 'application/pdf',
+ pdf,
+ )
+ })
+ .then(() => ({ delivered: true }))
+ .catch((reason) => {
+ // Tolerate failure, but log reason
+ this.logger.warn(
+ `Failed to upload case files record pdf to court for case ${theCase.id}`,
+ { reason },
+ )
- return { delivered }
+ return { delivered: false }
+ })
}
async archiveCaseFilesRecord(
@@ -649,26 +580,12 @@ export class InternalCaseService {
policeCaseNumber: string,
): Promise {
return this.awsS3Service
- .copyObject(
- `indictments/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
- `indictments/completed/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
+ .archiveObject(
+ theCase.type,
+ theCase.state,
+ `${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
)
- .then(() => {
- // Fire and forget, no need to wait for the result
- this.awsS3Service
- .deleteObject(
- `indictments/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
- )
- .catch((reason) => {
- // Tolerate failure, but log what happened
- this.logger.error(
- `Could not delete case files record for case ${theCase.id} and police case ${policeCaseNumber} from AWS S3`,
- { reason },
- )
- })
-
- return { delivered: true }
- })
+ .then(() => ({ delivered: true }))
.catch((reason) => {
this.logger.error(
`Failed to archive case files record for case ${theCase.id} and police case ${policeCaseNumber}`,
@@ -685,9 +602,9 @@ export class InternalCaseService {
): Promise {
await this.refreshFormatMessage()
- const delivered = await this.upploadRequestPdfToCourt(theCase, user)
-
- return { delivered }
+ return this.upploadRequestPdfToCourt(theCase, user).then((delivered) => ({
+ delivered,
+ }))
}
async deliverCourtRecordToCourt(
@@ -696,9 +613,9 @@ export class InternalCaseService {
): Promise {
await this.refreshFormatMessage()
- const delivered = await this.uploadCourtRecordPdfToCourt(theCase, user)
-
- return { delivered }
+ return this.uploadCourtRecordPdfToCourt(theCase, user).then(
+ (delivered) => ({ delivered }),
+ )
}
async deliverSignedRulingToCourt(
@@ -707,9 +624,9 @@ export class InternalCaseService {
): Promise {
await this.refreshFormatMessage()
- const delivered = await this.deliverSignedRulingPdfToCourt(theCase, user)
-
- return { delivered }
+ return this.deliverSignedRulingPdfToCourt(theCase, user).then(
+ (delivered) => ({ delivered }),
+ )
}
async deliverCaseConclusionToCourt(
@@ -830,7 +747,7 @@ export class InternalCaseService {
private async deliverCaseToPoliceWithFiles(
theCase: Case,
user: TUser,
- courtDocuments: { type: CourtDocumentType; courtDocument: string }[],
+ courtDocuments: PoliceDocument[],
): Promise {
const originalAncestor = await this.findOriginalAncestor(theCase)
@@ -872,13 +789,13 @@ export class InternalCaseService {
.then(async () => {
const courtDocuments = [
{
- type: CourtDocumentType.RVKR,
+ type: PoliceDocumentType.RVKR,
courtDocument: Base64.btoa(
await getRequestPdfAsString(theCase, this.formatMessage),
),
},
{
- type: CourtDocumentType.RVTB,
+ type: PoliceDocumentType.RVTB,
courtDocument: Base64.btoa(
await getCourtRecordPdfAsString(theCase, this.formatMessage),
),
@@ -888,7 +805,7 @@ export class InternalCaseService {
) && theCase.state === CaseState.ACCEPTED
? [
{
- type: CourtDocumentType.RVVI,
+ type: PoliceDocumentType.RVVI,
courtDocument: Base64.btoa(
await getCustodyNoticePdfAsString(
theCase,
@@ -929,13 +846,18 @@ export class InternalCaseService {
caseFile.key,
)
.map(async (caseFile) => {
- const file = await this.awsS3Service.getObject(caseFile.key ?? '')
+ // TODO: Tolerate failure, but log error
+ const file = await this.awsS3Service.getObject(
+ theCase.type,
+ theCase.state,
+ caseFile.key,
+ )
return {
type:
caseFile.category === CaseFileCategory.COURT_RECORD
- ? CourtDocumentType.RVTB
- : CourtDocumentType.RVDO,
+ ? PoliceDocumentType.RVTB
+ : PoliceDocumentType.RVDO,
courtDocument: Base64.btoa(file.toString('binary')),
}
}) ?? [],
@@ -959,91 +881,70 @@ export class InternalCaseService {
theCase: Case,
user: TUser,
): Promise {
- const delivered = await Promise.all(
- theCase.caseFiles
- ?.filter(
- (caseFile) =>
- caseFile.category === CaseFileCategory.INDICTMENT && caseFile.key,
- )
- .map(async (caseFile) => {
- const file = await this.awsS3Service.getObject(caseFile.key ?? '')
-
- return {
- type: CourtDocumentType.RVAS,
- courtDocument: Base64.btoa(file.toString('binary')),
- }
- }) ?? [],
- )
- .then(async (indictmentDocuments) => {
- if (indictmentDocuments.length === 0) {
- let confirmation: IndictmentConfirmation = undefined
- const confirmationEvent =
- await this.eventLogService.findEventTypeByCaseId(
- EventType.INDICTMENT_CONFIRMED,
- theCase.id,
- )
-
- if (confirmationEvent && confirmationEvent.nationalId) {
- const actor = await this.userService.findByNationalId(
- confirmationEvent.nationalId,
- )
-
- confirmation = {
- actor: actor.name,
- institution: actor.institution?.name ?? '',
- date: confirmationEvent.created,
- }
- }
-
- const file = await this.refreshFormatMessage().then(async () =>
- createIndictment(theCase, this.formatMessage, confirmation),
- )
+ try {
+ let policeDocuments: PoliceDocument[]
- indictmentDocuments.push({
- type: CourtDocumentType.RVAS,
- courtDocument: Base64.btoa(file.toString('binary')),
- })
- }
+ if (isTrafficViolationCase(theCase)) {
+ const file = await this.pdfService.getIndictmentPdf(theCase)
- return this.deliverCaseToPoliceWithFiles(
- theCase,
- user,
- indictmentDocuments,
- )
- })
- .catch((reason) => {
- // Tolerate failure, but log error
- this.logger.error(
- `Failed to deliver indictment for case ${theCase.id} to police`,
+ policeDocuments = [
{
- reason,
+ type: PoliceDocumentType.RVAS,
+ courtDocument: Base64.btoa(file.toString('binary')),
},
+ ]
+ } else {
+ policeDocuments = await Promise.all(
+ theCase.caseFiles
+ ?.filter(
+ (caseFile) =>
+ caseFile.category === CaseFileCategory.INDICTMENT &&
+ caseFile.key,
+ )
+ .map(async (caseFile) => {
+ // TODO: Tolerate failure, but log error
+ const file = await this.fileService.getCaseFileFromS3(
+ theCase,
+ caseFile,
+ )
+
+ return {
+ type: PoliceDocumentType.RVAS,
+ courtDocument: Base64.btoa(file.toString('binary')),
+ }
+ }) ?? [],
)
+ }
- return false
- })
+ const delivered = await this.deliverCaseToPoliceWithFiles(
+ theCase,
+ user,
+ policeDocuments,
+ )
- return { delivered }
+ return { delivered }
+ } catch (error) {
+ // Tolerate failure, but log error
+ this.logger.error(
+ `Failed to deliver indictment for case ${theCase.id} to police`,
+ { error },
+ )
+
+ return { delivered: false }
+ }
}
- private async throttleUploadCaseFilesRecordPdfToPolice(
+ async deliverCaseFilesRecordToPolice(
theCase: Case,
policeCaseNumber: string,
user: TUser,
- ): Promise {
- // Serialize all case files record pdf deliveries in this process
- await this.throttle.catch((reason) => {
- this.logger.info('Previous case files record pdf delivery failed', {
- reason,
- })
- })
-
- return this.refreshFormatMessage()
- .then(() => this.getCaseFilesRecordPdf(theCase, policeCaseNumber))
+ ): Promise {
+ const delivered = await this.pdfService
+ .getCaseFilesRecordPdf(theCase, policeCaseNumber)
.then((pdf) =>
this.deliverCaseToPoliceWithFiles(theCase, user, [
{
- type: CourtDocumentType.RVMG,
+ type: PoliceDocumentType.RVMG,
courtDocument: Base64.btoa(pdf.toString('binary')),
},
]),
@@ -1057,20 +958,6 @@ export class InternalCaseService {
return false
})
- }
-
- async deliverCaseFilesRecordToPolice(
- theCase: Case,
- policeCaseNumber: string,
- user: TUser,
- ): Promise {
- this.throttle = this.throttleUploadCaseFilesRecordPdfToPolice(
- theCase,
- policeCaseNumber,
- user,
- )
-
- const delivered = await this.throttle
return { delivered }
}
@@ -1083,7 +970,7 @@ export class InternalCaseService {
.then((pdf) =>
this.deliverCaseToPoliceWithFiles(theCase, user, [
{
- type: CourtDocumentType.RVUR,
+ type: PoliceDocumentType.RVUR,
courtDocument: Base64.btoa(pdf.toString('binary')),
},
]),
@@ -1109,10 +996,15 @@ export class InternalCaseService {
theCase.caseFiles
?.filter((file) => file.category === CaseFileCategory.APPEAL_RULING)
.map(async (caseFile) => {
- const file = await this.awsS3Service.getObject(caseFile.key ?? '')
+ // TODO: Tolerate failure, but log error
+ const file = await this.awsS3Service.getObject(
+ theCase.type,
+ theCase.state,
+ caseFile.key,
+ )
return {
- type: CourtDocumentType.RVUL,
+ type: PoliceDocumentType.RVUL,
courtDocument: Base64.btoa(file.toString('binary')),
}
}) ?? [],
diff --git a/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts b/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts
index a0e44ad60792..1be2398a862e 100644
--- a/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts
@@ -422,9 +422,10 @@ export class LimitedAccessCaseService {
),
) ?? []
+ // TODO: speed this up by fetching all files in parallel
for (const file of caseFilesByCategory) {
await this.awsS3Service
- .getObject(file.key ?? '')
+ .getObject(theCase.type, theCase.state, file.key)
.then((content) => filesToZip.push({ data: content, name: file.name }))
.catch((reason) =>
// Tolerate failure, but log what happened
diff --git a/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts b/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts
index 81117d9db4cf..fb6261333a6a 100644
--- a/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts
@@ -1013,4 +1013,12 @@ export class Case extends Model {
})
@ApiPropertyOptional({ enum: IndictmentCaseReviewDecision })
indictmentReviewDecision?: IndictmentCaseReviewDecision
+
+ /**********
+ * The md5 hash of the confirmed generated indictment
+ * Only used for traffic violation cases
+ **********/
+ @Column({ type: DataType.STRING, allowNull: true })
+ @ApiPropertyOptional({ type: String })
+ indictmentHash?: string
}
diff --git a/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts b/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts
index 1f9f9c5fa26a..d1b1d966820b 100644
--- a/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/pdf.service.ts
@@ -1,8 +1,12 @@
+import CryptoJS from 'crypto-js'
+
import {
+ BadRequestException,
Inject,
Injectable,
InternalServerErrorException,
} from '@nestjs/common'
+import { InjectModel } from '@nestjs/sequelize'
import { FormatMessage, IntlService } from '@island.is/cms-translations'
import type { Logger } from '@island.is/logging'
@@ -10,10 +14,9 @@ import { LOGGER_PROVIDER } from '@island.is/logging'
import {
CaseFileCategory,
- CaseState,
EventType,
- type IndictmentConfirmation,
- isCompletedCase,
+ hasIndictmentCaseBeenSubmittedToCourt,
+ isTrafficViolationCase,
type User as TUser,
} from '@island.is/judicial-system/types'
@@ -24,10 +27,9 @@ import {
getCustodyNoticePdfAsBuffer,
getRequestPdfAsBuffer,
getRulingPdfAsBuffer,
+ IndictmentConfirmation,
} from '../../formatters'
-import { createConfirmedIndictment } from '../../formatters/confirmedIndictmentPdf'
import { AwsS3Service } from '../aws-s3'
-import { EventLogService } from '../event-log'
import { UserService } from '../user'
import { Case } from './models/case.model'
@@ -38,8 +40,8 @@ export class PDFService {
constructor(
private readonly awsS3Service: AwsS3Service,
private readonly intlService: IntlService,
- private readonly eventLogService: EventLogService,
private readonly userService: UserService,
+ @InjectModel(Case) private readonly caseModel: typeof Case,
@Inject(LOGGER_PROVIDER) private readonly logger: Logger,
) {}
@@ -87,36 +89,50 @@ export class PDFService {
)
?.map((caseFile) => async () => {
const buffer = await this.awsS3Service
- .getObject(caseFile.key ?? '')
+ .getObject(theCase.type, theCase.state, caseFile.key)
.catch((reason) => {
// Tolerate failure, but log error
this.logger.error(
`Unable to get file ${caseFile.id} of case ${theCase.id} from AWS S3`,
{ reason },
)
+
+ return undefined
})
return {
chapter: caseFile.chapter as number,
date: caseFile.displayDate ?? caseFile.created,
name: caseFile.userGeneratedFilename ?? caseFile.name,
- buffer: buffer ?? undefined,
+ buffer: buffer,
}
})
- return createCaseFilesRecord(
+ const generatedPdf = await createCaseFilesRecord(
theCase,
policeCaseNumber,
caseFiles ?? [],
this.formatMessage,
)
+
+ if (hasIndictmentCaseBeenSubmittedToCourt(theCase.state)) {
+ // No need to wait for the upload to finish
+ this.tryUploadPdfToS3(
+ theCase,
+ `${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
+ generatedPdf,
+ )
+ }
+
+ return generatedPdf
}
async getCourtRecordPdf(theCase: Case, user: TUser): Promise {
if (theCase.courtRecordSignatureDate) {
try {
- return await this.awsS3Service.getObject(
- `generated/${theCase.id}/courtRecord.pdf`,
+ return await this.awsS3Service.getGeneratedObject(
+ theCase.type,
+ `${theCase.id}/courtRecord.pdf`,
)
} catch (error) {
this.logger.info(
@@ -140,8 +156,9 @@ export class PDFService {
async getRulingPdf(theCase: Case): Promise {
if (theCase.rulingSignatureDate) {
try {
- return await this.awsS3Service.getObject(
- `generated/${theCase.id}/ruling.pdf`,
+ return await this.awsS3Service.getGeneratedObject(
+ theCase.type,
+ `${theCase.id}/ruling.pdf`,
)
} catch (error) {
this.logger.info(
@@ -162,62 +179,101 @@ export class PDFService {
return getCustodyNoticePdfAsBuffer(theCase, this.formatMessage)
}
+ private async tryGetPdfFromS3(
+ theCase: Case,
+ key: string,
+ ): Promise {
+ return await this.awsS3Service
+ .getObject(theCase.type, theCase.state, key)
+ .catch(() => undefined) // Ignore errors and return undefined
+ }
+
+ private tryUploadPdfToS3(theCase: Case, key: string, pdf: Buffer) {
+ this.awsS3Service
+ .putObject(theCase.type, theCase.state, key, pdf.toString('binary'))
+ .catch((reason) => {
+ this.logger.error(`Failed to upload pdf ${key} to AWS S3`, { reason })
+ })
+ }
+
async getIndictmentPdf(theCase: Case): Promise {
- await this.refreshFormatMessage()
+ if (!isTrafficViolationCase(theCase)) {
+ throw new BadRequestException(
+ `Case ${theCase.id} is not a traffic violation case`,
+ )
+ }
- let confirmation: IndictmentConfirmation = undefined
- const confirmationEvent = await this.eventLogService.findEventTypeByCaseId(
- EventType.INDICTMENT_CONFIRMED,
- theCase.id,
- )
+ let confirmation: IndictmentConfirmation | undefined = undefined
- if (confirmationEvent && confirmationEvent.nationalId) {
- const actor = await this.userService.findByNationalId(
- confirmationEvent.nationalId,
+ if (hasIndictmentCaseBeenSubmittedToCourt(theCase.state)) {
+ if (theCase.indictmentHash) {
+ const existingPdf = await this.tryGetPdfFromS3(
+ theCase,
+ `${theCase.id}/indictment.pdf`,
+ )
+
+ if (existingPdf) {
+ return existingPdf
+ }
+ }
+
+ const confirmationEvent = theCase.eventLogs?.find(
+ (event) => event.eventType === EventType.INDICTMENT_CONFIRMED,
)
- confirmation = {
- actor: actor.name,
- institution: actor.institution?.name ?? '',
- date: confirmationEvent.created,
+ if (confirmationEvent && confirmationEvent.nationalId) {
+ const actor = await this.userService.findByNationalId(
+ confirmationEvent.nationalId,
+ )
+
+ confirmation = {
+ actor: actor.name,
+ institution: actor.institution?.name ?? '',
+ date: confirmationEvent.created,
+ }
}
}
- return createIndictment(theCase, this.formatMessage, confirmation)
- }
+ await this.refreshFormatMessage()
- async getConfirmedIndictmentPdf(
- confirmation: IndictmentConfirmation,
- indictmentPDF: Buffer,
- ): Promise {
- return createConfirmedIndictment(confirmation, indictmentPDF)
+ const generatedPdf = await createIndictment(
+ theCase,
+ this.formatMessage,
+ confirmation,
+ )
+
+ if (hasIndictmentCaseBeenSubmittedToCourt(theCase.state) && confirmation) {
+ const indictmentHash = CryptoJS.MD5(
+ generatedPdf.toString('binary'),
+ ).toString(CryptoJS.enc.Hex)
+
+ // No need to wait for this to finish
+ this.caseModel
+ .update({ indictmentHash }, { where: { id: theCase.id } })
+ .then(() =>
+ this.tryUploadPdfToS3(
+ theCase,
+ `${theCase.id}/indictment.pdf`,
+ generatedPdf,
+ ),
+ )
+ }
+
+ return generatedPdf
}
async getCaseFilesRecordPdf(
theCase: Case,
policeCaseNumber: string,
): Promise {
- if (
- ![CaseState.NEW, CaseState.DRAFT, CaseState.SUBMITTED].includes(
- theCase.state,
+ if (hasIndictmentCaseBeenSubmittedToCourt(theCase.state)) {
+ const existingPdf = await this.tryGetPdfFromS3(
+ theCase,
+ `${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
)
- ) {
- if (isCompletedCase(theCase.state)) {
- try {
- return await this.awsS3Service.getObject(
- `indictments/completed/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
- )
- } catch {
- // Ignore the error and try the original key
- }
- }
- try {
- return await this.awsS3Service.getObject(
- `indictments/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
- )
- } catch {
- // Ignore the error and generate the pdf
+ if (existingPdf) {
+ return existingPdf
}
}
@@ -226,6 +282,6 @@ export class PDFService {
policeCaseNumber,
)
- return this.throttle
+ return await this.throttle
}
}
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getAll.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getAll.spec.ts
index bc96214b669d..9abf249fb601 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getAll.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getAll.spec.ts
@@ -49,7 +49,7 @@ describe('CaseController - Get all', () => {
const mockGetCasesQueryFilter = getCasesQueryFilter as jest.Mock
mockGetCasesQueryFilter.mockReturnValueOnce(filter)
const mockFindAll = mockCaseModel.findAll as jest.Mock
- mockFindAll.mockReturnValueOnce(cases)
+ mockFindAll.mockResolvedValueOnce(cases)
then = await givenWhenThen()
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCaseFilesRecordPdf.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCaseFilesRecordPdf.spec.ts
index 79e03ac601df..b982b0dfba58 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCaseFilesRecordPdf.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCaseFilesRecordPdf.spec.ts
@@ -3,7 +3,11 @@ import { uuid } from 'uuidv4'
import { BadRequestException } from '@nestjs/common'
-import { CaseFileCategory, CaseState } from '@island.is/judicial-system/types'
+import {
+ CaseFileCategory,
+ CaseState,
+ CaseType,
+} from '@island.is/judicial-system/types'
import { createTestingCaseModule } from '../createTestingCaseModule'
@@ -38,11 +42,12 @@ describe('CaseController - Get case files record pdf', () => {
] as CaseFile[]
const theCase = {
id: caseId,
- state: CaseState.ACCEPTED,
+ type: CaseType.INDICTMENT,
+ state: CaseState.COMPLETED,
policeCaseNumbers: [uuid(), policeCaseNumber, uuid()],
caseFiles,
} as Case
- const pdf = uuid()
+ const pdf = Buffer.from(uuid())
const res = { end: jest.fn() } as unknown as Response
let mockawsS3Service: AwsS3Service
@@ -54,6 +59,8 @@ describe('CaseController - Get case files record pdf', () => {
mockawsS3Service = awsS3Service
const mockGetObject = mockawsS3Service.getObject as jest.Mock
mockGetObject.mockRejectedValue(new Error('Some error'))
+ const mockPutObject = mockawsS3Service.putObject as jest.Mock
+ mockPutObject.mockRejectedValue(new Error('Some error'))
givenWhenThen = async (policeCaseNumber: string) => {
const then = {} as Then
@@ -82,13 +89,10 @@ describe('CaseController - Get case files record pdf', () => {
})
it('should generate pdf after failing to get it from AWS S3', () => {
- expect(mockawsS3Service.getObject).toHaveBeenNthCalledWith(
- 1,
- `indictments/completed/${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
- )
- expect(mockawsS3Service.getObject).toHaveBeenNthCalledWith(
- 2,
- `indictments/${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
+ expect(mockawsS3Service.getObject).toHaveBeenCalledWith(
+ theCase.type,
+ theCase.state,
+ `${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
)
expect(createCaseFilesRecord).toHaveBeenCalledWith(
theCase,
@@ -96,28 +100,20 @@ describe('CaseController - Get case files record pdf', () => {
expect.any(Array),
expect.any(Function),
)
+ expect(mockawsS3Service.putObject).toHaveBeenCalledWith(
+ theCase.type,
+ theCase.state,
+ `${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
+ pdf.toString('binary'),
+ )
expect(res.end).toHaveBeenCalledWith(pdf)
})
})
- describe('pdf returned from AWS S3 indictment completed folder', () => {
- beforeEach(async () => {
- const mockGetObject = mockawsS3Service.getObject as jest.Mock
- mockGetObject.mockReturnValueOnce(pdf)
-
- await givenWhenThen(policeCaseNumber)
- })
-
- it('should return pdf', () => {
- expect(res.end).toHaveBeenCalledWith(pdf)
- })
- })
-
- describe('pdf returned from AWS S3 indictment folder', () => {
+ describe('pdf returned from AWS S3', () => {
beforeEach(async () => {
const mockGetObject = mockawsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some error'))
- mockGetObject.mockReturnValueOnce(pdf)
+ mockGetObject.mockResolvedValueOnce(pdf)
await givenWhenThen(policeCaseNumber)
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCourtRecordPdf.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCourtRecordPdf.spec.ts
index b2e92770f532..143d68c2ab8b 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCourtRecordPdf.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCourtRecordPdf.spec.ts
@@ -3,7 +3,7 @@ import { uuid } from 'uuidv4'
import { Logger } from '@island.is/logging'
-import { User } from '@island.is/judicial-system/types'
+import { CaseState, CaseType, User } from '@island.is/judicial-system/types'
import { createTestingCaseModule } from '../createTestingCaseModule'
@@ -33,9 +33,16 @@ describe('CaseController - Get court record pdf', () => {
beforeEach(async () => {
const { awsS3Service, logger, caseController } =
await createTestingCaseModule()
+
mockAwsS3Service = awsS3Service
mockLogger = logger
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockRejectedValue(new Error('Some error'))
+ const getMock = getCourtRecordPdfAsBuffer as jest.Mock
+ getMock.mockRejectedValue(new Error('Some error'))
+
givenWhenThen = async (
caseId: string,
user: User,
@@ -57,23 +64,29 @@ describe('CaseController - Get court record pdf', () => {
describe('AWS S3 pdf returned', () => {
const user = { id: uuid() } as User
const caseId = uuid()
+ const caseType = CaseType.PAROLE_REVOCATION
+ const caseSate = CaseState.ACCEPTED
const theCase = {
id: caseId,
+ type: caseType,
+ state: caseSate,
courtRecordSignatureDate: nowFactory(),
} as Case
const res = { end: jest.fn() } as unknown as Response
const pdf = uuid()
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockResolvedValueOnce(pdf)
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockResolvedValueOnce(pdf)
await givenWhenThen(caseId, user, theCase, res)
})
it('should lookup pdf', () => {
- expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
- `generated/${caseId}/courtRecord.pdf`,
+ expect(mockAwsS3Service.getGeneratedObject).toHaveBeenCalledWith(
+ caseType,
+ `${caseId}/courtRecord.pdf`,
)
expect(res.end).toHaveBeenCalledWith(pdf)
})
@@ -86,13 +99,11 @@ describe('CaseController - Get court record pdf', () => {
id: caseId,
courtRecordSignatureDate: nowFactory(),
} as Case
- const error = new Error('Some ignored error')
+ const error = new Error('Some error')
const res = { end: jest.fn() } as unknown as Response
const pdf = uuid()
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(error)
const getMock = getCourtRecordPdfAsBuffer as jest.Mock
getMock.mockResolvedValueOnce(pdf)
@@ -124,8 +135,6 @@ describe('CaseController - Get court record pdf', () => {
const res = {} as Response
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some ignored error'))
const getMock = getCourtRecordPdfAsBuffer as jest.Mock
getMock.mockRejectedValueOnce(new Error('Some error'))
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCourtRecordSignatureConfirmation.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCourtRecordSignatureConfirmation.spec.ts
index 1edc06628158..59bc8a99c6a4 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCourtRecordSignatureConfirmation.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getCourtRecordSignatureConfirmation.spec.ts
@@ -38,6 +38,14 @@ describe('CaseController - Get court record signature confirmation', () => {
mockAwsS3Service = awsS3Service
mockCaseModel = caseModel
+ const mockPutGeneratedObject =
+ mockAwsS3Service.putGeneratedObject as jest.Mock
+ mockPutGeneratedObject.mockRejectedValue(new Error('Some error'))
+ const mockUpdate = mockCaseModel.update as jest.Mock
+ mockUpdate.mockRejectedValue(new Error('Some error'))
+ const mockFindOne = mockCaseModel.findOne as jest.Mock
+ mockFindOne.mockRejectedValue(new Error('Some error'))
+
const mockTransaction = sequelize.transaction as jest.Mock
transaction = {} as Transaction
mockTransaction.mockImplementationOnce(
@@ -92,8 +100,9 @@ describe('CaseController - Get court record signature confirmation', () => {
let then: Then
beforeEach(async () => {
- const mockPutObject = mockAwsS3Service.putObject as jest.Mock
- mockPutObject.mockResolvedValueOnce(Promise.resolve())
+ const mockPutGeneratedObject =
+ mockAwsS3Service.putGeneratedObject as jest.Mock
+ mockPutGeneratedObject.mockResolvedValueOnce(Promise.resolve())
const mockUpdate = mockCaseModel.update as jest.Mock
mockUpdate.mockResolvedValueOnce([1, [theCase]])
const mockFindOne = mockCaseModel.findOne as jest.Mock
@@ -120,9 +129,6 @@ describe('CaseController - Get court record signature confirmation', () => {
let then: Then
beforeEach(async () => {
- const mockPutObject = mockAwsS3Service.putObject as jest.Mock
- mockPutObject.mockRejectedValueOnce(new Error('Some error'))
-
then = await givenWhenThen(caseId, user, theCase, documentToken)
})
@@ -138,10 +144,9 @@ describe('CaseController - Get court record signature confirmation', () => {
let then: Then
beforeEach(async () => {
- const mockPutObject = mockAwsS3Service.putObject as jest.Mock
- mockPutObject.mockResolvedValueOnce(Promise.resolve())
- const mockUpdate = mockCaseModel.update as jest.Mock
- mockUpdate.mockRejectedValueOnce(new Error('Some error'))
+ const mockPutGeneratedObject =
+ mockAwsS3Service.putGeneratedObject as jest.Mock
+ mockPutGeneratedObject.mockResolvedValueOnce(Promise.resolve())
then = await givenWhenThen(caseId, user, theCase, documentToken)
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getIndictmentPdf.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getIndictmentPdf.spec.ts
index 5cc6729c4027..015d1f3c01ff 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getIndictmentPdf.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getIndictmentPdf.spec.ts
@@ -1,9 +1,16 @@
import { Response } from 'express'
import { uuid } from 'uuidv4'
+import {
+ CaseState,
+ CaseType,
+ IndictmentSubtype,
+} from '@island.is/judicial-system/types'
+
import { createTestingCaseModule } from '../createTestingCaseModule'
import { createIndictment } from '../../../../formatters'
+import { AwsS3Service } from '../../../aws-s3'
import { Case } from '../../models/case.model'
jest.mock('../../../../formatters/indictmentPdf')
@@ -16,16 +23,29 @@ type GivenWhenThen = () => Promise
describe('CaseController - Get indictment pdf', () => {
const caseId = uuid()
+ const policeCaseNumber = uuid()
const theCase = {
id: caseId,
+ type: CaseType.INDICTMENT,
+ state: CaseState.COMPLETED,
+ policeCaseNumbers: [policeCaseNumber],
+ indictmentSubtypes: {
+ [policeCaseNumber]: [IndictmentSubtype.TRAFFIC_VIOLATION],
+ },
+ indictmentHash: uuid(),
} as Case
- const pdf = uuid()
+ const pdf = Buffer.from(uuid())
const res = { end: jest.fn() } as unknown as Response
+ let mockAwsS3Service: AwsS3Service
let givenWhenThen: GivenWhenThen
beforeEach(async () => {
- const { caseController } = await createTestingCaseModule()
+ const { awsS3Service, caseController } = await createTestingCaseModule()
+
+ mockAwsS3Service = awsS3Service
+ const mockGetObject = mockAwsS3Service.getObject as jest.Mock
+ mockGetObject.mockRejectedValue(new Error('Some error'))
givenWhenThen = async () => {
const then = {} as Then
@@ -48,7 +68,12 @@ describe('CaseController - Get indictment pdf', () => {
await givenWhenThen()
})
- it('should generate pdf', () => {
+ it('should generate pdf after failing to get it from AWS S3', () => {
+ expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
+ theCase.type,
+ theCase.state,
+ `${caseId}/indictment.pdf`,
+ )
expect(createIndictment).toHaveBeenCalledWith(
theCase,
expect.any(Function),
@@ -57,4 +82,17 @@ describe('CaseController - Get indictment pdf', () => {
expect(res.end).toHaveBeenCalledWith(pdf)
})
})
+
+ describe('pdf returned from AWS S3', () => {
+ beforeEach(async () => {
+ const mockGetObject = mockAwsS3Service.getObject as jest.Mock
+ mockGetObject.mockResolvedValueOnce(pdf)
+
+ await givenWhenThen()
+ })
+
+ it('should return pdf', () => {
+ expect(res.end).toHaveBeenCalledWith(pdf)
+ })
+ })
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingPdf.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingPdf.spec.ts
index 4be69ad5d793..0f081c9a98ed 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingPdf.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingPdf.spec.ts
@@ -3,6 +3,8 @@ import { uuid } from 'uuidv4'
import { Logger } from '@island.is/logging'
+import { CaseState, CaseType } from '@island.is/judicial-system/types'
+
import { createTestingCaseModule } from '../createTestingCaseModule'
import { nowFactory } from '../../../../factories'
@@ -30,9 +32,16 @@ describe('CaseController - Get ruling pdf', () => {
beforeEach(async () => {
const { awsS3Service, logger, caseController } =
await createTestingCaseModule()
+
mockAwsS3Service = awsS3Service
mockLogger = logger
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockRejectedValue(new Error('Some error'))
+ const getMock = getRulingPdfAsBuffer as jest.Mock
+ getMock.mockRejectedValue(new Error('Some error'))
+
givenWhenThen = async (caseId: string, theCase: Case, res: Response) => {
const then = {} as Then
@@ -46,36 +55,32 @@ describe('CaseController - Get ruling pdf', () => {
}
})
- describe('AWS S3 lookup', () => {
- const caseId = uuid()
- const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
- const res = {} as Response
-
- beforeEach(async () => {
- await givenWhenThen(caseId, theCase, res)
- })
-
- it('should lookup pdf', () => {
- expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
- `generated/${caseId}/ruling.pdf`,
- )
- })
- })
-
describe('AWS S3 pdf returned', () => {
const caseId = uuid()
- const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
+ const caseType = CaseType.EXPULSION_FROM_HOME
+ const caseState = CaseState.REJECTED
+ const theCase = {
+ id: caseId,
+ type: caseType,
+ state: caseState,
+ rulingSignatureDate: nowFactory(),
+ } as Case
const res = { end: jest.fn() } as unknown as Response
const pdf = {}
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockResolvedValueOnce(pdf)
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockResolvedValueOnce(pdf)
await givenWhenThen(caseId, theCase, res)
})
it('should return pdf', () => {
+ expect(mockAwsS3Service.getGeneratedObject).toHaveBeenCalledWith(
+ caseType,
+ `${caseId}/ruling.pdf`,
+ )
expect(res.end).toHaveBeenCalledWith(pdf)
})
})
@@ -84,12 +89,9 @@ describe('CaseController - Get ruling pdf', () => {
const caseId = uuid()
const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
const res = {} as Response
- const error = new Error('Some ignored error')
+ const error = new Error('Some error')
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(error)
-
await givenWhenThen(caseId, theCase, res)
})
@@ -101,43 +103,25 @@ describe('CaseController - Get ruling pdf', () => {
})
})
- describe('pdf generated', () => {
- const caseId = uuid()
- const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
- const res = {} as Response
-
- beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some ignored error'))
-
- await givenWhenThen(caseId, theCase, res)
- })
-
- it('should generate pdf', () => {
- expect(getRulingPdfAsBuffer).toHaveBeenCalledWith(
- theCase,
- expect.any(Function),
- )
- })
- })
-
- describe('pdf generated', () => {
+ describe('generated pdf returned', () => {
const caseId = uuid()
const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
- const res = {} as Response
+ const res = { end: jest.fn() } as unknown as Response
+ const pdf = {}
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some ignored error'))
+ const getMock = getRulingPdfAsBuffer as jest.Mock
+ getMock.mockResolvedValueOnce(pdf)
await givenWhenThen(caseId, theCase, res)
})
- it('should generate pdf', () => {
+ it('should return pdf', () => {
expect(getRulingPdfAsBuffer).toHaveBeenCalledWith(
theCase,
expect.any(Function),
)
+ expect(res.end).toHaveBeenCalledWith(pdf)
})
})
@@ -158,26 +142,6 @@ describe('CaseController - Get ruling pdf', () => {
})
})
- describe('generated pdf returned', () => {
- const caseId = uuid()
- const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
- const res = { end: jest.fn() } as unknown as Response
- const pdf = {}
-
- beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some ignored error'))
- const getMock = getRulingPdfAsBuffer as jest.Mock
- getMock.mockResolvedValueOnce(pdf)
-
- await givenWhenThen(caseId, theCase, res)
- })
-
- it('should return pdf', () => {
- expect(res.end).toHaveBeenCalledWith(pdf)
- })
- })
-
describe('pdf generation fails', () => {
const caseId = uuid()
const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
@@ -185,11 +149,6 @@ describe('CaseController - Get ruling pdf', () => {
const res = {} as Response
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some ignored error'))
- const getMock = getRulingPdfAsBuffer as jest.Mock
- getMock.mockRejectedValueOnce(new Error('Some error'))
-
then = await givenWhenThen(caseId, theCase, res)
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmation.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmation.spec.ts
index 6ebe21d948ae..c05595c977b2 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmation.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmation.spec.ts
@@ -65,8 +65,9 @@ describe('CaseController - Get ruling signature confirmation', () => {
const mockToday = nowFactory as jest.Mock
mockToday.mockReturnValueOnce(date)
- const mockPutObject = mockAwsS3Service.putObject as jest.Mock
- mockPutObject.mockResolvedValue(uuid())
+ const mockPutGeneratedObject =
+ mockAwsS3Service.putGeneratedObject as jest.Mock
+ mockPutGeneratedObject.mockResolvedValue(uuid())
const mockUpdate = mockCaseModel.update as jest.Mock
mockUpdate.mockResolvedValue([1])
const mockPostMessageToQueue =
@@ -125,15 +126,12 @@ describe('CaseController - Get ruling signature confirmation', () => {
then = await givenWhenThen(caseId, user, theCase, documentToken)
})
- it('should set the ruling signature date', () => {
+ it('should return success', () => {
expect(mockCaseModel.update).toHaveBeenCalledWith(
{ rulingSignatureDate: date },
{ where: { id: caseId }, transaction },
)
- })
-
- it('should return success', () => {
- expect(mockAwsS3Service.putObject).toHaveBeenCalled()
+ expect(mockAwsS3Service.putGeneratedObject).toHaveBeenCalled()
expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([
{ type: MessageType.DELIVERY_TO_COURT_SIGNED_RULING, user, caseId },
{
@@ -169,10 +167,7 @@ describe('CaseController - Get ruling signature confirmation', () => {
{ rulingSignatureDate: date },
{ where: { id: caseId }, transaction },
)
- })
-
- it('should return success', () => {
- expect(mockAwsS3Service.putObject).toHaveBeenCalled()
+ expect(mockAwsS3Service.putGeneratedObject).toHaveBeenCalled()
expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([
{ type: MessageType.DELIVERY_TO_COURT_SIGNED_RULING, user, caseId },
{
@@ -269,8 +264,9 @@ describe('CaseController - Get ruling signature confirmation', () => {
let then: Then
beforeEach(async () => {
- const mockPutObject = mockAwsS3Service.putObject as jest.Mock
- mockPutObject.mockRejectedValueOnce(new Error('Some error'))
+ const mockPutGeneratedObject =
+ mockAwsS3Service.putGeneratedObject as jest.Mock
+ mockPutGeneratedObject.mockRejectedValueOnce(new Error('Some error'))
then = await givenWhenThen(caseId, user, theCase, documentToken)
})
@@ -279,7 +275,6 @@ describe('CaseController - Get ruling signature confirmation', () => {
expect(then.result.documentSigned).toBe(false)
expect(then.result.message).toBeTruthy()
expect(then.result.code).toBeUndefined()
-
expect(mockCaseModel.update).not.toHaveBeenCalled()
expect(mockMessageService.sendMessagesToQueue).not.toHaveBeenCalled()
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/transition.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/transition.spec.ts
index ac0f883e4f10..0c65a57ebf29 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/transition.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/transition.spec.ts
@@ -359,12 +359,22 @@ describe('CaseController - Transition', () => {
transition === CaseTransition.DELETE ? null : undefined,
courtCaseNumber:
transition === CaseTransition.RETURN_INDICTMENT
- ? ''
+ ? null
+ : undefined,
+ indictmentHash:
+ transition === CaseTransition.RETURN_INDICTMENT
+ ? null
: undefined,
rulingDate:
transition === CaseTransition.COMPLETE ? date : undefined,
judgeId:
transition === CaseTransition.REDISTRIBUTE ? null : undefined,
+ indictmentDeniedExplanation:
+ transition === CaseTransition.SUBMIT ? null : undefined,
+ indictmentReturnedExplanation:
+ transition === CaseTransition.ASK_FOR_CONFIRMATION
+ ? null
+ : undefined,
},
{ where: { id: caseId }, transaction },
)
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts
index 117142cff466..3d4b3d6fba78 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts
@@ -190,6 +190,7 @@ describe('CaseController - Update', () => {
it('should delete a case file', () => {
expect(mockFileService.deleteCaseFile).toHaveBeenCalledWith(
+ theCase,
caseFile,
transaction,
)
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/archiveCaseFilesRecord.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/archiveCaseFilesRecord.spec.ts
index 11ed25dccfda..9ae4785ad11c 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/archiveCaseFilesRecord.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/archiveCaseFilesRecord.spec.ts
@@ -2,6 +2,8 @@ import { uuid } from 'uuidv4'
import { BadRequestException } from '@nestjs/common'
+import { CaseState, CaseType } from '@island.is/judicial-system/types'
+
import { createTestingCaseModule } from '../createTestingCaseModule'
import { AwsS3Service } from '../../../aws-s3'
@@ -20,6 +22,8 @@ type GivenWhenThen = (
describe('InternalCaseController - Archive case files record', () => {
const caseId = uuid()
+ const caseType = CaseType.INDICTMENT
+ const caseState = CaseState.COMPLETED
const policeCaseNumber = uuid()
let mockawsS3Service: AwsS3Service
@@ -30,10 +34,8 @@ describe('InternalCaseController - Archive case files record', () => {
await createTestingCaseModule()
mockawsS3Service = awsS3Service
- const mockCopyObject = mockawsS3Service.copyObject as jest.Mock
- mockCopyObject.mockRejectedValue(new Error('Some error'))
- const mockDeleteObject = mockawsS3Service.deleteObject as jest.Mock
- mockDeleteObject.mockRejectedValue(new Error('Some error'))
+ const mockArchiveObject = mockawsS3Service.archiveObject as jest.Mock
+ mockArchiveObject.mockRejectedValue(new Error('Some error'))
givenWhenThen = async (
policeCaseNumber: string,
@@ -44,6 +46,8 @@ describe('InternalCaseController - Archive case files record', () => {
await internalCaseController
.archiveCaseFilesRecord(caseId, policeCaseNumber, {
id: caseId,
+ type: caseType,
+ state: caseState,
policeCaseNumbers,
} as Case)
.then((result) => (then.result = result))
@@ -57,8 +61,8 @@ describe('InternalCaseController - Archive case files record', () => {
let then: Then
beforeEach(async () => {
- const mockCopyObject = mockawsS3Service.copyObject as jest.Mock
- mockCopyObject.mockResolvedValueOnce(uuid())
+ const mockArchiveObject = mockawsS3Service.archiveObject as jest.Mock
+ mockArchiveObject.mockResolvedValueOnce(uuid())
then = await givenWhenThen(policeCaseNumber, [
uuid(),
@@ -67,20 +71,12 @@ describe('InternalCaseController - Archive case files record', () => {
])
})
- it('should copy the case files record to the AWS S3 indictment completed folder', () => {
- expect(mockawsS3Service.copyObject).toHaveBeenCalledWith(
- `indictments/${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
- `indictments/completed/${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
- )
- })
-
- it('should delete the case files record from the AWS S3 indictment folder', () => {
- expect(mockawsS3Service.deleteObject).toHaveBeenCalledWith(
- `indictments/${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
+ it('should archive the case files record', () => {
+ expect(mockawsS3Service.archiveObject).toHaveBeenCalledWith(
+ caseType,
+ caseState,
+ `${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
)
- })
-
- it('should return a success response', () => {
expect(then.result).toEqual({ delivered: true })
})
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverAppealToPolice.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverAppealToPolice.spec.ts
index e00a9c2cf05a..fe04e77ebbf0 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverAppealToPolice.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverAppealToPolice.spec.ts
@@ -14,7 +14,7 @@ import { createTestingCaseModule } from '../createTestingCaseModule'
import { randomDate } from '../../../../test'
import { AwsS3Service } from '../../../aws-s3'
-import { CourtDocumentType, PoliceService } from '../../../police'
+import { PoliceDocumentType, PoliceService } from '../../../police'
import { Case } from '../../models/case.model'
import { DeliverResponse } from '../../models/deliver.response'
@@ -40,8 +40,8 @@ describe('InternalCaseController - Deliver appeal to police', () => {
mockAwsS3Service = awsS3Service
mockPoliceService = policeService
- const mockGetObject = awsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValue(new Error('Some error'))
+ const mockGetGeneratedObject = awsS3Service.getObject as jest.Mock
+ mockGetGeneratedObject.mockRejectedValue(new Error('Some error'))
const mockUpdatePoliceCase = mockPoliceService.updatePoliceCase as jest.Mock
mockUpdatePoliceCase.mockRejectedValue(new Error('Some error'))
@@ -87,8 +87,8 @@ describe('InternalCaseController - Deliver appeal to police', () => {
let then: Then
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockResolvedValueOnce(appealRulingPdf)
+ const mockGetGeneratedObject = mockAwsS3Service.getObject as jest.Mock
+ mockGetGeneratedObject.mockResolvedValueOnce(appealRulingPdf)
const mockUpdatePoliceCase =
mockPoliceService.updatePoliceCase as jest.Mock
mockUpdatePoliceCase.mockResolvedValueOnce(true)
@@ -96,7 +96,11 @@ describe('InternalCaseController - Deliver appeal to police', () => {
then = await givenWhenThen(caseId, theCase)
})
it('should update the police case', async () => {
- expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(appealRulingKey)
+ expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
+ caseType,
+ caseState,
+ appealRulingKey,
+ )
expect(mockPoliceService.updatePoliceCase).toHaveBeenCalledWith(
user,
caseId,
@@ -109,7 +113,7 @@ describe('InternalCaseController - Deliver appeal to police', () => {
caseConclusion,
[
{
- type: CourtDocumentType.RVUL,
+ type: PoliceDocumentType.RVUL,
courtDocument: Base64.btoa(appealRulingPdf),
},
],
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToCourt.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToCourt.spec.ts
index ee2a309ee87c..f8275ed779fc 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToCourt.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToCourt.spec.ts
@@ -2,7 +2,7 @@ import { uuid } from 'uuidv4'
import { BadRequestException } from '@nestjs/common'
-import { CaseState, User } from '@island.is/judicial-system/types'
+import { CaseState, CaseType, User } from '@island.is/judicial-system/types'
import { createTestingCaseModule } from '../createTestingCaseModule'
@@ -33,7 +33,8 @@ describe('InternalCaseController - Deliver case files record to court', () => {
const courtCaseNumber = uuid()
const theCase = {
id: caseId,
- state: CaseState.ACCEPTED,
+ type: CaseType.INDICTMENT,
+ state: CaseState.COMPLETED,
policeCaseNumbers: [policeCaseNumber],
courtId,
courtCaseNumber,
@@ -45,9 +46,6 @@ describe('InternalCaseController - Deliver case files record to court', () => {
let givenWhenThen: GivenWhenThen
beforeEach(async () => {
- const mockGet = createCaseFilesRecord as jest.Mock
- mockGet.mockRejectedValue(new Error('Some error'))
-
const { awsS3Service, courtService, internalCaseController } =
await createTestingCaseModule()
@@ -94,37 +92,24 @@ describe('InternalCaseController - Deliver case files record to court', () => {
then = await givenWhenThen(caseId, policeCaseNumber, theCase)
})
- it('should try to get the pdf from AWS S3 indictment completed folder', () => {
- expect(mockAwsS3Service.getObject).toHaveBeenNthCalledWith(
- 1,
- `indictments/completed/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
+ it('should deliver the case files record', () => {
+ expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
+ theCase.type,
+ theCase.state,
+ `${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
)
- })
-
- it('should try to get the pdf from AWS S3 indictment folder', () => {
- expect(mockAwsS3Service.getObject).toHaveBeenNthCalledWith(
- 2,
- `indictments/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
- )
- })
-
- it('should generate the case files record', async () => {
expect(createCaseFilesRecord).toHaveBeenCalledWith(
theCase,
policeCaseNumber,
[],
expect.any(Function),
)
- })
-
- it('should store the case files record in AWS S3', async () => {
expect(mockAwsS3Service.putObject).toHaveBeenCalledWith(
- `indictments/completed/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
+ theCase.type,
+ theCase.state,
+ `${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
pdf.toString(),
)
- })
-
- it('should create a case files record at court', async () => {
expect(mockCourtService.createDocument).toHaveBeenCalledWith(
user,
caseId,
@@ -136,41 +121,14 @@ describe('InternalCaseController - Deliver case files record to court', () => {
'application/pdf',
pdf,
)
- })
-
- it('should return a success response', async () => {
expect(then.result).toEqual({ delivered: true })
})
})
- describe('pdf returned from AWS S3 indictment completed folder', () => {
- beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockReturnValueOnce(pdf)
-
- await givenWhenThen(caseId, policeCaseNumber, theCase)
- })
-
- it('should use the AWS S3 pdf', () => {
- expect(mockCourtService.createDocument).toHaveBeenCalledWith(
- user,
- caseId,
- courtId,
- courtCaseNumber,
- CourtDocumentFolder.CASE_DOCUMENTS,
- `Skjalaskrá ${policeCaseNumber}`,
- `Skjalaskrá ${policeCaseNumber}.pdf`,
- 'application/pdf',
- pdf,
- )
- })
- })
-
- describe('pdf returned from AWS S3 indictment folder', () => {
+ describe('pdf returned from AWS S3', () => {
beforeEach(async () => {
const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some error'))
- mockGetObject.mockReturnValueOnce(pdf)
+ mockGetObject.mockResolvedValueOnce(pdf)
await givenWhenThen(caseId, policeCaseNumber, theCase)
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToCourtGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToCourtGuards.spec.ts
index e0b918dcb4c3..328109144d6c 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToCourtGuards.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToCourtGuards.spec.ts
@@ -1,5 +1,3 @@
-import { CanActivate } from '@nestjs/common'
-
import { indictmentCases } from '@island.is/judicial-system/types'
import { CaseExistsGuard } from '../../guards/caseExists.guard'
@@ -17,34 +15,12 @@ describe('InternalCaseController - Deliver case files record to court guards', (
)
})
- it('should have two guards', () => {
+ it('should have the right guard configuration', () => {
expect(guards).toHaveLength(2)
- })
-
- describe('CaseExistsGuard', () => {
- let guard: CanActivate
-
- beforeEach(() => {
- guard = new guards[0]()
- })
-
- it('should have CaseExistsGuard as guard 1', () => {
- expect(guard).toBeInstanceOf(CaseExistsGuard)
- })
- })
-
- describe('CaseTypeGuard', () => {
- let guard: CanActivate
-
- beforeEach(() => {
- guard = guards[1]
- })
-
- it('should have CaseTypeGuard as guard 2', () => {
- expect(guard).toBeInstanceOf(CaseTypeGuard)
- expect(guard).toEqual({
- allowedCaseTypes: indictmentCases,
- })
+ expect(new guards[0]()).toBeInstanceOf(CaseExistsGuard)
+ expect(guards[1]).toBeInstanceOf(CaseTypeGuard)
+ expect(guards[1]).toEqual({
+ allowedCaseTypes: indictmentCases,
})
})
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToPolice.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToPolice.spec.ts
index 57d1eb6f335b..860e0e59dfd1 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToPolice.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseFilesRecordToPolice.spec.ts
@@ -11,7 +11,7 @@ import { nowFactory } from '../../../../factories'
import { createCaseFilesRecord } from '../../../../formatters'
import { randomDate } from '../../../../test'
import { AwsS3Service } from '../../../aws-s3'
-import { CourtDocumentType, PoliceService } from '../../../police'
+import { PoliceDocumentType, PoliceService } from '../../../police'
import { Case } from '../../models/case.model'
import { DeliverResponse } from '../../models/deliver.response'
@@ -34,7 +34,7 @@ describe('InternalCaseController - Deliver case files record to police', () => {
const user = { id: uuid() } as User
const caseId = uuid()
const caseType = CaseType.INDICTMENT
- const caseState = CaseState.ACCEPTED
+ const caseState = CaseState.COMPLETED
const policeCaseNumber = uuid()
const defendantNationalId = '0123456789'
const courtId = uuid()
@@ -107,13 +107,10 @@ describe('InternalCaseController - Deliver case files record to police', () => {
})
it('should update the police case', () => {
- expect(mockAwsS3Service.getObject).toHaveBeenNthCalledWith(
- 1,
- `indictments/completed/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
- )
- expect(mockAwsS3Service.getObject).toHaveBeenNthCalledWith(
- 2,
- `indictments/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
+ expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
+ caseType,
+ caseState,
+ `${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
)
expect(createCaseFilesRecord).toHaveBeenCalledWith(
theCase,
@@ -122,7 +119,9 @@ describe('InternalCaseController - Deliver case files record to police', () => {
expect.any(Function),
)
expect(mockAwsS3Service.putObject).toHaveBeenCalledWith(
- `indictments/completed/${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
+ theCase.type,
+ theCase.state,
+ `${theCase.id}/${policeCaseNumber}/caseFilesRecord.pdf`,
pdf.toString(),
)
expect(mockPoliceService.updatePoliceCase).toHaveBeenCalledWith(
@@ -137,7 +136,7 @@ describe('InternalCaseController - Deliver case files record to police', () => {
'',
[
{
- type: CourtDocumentType.RVMG,
+ type: PoliceDocumentType.RVMG,
courtDocument: Base64.btoa(pdf.toString('binary')),
},
],
@@ -146,40 +145,10 @@ describe('InternalCaseController - Deliver case files record to police', () => {
})
})
- describe('pdf returned from AWS S3 indictment completed folder', () => {
- beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockReturnValueOnce(pdf)
-
- await givenWhenThen(caseId, policeCaseNumber, theCase)
- })
-
- it('should use the AWS S3 pdf', () => {
- expect(mockPoliceService.updatePoliceCase).toHaveBeenCalledWith(
- user,
- caseId,
- caseType,
- caseState,
- policeCaseNumber,
- courtCaseNumber,
- defendantNationalId,
- date,
- '',
- [
- {
- type: CourtDocumentType.RVMG,
- courtDocument: Base64.btoa(pdf.toString('binary')),
- },
- ],
- )
- })
- })
-
- describe('pdf returned from AWS S3 indictment folder', () => {
+ describe('pdf returned from AWS S3', () => {
beforeEach(async () => {
const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some error'))
- mockGetObject.mockReturnValueOnce(pdf)
+ mockGetObject.mockResolvedValueOnce(pdf)
await givenWhenThen(caseId, policeCaseNumber, theCase)
})
@@ -197,7 +166,7 @@ describe('InternalCaseController - Deliver case files record to police', () => {
'',
[
{
- type: CourtDocumentType.RVMG,
+ type: PoliceDocumentType.RVMG,
courtDocument: Base64.btoa(pdf.toString('binary')),
},
],
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseToPolice.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseToPolice.spec.ts
index d26aa17ec415..65ad3532e525 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseToPolice.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverCaseToPolice.spec.ts
@@ -16,7 +16,7 @@ import {
getRequestPdfAsString,
} from '../../../../formatters'
import { randomDate } from '../../../../test'
-import { CourtDocumentType, PoliceService } from '../../../police'
+import { PoliceDocumentType, PoliceService } from '../../../police'
import { Case } from '../../models/case.model'
import { DeliverResponse } from '../../models/deliver.response'
@@ -130,15 +130,15 @@ describe('InternalCaseController - Deliver case to police', () => {
caseConclusion,
[
{
- type: CourtDocumentType.RVKR,
+ type: PoliceDocumentType.RVKR,
courtDocument: Base64.btoa(requestPdf),
},
{
- type: CourtDocumentType.RVTB,
+ type: PoliceDocumentType.RVTB,
courtDocument: Base64.btoa(courtRecordPdf),
},
{
- type: CourtDocumentType.RVVI,
+ type: PoliceDocumentType.RVVI,
courtDocument: Base64.btoa(custodyNoticePdf),
},
],
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentCaseToPolice.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentCaseToPolice.spec.ts
index 8bc07564f842..9897732717a4 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentCaseToPolice.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentCaseToPolice.spec.ts
@@ -14,7 +14,7 @@ import { createTestingCaseModule } from '../createTestingCaseModule'
import { nowFactory } from '../../../../factories'
import { randomDate } from '../../../../test'
import { AwsS3Service } from '../../../aws-s3'
-import { CourtDocumentType, PoliceService } from '../../../police'
+import { PoliceDocumentType, PoliceService } from '../../../police'
import { Case } from '../../models/case.model'
import { DeliverResponse } from '../../models/deliver.response'
@@ -101,8 +101,16 @@ describe('InternalCaseController - Deliver indictment case to police', () => {
})
it('should update the police case', async () => {
- expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(courtRecordKey)
- expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(rulingKey)
+ expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
+ caseType,
+ caseState,
+ courtRecordKey,
+ )
+ expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
+ caseType,
+ caseState,
+ rulingKey,
+ )
expect(mockPoliceService.updatePoliceCase).toHaveBeenCalledWith(
user,
caseId,
@@ -115,11 +123,11 @@ describe('InternalCaseController - Deliver indictment case to police', () => {
'',
[
{
- type: CourtDocumentType.RVTB,
+ type: PoliceDocumentType.RVTB,
courtDocument: Base64.btoa(courtRecordPdf),
},
{
- type: CourtDocumentType.RVDO,
+ type: PoliceDocumentType.RVDO,
courtDocument: Base64.btoa(rulingPdf),
},
],
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToCourt.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToCourt.spec.ts
new file mode 100644
index 000000000000..0bcb6dcd14ba
--- /dev/null
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToCourt.spec.ts
@@ -0,0 +1,166 @@
+import { uuid } from 'uuidv4'
+
+import {
+ CaseState,
+ CaseType,
+ IndictmentSubtype,
+ User,
+} from '@island.is/judicial-system/types'
+
+import { createTestingCaseModule } from '../createTestingCaseModule'
+
+import { createIndictment } from '../../../../formatters'
+import { AwsS3Service } from '../../../aws-s3'
+import { CourtDocumentFolder, CourtService } from '../../../court'
+import { Case } from '../../models/case.model'
+import { DeliverResponse } from '../../models/deliver.response'
+
+jest.mock('../../../../formatters/indictmentPdf')
+
+interface Then {
+ result: DeliverResponse
+ error: Error
+}
+
+type GivenWhenThen = (caseId: string, theCase: Case) => Promise
+
+describe('InternalCaseController - Deliver indictment to court', () => {
+ const user = { id: uuid() } as User
+ const caseId = uuid()
+ const policeCaseNumber = uuid()
+ const courtId = uuid()
+ const courtCaseNumber = uuid()
+ const theCase = {
+ id: caseId,
+ type: CaseType.INDICTMENT,
+ state: CaseState.COMPLETED,
+ policeCaseNumbers: [policeCaseNumber],
+ indictmentSubtypes: {
+ [policeCaseNumber]: [IndictmentSubtype.TRAFFIC_VIOLATION],
+ },
+ courtId,
+ courtCaseNumber,
+ indictmentHash: uuid(),
+ } as Case
+ const pdf = Buffer.from('test indictment')
+
+ let mockAwsS3Service: AwsS3Service
+ let mockCourtService: CourtService
+ let givenWhenThen: GivenWhenThen
+
+ beforeEach(async () => {
+ const { awsS3Service, courtService, internalCaseController } =
+ await createTestingCaseModule()
+
+ mockAwsS3Service = awsS3Service
+ const mockGetObject = mockAwsS3Service.getObject as jest.Mock
+ mockGetObject.mockRejectedValue(new Error('Some error'))
+
+ const mockCreateIndictment = createIndictment as jest.Mock
+ mockCreateIndictment.mockRejectedValue(new Error('Some error'))
+
+ mockCourtService = courtService
+ const mockCreateDocument = mockCourtService.createDocument as jest.Mock
+ mockCreateDocument.mockRejectedValue(new Error('Some error'))
+
+ givenWhenThen = async (caseId: string, theCase: Case) => {
+ const then = {} as Then
+
+ await internalCaseController
+ .deliverIndictmentToCourt(caseId, theCase, { user })
+ .then((result) => (then.result = result))
+ .catch((error) => (then.error = error))
+
+ return then
+ }
+ })
+
+ describe('deliver generated indictment pdf to court', () => {
+ let then: Then
+
+ beforeEach(async () => {
+ const mockCreateIndictment = createIndictment as jest.Mock
+ mockCreateIndictment.mockResolvedValueOnce(pdf)
+ const mockCreateDocument = mockCourtService.createDocument as jest.Mock
+ mockCreateDocument.mockResolvedValueOnce(uuid())
+
+ then = await givenWhenThen(caseId, theCase)
+ })
+
+ it('should deliver the indictment', () => {
+ expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
+ theCase.type,
+ theCase.state,
+ `${theCase.id}/indictment.pdf`,
+ )
+ expect(createIndictment).toHaveBeenCalledWith(
+ theCase,
+ expect.any(Function),
+ undefined,
+ )
+ expect(mockCourtService.createDocument).toHaveBeenCalledWith(
+ user,
+ caseId,
+ courtId,
+ courtCaseNumber,
+ CourtDocumentFolder.INDICTMENT_DOCUMENTS,
+ `Ákæra`,
+ `Ákæra.pdf`,
+ 'application/pdf',
+ pdf,
+ )
+ expect(then.result).toEqual({ delivered: true })
+ })
+ })
+
+ describe('deliver indictment pdf from AWS S3 to court', () => {
+ beforeEach(async () => {
+ const mockGetGeneratedIndictmentCaseObject =
+ mockAwsS3Service.getObject as jest.Mock
+ mockGetGeneratedIndictmentCaseObject.mockResolvedValueOnce(pdf)
+
+ await givenWhenThen(caseId, theCase)
+ })
+
+ it('should use the AWS S3 pdf', () => {
+ expect(mockCourtService.createDocument).toHaveBeenCalledWith(
+ user,
+ caseId,
+ courtId,
+ courtCaseNumber,
+ CourtDocumentFolder.INDICTMENT_DOCUMENTS,
+ `Ákæra`,
+ `Ákæra.pdf`,
+ 'application/pdf',
+ pdf,
+ )
+ })
+ })
+
+ describe('delivery to court fails', () => {
+ let then: Then
+
+ beforeEach(async () => {
+ const mockCreateIndictment = createIndictment as jest.Mock
+ mockCreateIndictment.mockResolvedValueOnce(pdf)
+
+ then = await givenWhenThen(caseId, theCase)
+ })
+
+ it('should return a failure response', async () => {
+ expect(then.result.delivered).toEqual(false)
+ })
+ })
+
+ describe('pdf generation fails', () => {
+ let then: Then
+
+ beforeEach(async () => {
+ then = await givenWhenThen(caseId, theCase)
+ })
+
+ it('should return a failure response', async () => {
+ expect(then.result.delivered).toEqual(false)
+ })
+ })
+})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToCourtGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToCourtGuards.spec.ts
new file mode 100644
index 000000000000..897190bc36a4
--- /dev/null
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToCourtGuards.spec.ts
@@ -0,0 +1,26 @@
+import { indictmentCases } 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 indictment to court guards', () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let guards: any[]
+
+ beforeEach(() => {
+ guards = Reflect.getMetadata(
+ '__guards__',
+ InternalCaseController.prototype.deliverIndictmentToCourt,
+ )
+ })
+
+ 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: indictmentCases,
+ })
+ })
+})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToPolice.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToPolice.spec.ts
index 6eab6494afed..a9bf45ec1fb2 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToPolice.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToPolice.spec.ts
@@ -6,6 +6,7 @@ import {
CaseOrigin,
CaseState,
CaseType,
+ IndictmentSubtype,
User,
} from '@island.is/judicial-system/types'
@@ -15,7 +16,8 @@ import { nowFactory } from '../../../../factories'
import { createIndictment } from '../../../../formatters'
import { randomDate } from '../../../../test'
import { AwsS3Service } from '../../../aws-s3'
-import { CourtDocumentType, PoliceService } from '../../../police'
+import { FileService } from '../../../file'
+import { PoliceDocumentType, PoliceService } from '../../../police'
import { Case } from '../../models/case.model'
import { DeliverResponse } from '../../models/deliver.response'
@@ -35,20 +37,24 @@ describe('InternalCaseController - Deliver indictment to police', () => {
const user = { id: userId } as User
let mockAwsS3Service: AwsS3Service
+ let mockFileService: FileService
let mockPoliceService: PoliceService
let givenWhenThen: GivenWhenThen
beforeEach(async () => {
- const { awsS3Service, policeService, internalCaseController } =
+ const { awsS3Service, fileService, policeService, internalCaseController } =
await createTestingCaseModule()
mockAwsS3Service = awsS3Service
+ mockFileService = fileService
mockPoliceService = policeService
const mockToday = nowFactory as jest.Mock
mockToday.mockReturnValueOnce(date)
- const mockGetObject = awsS3Service.getObject as jest.Mock
+ const mockGetObject = mockAwsS3Service.getObject as jest.Mock
mockGetObject.mockRejectedValue(new Error('Some error'))
+ const mockGetCaseFileFromS3 = mockFileService.getCaseFileFromS3 as jest.Mock
+ mockGetCaseFileFromS3.mockRejectedValue(new Error('Some error'))
const mockCreateIndictment = createIndictment as jest.Mock
mockCreateIndictment.mockRejectedValue(new Error('Some error'))
const mockUpdatePoliceCase = mockPoliceService.updatePoliceCase as jest.Mock
@@ -71,12 +77,17 @@ describe('InternalCaseController - Deliver indictment to police', () => {
describe('deliver indictment case files to police', () => {
const caseId = uuid()
const caseType = CaseType.INDICTMENT
- const caseState = CaseState.ACCEPTED
+ const caseState = CaseState.WAITING_FOR_CONFIRMATION
const policeCaseNumber = uuid()
const courtCaseNumber = uuid()
const defendantNationalId = '0123456789'
const indictmentKey = uuid()
const indictmentPdf = 'test indictment'
+ const caseFile = {
+ id: uuid(),
+ key: indictmentKey,
+ category: CaseFileCategory.INDICTMENT,
+ }
const theCase = {
id: caseId,
origin: CaseOrigin.LOKE,
@@ -85,16 +96,15 @@ describe('InternalCaseController - Deliver indictment to police', () => {
policeCaseNumbers: [policeCaseNumber],
courtCaseNumber,
defendants: [{ nationalId: defendantNationalId }],
- caseFiles: [
- { key: indictmentKey, category: CaseFileCategory.INDICTMENT },
- ],
+ caseFiles: [caseFile],
} as Case
let then: Then
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockResolvedValueOnce(indictmentPdf)
+ const mockGetCaseFileFromS3 =
+ mockFileService.getCaseFileFromS3 as jest.Mock
+ mockGetCaseFileFromS3.mockResolvedValueOnce(indictmentPdf)
const mockUpdatePoliceCase =
mockPoliceService.updatePoliceCase as jest.Mock
mockUpdatePoliceCase.mockResolvedValueOnce(true)
@@ -103,7 +113,10 @@ describe('InternalCaseController - Deliver indictment to police', () => {
})
it('should update the police case', async () => {
- expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(indictmentKey)
+ expect(mockFileService.getCaseFileFromS3).toHaveBeenCalledWith(
+ theCase,
+ caseFile,
+ )
expect(mockPoliceService.updatePoliceCase).toHaveBeenCalledWith(
user,
caseId,
@@ -116,7 +129,7 @@ describe('InternalCaseController - Deliver indictment to police', () => {
'',
[
{
- type: CourtDocumentType.RVAS,
+ type: PoliceDocumentType.RVAS,
courtDocument: Base64.btoa(indictmentPdf),
},
],
@@ -128,7 +141,7 @@ describe('InternalCaseController - Deliver indictment to police', () => {
describe('deliver generated indictment pdf to police', () => {
const caseId = uuid()
const caseType = CaseType.INDICTMENT
- const caseState = CaseState.ACCEPTED
+ const caseState = CaseState.COMPLETED
const policeCaseNumber = uuid()
const courtCaseNumber = uuid()
const defendantNationalId = '0123456789'
@@ -141,6 +154,9 @@ describe('InternalCaseController - Deliver indictment to police', () => {
policeCaseNumbers: [policeCaseNumber],
courtCaseNumber,
defendants: [{ nationalId: defendantNationalId }],
+ indictmentSubtypes: {
+ [policeCaseNumber]: [IndictmentSubtype.TRAFFIC_VIOLATION],
+ },
} as Case
let then: Then
@@ -173,7 +189,7 @@ describe('InternalCaseController - Deliver indictment to police', () => {
'',
[
{
- type: CourtDocumentType.RVAS,
+ type: PoliceDocumentType.RVAS,
courtDocument: Base64.btoa(indictmentPdf),
},
],
@@ -181,4 +197,55 @@ describe('InternalCaseController - Deliver indictment to police', () => {
expect(then.result.delivered).toEqual(true)
})
})
+
+ describe('deliver indictment pdf from AWS S3 to police', () => {
+ const caseId = uuid()
+ const caseType = CaseType.INDICTMENT
+ const caseState = CaseState.COMPLETED
+ const policeCaseNumber = uuid()
+ const courtCaseNumber = uuid()
+ const defendantNationalId = '0123456789'
+ const indictmentPdf = 'test indictment'
+ const theCase = {
+ id: caseId,
+ origin: CaseOrigin.LOKE,
+ type: caseType,
+ state: caseState,
+ policeCaseNumbers: [policeCaseNumber],
+ courtCaseNumber,
+ defendants: [{ nationalId: defendantNationalId }],
+ indictmentSubtypes: {
+ [policeCaseNumber]: [IndictmentSubtype.TRAFFIC_VIOLATION],
+ },
+ indictmentHash: uuid(),
+ } as Case
+
+ beforeEach(async () => {
+ const mockGetGeneratedIndictmentCaseObject =
+ mockAwsS3Service.getObject as jest.Mock
+ mockGetGeneratedIndictmentCaseObject.mockResolvedValueOnce(indictmentPdf)
+
+ await givenWhenThen(caseId, theCase)
+ })
+
+ it('should update the police case', async () => {
+ expect(mockPoliceService.updatePoliceCase).toHaveBeenCalledWith(
+ user,
+ caseId,
+ caseType,
+ caseState,
+ policeCaseNumber,
+ courtCaseNumber,
+ defendantNationalId,
+ date,
+ '',
+ [
+ {
+ type: PoliceDocumentType.RVAS,
+ courtDocument: Base64.btoa(indictmentPdf),
+ },
+ ],
+ )
+ })
+ })
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToPoliceGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToPoliceGuards.spec.ts
index 3d389c82009d..454c56553c2b 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToPoliceGuards.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentToPoliceGuards.spec.ts
@@ -1,5 +1,3 @@
-import { CanActivate } from '@nestjs/common'
-
import { indictmentCases } from '@island.is/judicial-system/types'
import { CaseExistsGuard } from '../../guards/caseExists.guard'
@@ -17,34 +15,12 @@ describe('InternalCaseController - Deliver indictment to police guards', () => {
)
})
- it('should have two guards', () => {
+ it('should have the right guard configuration', () => {
expect(guards).toHaveLength(2)
- })
-
- describe('CaseExistsGuard', () => {
- let guard: CanActivate
-
- beforeEach(() => {
- guard = new guards[0]()
- })
-
- it('should have CaseExistsGuard as guard 1', () => {
- expect(guard).toBeInstanceOf(CaseExistsGuard)
- })
- })
-
- describe('CaseTypeGuard', () => {
- let guard: CanActivate
-
- beforeEach(() => {
- guard = guards[1]
- })
-
- it('should have CaseTypeGuard as guard 2', () => {
- expect(guard).toBeInstanceOf(CaseTypeGuard)
- expect(guard).toEqual({
- allowedCaseTypes: indictmentCases,
- })
+ expect(new guards[0]()).toBeInstanceOf(CaseExistsGuard)
+ expect(guards[1]).toBeInstanceOf(CaseTypeGuard)
+ expect(guards[1]).toEqual({
+ allowedCaseTypes: indictmentCases,
})
})
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedRulingToCourt.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedRulingToCourt.spec.ts
index 30df480ec2c0..b0d1b3e91c19 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedRulingToCourt.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedRulingToCourt.spec.ts
@@ -1,7 +1,7 @@
import format from 'date-fns/format'
import { uuid } from 'uuidv4'
-import { User } from '@island.is/judicial-system/types'
+import { CaseState, CaseType, User } from '@island.is/judicial-system/types'
import { createTestingCaseModule } from '../createTestingCaseModule'
@@ -38,8 +38,9 @@ describe('InternalCaseController - Deliver signed ruling to court', () => {
mockCreateDocument.mockRejectedValue(new Error('Some error'))
mockAwsS3Service = awsS3Service
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValue(new Error('Some error'))
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockRejectedValue(new Error('Some error'))
givenWhenThen = async (caseId: string, theCase: Case) => {
const then = {} as Then
@@ -56,8 +57,17 @@ describe('InternalCaseController - Deliver signed ruling to court', () => {
describe('signed ruling delivered', () => {
const caseId = uuid()
const courtId = uuid()
+ const caseType = CaseType.BODY_SEARCH
+ const caseState = CaseState.ACCEPTED
+
const courtCaseNumber = uuid()
- const theCase = { id: caseId, courtId, courtCaseNumber } as Case
+ const theCase = {
+ id: caseId,
+ type: caseType,
+ state: caseState,
+ courtId,
+ courtCaseNumber,
+ } as Case
const pdf = Buffer.from('test ruling')
const now = randomDate()
@@ -66,21 +76,20 @@ describe('InternalCaseController - Deliver signed ruling to court', () => {
beforeEach(async () => {
const mockNowFactory = nowFactory as jest.Mock
mockNowFactory.mockReturnValue(now)
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockResolvedValueOnce(pdf)
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockResolvedValueOnce(pdf)
const mockCreateDocument = mockCourtService.createDocument as jest.Mock
mockCreateDocument.mockResolvedValueOnce(uuid())
then = await givenWhenThen(caseId, theCase)
})
- it('should get the signed ruling from S3', async () => {
- expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
- `generated/${caseId}/ruling.pdf`,
+ it('should deliver the signed ruling to court', async () => {
+ expect(mockAwsS3Service.getGeneratedObject).toHaveBeenCalledWith(
+ caseType,
+ `${caseId}/ruling.pdf`,
)
- })
-
- it('should create a ruling at court', async () => {
expect(mockCourtService.createDocument).toHaveBeenCalledWith(
user,
caseId,
@@ -92,9 +101,6 @@ describe('InternalCaseController - Deliver signed ruling to court', () => {
'application/pdf',
pdf,
)
- })
-
- it('should return a success response', async () => {
expect(then.result.delivered).toEqual(true)
})
})
@@ -108,8 +114,9 @@ describe('InternalCaseController - Deliver signed ruling to court', () => {
let then: Then
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockResolvedValueOnce(pdf)
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockResolvedValueOnce(pdf)
then = await givenWhenThen(caseId, theCase)
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedRulingToPolice.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedRulingToPolice.spec.ts
index bfc2c4634e90..6085395e9d23 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedRulingToPolice.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedRulingToPolice.spec.ts
@@ -12,7 +12,7 @@ import { createTestingCaseModule } from '../createTestingCaseModule'
import { randomDate } from '../../../../test'
import { AwsS3Service } from '../../../aws-s3'
-import { CourtDocumentType, PoliceService } from '../../../police'
+import { PoliceDocumentType, PoliceService } from '../../../police'
import { Case } from '../../models/case.model'
import { DeliverResponse } from '../../models/deliver.response'
@@ -38,8 +38,8 @@ describe('InternalCaseController - Deliver signed ruling to police', () => {
mockAwsS3Service = awsS3Service
mockPoliceService = policeService
- const mockGetObject = awsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValue(new Error('Some error'))
+ const mockGetGeneratedObject = awsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockRejectedValue(new Error('Some error'))
const mockUpdatePoliceCase = mockPoliceService.updatePoliceCase as jest.Mock
mockUpdatePoliceCase.mockRejectedValue(new Error('Some error'))
@@ -80,8 +80,9 @@ describe('InternalCaseController - Deliver signed ruling to police', () => {
let then: Then
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockResolvedValueOnce(rulingPdf)
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockResolvedValueOnce(rulingPdf)
const mockUpdatePoliceCase =
mockPoliceService.updatePoliceCase as jest.Mock
mockUpdatePoliceCase.mockResolvedValueOnce(true)
@@ -90,8 +91,9 @@ describe('InternalCaseController - Deliver signed ruling to police', () => {
})
it('should update the police case', async () => {
- expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
- `generated/${caseId}/ruling.pdf`,
+ expect(mockAwsS3Service.getGeneratedObject).toHaveBeenCalledWith(
+ caseType,
+ `${caseId}/ruling.pdf`,
)
expect(mockPoliceService.updatePoliceCase).toHaveBeenCalledWith(
user,
@@ -105,7 +107,7 @@ describe('InternalCaseController - Deliver signed ruling to police', () => {
caseConclusion,
[
{
- type: CourtDocumentType.RVUR,
+ type: PoliceDocumentType.RVUR,
courtDocument: Base64.btoa(rulingPdf),
},
],
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getCaseFilesRecordPdf.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getCaseFilesRecordPdf.spec.ts
index 6690c916730c..5a8fbdb69ce0 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getCaseFilesRecordPdf.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getCaseFilesRecordPdf.spec.ts
@@ -3,7 +3,11 @@ import { uuid } from 'uuidv4'
import { BadRequestException } from '@nestjs/common'
-import { CaseFileCategory, CaseState } from '@island.is/judicial-system/types'
+import {
+ CaseFileCategory,
+ CaseState,
+ CaseType,
+} from '@island.is/judicial-system/types'
import { createTestingCaseModule } from '../createTestingCaseModule'
@@ -38,11 +42,12 @@ describe('LimitedAccessCaseController - Get case files record pdf', () => {
] as CaseFile[]
const theCase = {
id: caseId,
- state: CaseState.ACCEPTED,
+ type: CaseType.INDICTMENT,
+ state: CaseState.COMPLETED,
policeCaseNumbers: [uuid(), policeCaseNumber, uuid()],
caseFiles,
} as Case
- const pdf = uuid()
+ const pdf = Buffer.from(uuid())
const res = { end: jest.fn() } as unknown as Response
let mockawsS3Service: AwsS3Service
@@ -55,6 +60,8 @@ describe('LimitedAccessCaseController - Get case files record pdf', () => {
mockawsS3Service = awsS3Service
const mockGetObject = mockawsS3Service.getObject as jest.Mock
mockGetObject.mockRejectedValue(new Error('Some error'))
+ const mockPutObject = mockawsS3Service.putObject as jest.Mock
+ mockPutObject.mockRejectedValue(new Error('Some error'))
givenWhenThen = async (policeCaseNumber: string) => {
const then = {} as Then
@@ -83,13 +90,10 @@ describe('LimitedAccessCaseController - Get case files record pdf', () => {
})
it('should generate pdf after failing to get it from AWS S3', () => {
- expect(mockawsS3Service.getObject).toHaveBeenNthCalledWith(
- 1,
- `indictments/completed/${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
- )
- expect(mockawsS3Service.getObject).toHaveBeenNthCalledWith(
- 2,
- `indictments/${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
+ expect(mockawsS3Service.getObject).toHaveBeenCalledWith(
+ theCase.type,
+ theCase.state,
+ `${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
)
expect(createCaseFilesRecord).toHaveBeenCalledWith(
theCase,
@@ -97,28 +101,20 @@ describe('LimitedAccessCaseController - Get case files record pdf', () => {
expect.any(Array),
expect.any(Function),
)
+ expect(mockawsS3Service.putObject).toHaveBeenCalledWith(
+ theCase.type,
+ theCase.state,
+ `${caseId}/${policeCaseNumber}/caseFilesRecord.pdf`,
+ pdf.toString('binary'),
+ )
expect(res.end).toHaveBeenCalledWith(pdf)
})
})
- describe('pdf returned from AWS S3 indictment completed folder', () => {
- beforeEach(async () => {
- const mockGetObject = mockawsS3Service.getObject as jest.Mock
- mockGetObject.mockReturnValueOnce(pdf)
-
- await givenWhenThen(policeCaseNumber)
- })
-
- it('should return pdf', () => {
- expect(res.end).toHaveBeenCalledWith(pdf)
- })
- })
-
- describe('pdf returned from AWS S3 indictment folder', () => {
+ describe('pdf returned from AWS S3', () => {
beforeEach(async () => {
const mockGetObject = mockawsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some error'))
- mockGetObject.mockReturnValueOnce(pdf)
+ mockGetObject.mockResolvedValueOnce(pdf)
await givenWhenThen(policeCaseNumber)
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getCourtRecordPdf.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getCourtRecordPdf.spec.ts
index d624584fd8ea..2d642cd9d34a 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getCourtRecordPdf.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getCourtRecordPdf.spec.ts
@@ -3,7 +3,7 @@ import { uuid } from 'uuidv4'
import { Logger } from '@island.is/logging'
-import { User } from '@island.is/judicial-system/types'
+import { CaseState, CaseType, User } from '@island.is/judicial-system/types'
import { createTestingCaseModule } from '../createTestingCaseModule'
@@ -33,9 +33,16 @@ describe('LimitedAccessCaseController - Get court record pdf', () => {
beforeEach(async () => {
const { awsS3Service, logger, limitedAccessCaseController } =
await createTestingCaseModule()
+
mockAwsS3Service = awsS3Service
mockLogger = logger
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockRejectedValue(new Error('Some error'))
+ const getMock = getCourtRecordPdfAsBuffer as jest.Mock
+ getMock.mockRejectedValue(new Error('Some error'))
+
givenWhenThen = async (
caseId: string,
user: User,
@@ -59,44 +66,33 @@ describe('LimitedAccessCaseController - Get court record pdf', () => {
}
})
- describe('AWS S3 lookup', () => {
- const user = {} as User
- const caseId = uuid()
- const theCase = {
- id: caseId,
- courtRecordSignatureDate: nowFactory(),
- } as Case
- const res = {} as Response
-
- beforeEach(async () => {
- await givenWhenThen(caseId, user, theCase, res)
- })
-
- it('should lookup pdf', () => {
- expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
- `generated/${caseId}/courtRecord.pdf`,
- )
- })
- })
-
describe('AWS S3 pdf returned', () => {
const user = {} as User
const caseId = uuid()
+ const caseType = CaseType.EXPULSION_FROM_HOME
+ const caseState = CaseState.DISMISSED
const theCase = {
id: caseId,
+ type: caseType,
+ state: caseState,
courtRecordSignatureDate: nowFactory(),
} as Case
const res = { end: jest.fn() } as unknown as Response
const pdf = {}
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockResolvedValueOnce(pdf)
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockResolvedValueOnce(pdf)
await givenWhenThen(caseId, user, theCase, res)
})
it('should return pdf', () => {
+ expect(mockAwsS3Service.getGeneratedObject).toHaveBeenCalledWith(
+ caseType,
+ `${caseId}/courtRecord.pdf`,
+ )
expect(res.end).toHaveBeenCalledWith(pdf)
})
})
@@ -109,12 +105,9 @@ describe('LimitedAccessCaseController - Get court record pdf', () => {
courtRecordSignatureDate: nowFactory(),
} as Case
const res = {} as Response
- const error = new Error('Some ignored error')
+ const error = new Error('Some error')
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(error)
-
await givenWhenThen(caseId, user, theCase, res)
})
@@ -126,31 +119,6 @@ describe('LimitedAccessCaseController - Get court record pdf', () => {
})
})
- describe('pdf generated', () => {
- const user = {} as User
- const caseId = uuid()
- const theCase = {
- id: caseId,
- courtRecordSignatureDate: nowFactory(),
- } as Case
- const res = {} as Response
-
- beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some ignored error'))
-
- await givenWhenThen(caseId, user, theCase, res)
- })
-
- it('should generate pdf', () => {
- expect(getCourtRecordPdfAsBuffer).toHaveBeenCalledWith(
- theCase,
- expect.any(Function),
- user,
- )
- })
- })
-
describe('generated pdf returned', () => {
const user = {} as User
const caseId = uuid()
@@ -162,8 +130,6 @@ describe('LimitedAccessCaseController - Get court record pdf', () => {
const pdf = {}
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some ignored error'))
const getMock = getCourtRecordPdfAsBuffer as jest.Mock
getMock.mockResolvedValueOnce(pdf)
@@ -171,6 +137,11 @@ describe('LimitedAccessCaseController - Get court record pdf', () => {
})
it('should return pdf', () => {
+ expect(getCourtRecordPdfAsBuffer).toHaveBeenCalledWith(
+ theCase,
+ expect.any(Function),
+ user,
+ )
expect(res.end).toHaveBeenCalledWith(pdf)
})
})
@@ -186,11 +157,6 @@ describe('LimitedAccessCaseController - Get court record pdf', () => {
const res = {} as Response
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some ignored error'))
- const getMock = getCourtRecordPdfAsBuffer as jest.Mock
- getMock.mockRejectedValueOnce(new Error('Some error'))
-
then = await givenWhenThen(caseId, user, theCase, res)
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getIndictmentPdf.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getIndictmentPdf.spec.ts
index 6e40f0b0e114..026b70c71758 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getIndictmentPdf.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getIndictmentPdf.spec.ts
@@ -1,9 +1,16 @@
import { Response } from 'express'
import { uuid } from 'uuidv4'
+import {
+ CaseState,
+ CaseType,
+ IndictmentSubtype,
+} from '@island.is/judicial-system/types'
+
import { createTestingCaseModule } from '../createTestingCaseModule'
import { createIndictment } from '../../../../formatters'
+import { AwsS3Service } from '../../../aws-s3'
import { Case } from '../../models/case.model'
jest.mock('../../../../formatters/indictmentPdf')
@@ -16,16 +23,30 @@ type GivenWhenThen = () => Promise
describe('LimitedCaseController - Get indictment pdf', () => {
const caseId = uuid()
+ const policeCaseNumber = uuid()
const theCase = {
id: caseId,
+ type: CaseType.INDICTMENT,
+ state: CaseState.COMPLETED,
+ policeCaseNumbers: [policeCaseNumber],
+ indictmentSubtypes: {
+ [policeCaseNumber]: [IndictmentSubtype.TRAFFIC_VIOLATION],
+ },
+ indictmentHash: uuid(),
} as Case
- const pdf = uuid()
+ const pdf = Buffer.from(uuid())
const res = { end: jest.fn() } as unknown as Response
+ let mockawsS3Service: AwsS3Service
let givenWhenThen: GivenWhenThen
beforeEach(async () => {
- const { limitedAccessCaseController } = await createTestingCaseModule()
+ const { awsS3Service, limitedAccessCaseController } =
+ await createTestingCaseModule()
+
+ mockawsS3Service = awsS3Service
+ const mockGetObject = mockawsS3Service.getObject as jest.Mock
+ mockGetObject.mockRejectedValue(new Error('Some error'))
givenWhenThen = async () => {
const then = {} as Then
@@ -49,6 +70,11 @@ describe('LimitedCaseController - Get indictment pdf', () => {
})
it('should generate pdf', () => {
+ expect(mockawsS3Service.getObject).toHaveBeenCalledWith(
+ theCase.type,
+ theCase.state,
+ `${caseId}/indictment.pdf`,
+ )
expect(createIndictment).toHaveBeenCalledWith(
theCase,
expect.any(Function),
@@ -57,4 +83,17 @@ describe('LimitedCaseController - Get indictment pdf', () => {
expect(res.end).toHaveBeenCalledWith(pdf)
})
})
+
+ describe('pdf returned from AWS S3', () => {
+ beforeEach(async () => {
+ const mockGetObject = mockawsS3Service.getObject as jest.Mock
+ mockGetObject.mockResolvedValueOnce(pdf)
+
+ await givenWhenThen()
+ })
+
+ it('should return pdf', () => {
+ expect(res.end).toHaveBeenCalledWith(pdf)
+ })
+ })
})
diff --git a/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getRulingPdf.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getRulingPdf.spec.ts
index 3b19de1e2299..d7e5b8e6c89c 100644
--- a/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getRulingPdf.spec.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getRulingPdf.spec.ts
@@ -3,6 +3,8 @@ import { uuid } from 'uuidv4'
import { Logger } from '@island.is/logging'
+import { CaseState, CaseType } from '@island.is/judicial-system/types'
+
import { createTestingCaseModule } from '../createTestingCaseModule'
import { nowFactory } from '../../../../factories'
@@ -30,9 +32,16 @@ describe('LimitedAccessCaseController - Get ruling pdf', () => {
beforeEach(async () => {
const { awsS3Service, logger, limitedAccessCaseController } =
await createTestingCaseModule()
+
mockAwsS3Service = awsS3Service
mockLogger = logger
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockRejectedValue(new Error('Some error'))
+ const getMock = getRulingPdfAsBuffer as jest.Mock
+ getMock.mockRejectedValue(new Error('Some error'))
+
givenWhenThen = async (caseId: string, theCase: Case, res: Response) => {
const then = {} as Then
@@ -46,36 +55,32 @@ describe('LimitedAccessCaseController - Get ruling pdf', () => {
}
})
- describe('AWS S3 lookup', () => {
- const caseId = uuid()
- const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
- const res = {} as Response
-
- beforeEach(async () => {
- await givenWhenThen(caseId, theCase, res)
- })
-
- it('should lookup pdf', () => {
- expect(mockAwsS3Service.getObject).toHaveBeenCalledWith(
- `generated/${caseId}/ruling.pdf`,
- )
- })
- })
-
describe('AWS S3 pdf returned', () => {
const caseId = uuid()
- const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
+ const caseType = CaseType.AUTOPSY
+ const caseState = CaseState.REJECTED
+ const theCase = {
+ id: caseId,
+ type: caseType,
+ state: caseState,
+ rulingSignatureDate: nowFactory(),
+ } as Case
const res = { end: jest.fn() } as unknown as Response
const pdf = {}
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockResolvedValueOnce(pdf)
+ const mockGetGeneratedObject =
+ mockAwsS3Service.getGeneratedObject as jest.Mock
+ mockGetGeneratedObject.mockResolvedValueOnce(pdf)
await givenWhenThen(caseId, theCase, res)
})
it('should return pdf', () => {
+ expect(mockAwsS3Service.getGeneratedObject).toHaveBeenCalledWith(
+ caseType,
+ `${caseId}/ruling.pdf`,
+ )
expect(res.end).toHaveBeenCalledWith(pdf)
})
})
@@ -84,12 +89,9 @@ describe('LimitedAccessCaseController - Get ruling pdf', () => {
const caseId = uuid()
const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
const res = {} as Response
- const error = new Error('Some ignored error')
+ const error = new Error('Some error')
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(error)
-
await givenWhenThen(caseId, theCase, res)
})
@@ -101,26 +103,6 @@ describe('LimitedAccessCaseController - Get ruling pdf', () => {
})
})
- describe('pdf generated', () => {
- const caseId = uuid()
- const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
- const res = {} as Response
-
- beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some ignored error'))
-
- await givenWhenThen(caseId, theCase, res)
- })
-
- it('should generate pdf', () => {
- expect(getRulingPdfAsBuffer).toHaveBeenCalledWith(
- theCase,
- expect.any(Function),
- )
- })
- })
-
describe('generated pdf returned', () => {
const caseId = uuid()
const theCase = { id: caseId, rulingSignatureDate: nowFactory() } as Case
@@ -128,8 +110,6 @@ describe('LimitedAccessCaseController - Get ruling pdf', () => {
const pdf = {}
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some ignored error'))
const getMock = getRulingPdfAsBuffer as jest.Mock
getMock.mockResolvedValueOnce(pdf)
@@ -137,6 +117,10 @@ describe('LimitedAccessCaseController - Get ruling pdf', () => {
})
it('should return pdf', () => {
+ expect(getRulingPdfAsBuffer).toHaveBeenCalledWith(
+ theCase,
+ expect.any(Function),
+ )
expect(res.end).toHaveBeenCalledWith(pdf)
})
})
@@ -148,11 +132,6 @@ describe('LimitedAccessCaseController - Get ruling pdf', () => {
const res = {} as Response
beforeEach(async () => {
- const mockGetObject = mockAwsS3Service.getObject as jest.Mock
- mockGetObject.mockRejectedValueOnce(new Error('Some ignored error'))
- const getMock = getRulingPdfAsBuffer as jest.Mock
- getMock.mockRejectedValueOnce(new Error('Some error'))
-
then = await givenWhenThen(caseId, theCase, res)
})
diff --git a/apps/judicial-system/backend/src/app/modules/event-log/eventLog.service.ts b/apps/judicial-system/backend/src/app/modules/event-log/eventLog.service.ts
index 044af37f6c89..c1ee4450f943 100644
--- a/apps/judicial-system/backend/src/app/modules/event-log/eventLog.service.ts
+++ b/apps/judicial-system/backend/src/app/modules/event-log/eventLog.service.ts
@@ -1,3 +1,4 @@
+import { Transaction } from 'sequelize/types'
import { Sequelize } from 'sequelize-typescript'
import { Inject, Injectable } from '@nestjs/common'
@@ -11,11 +12,12 @@ import { EventType } from '@island.is/judicial-system/types'
import { CreateEventLogDto } from './dto/createEventLog.dto'
import { EventLog } from './models/eventLog.model'
-const allowMultiple = [
- 'LOGIN',
- 'LOGIN_UNAUTHORIZED',
- 'LOGIN_BYPASS',
- 'LOGIN_BYPASS_UNAUTHORIZED',
+const allowMultiple: EventType[] = [
+ EventType.LOGIN,
+ EventType.LOGIN_UNAUTHORIZED,
+ EventType.LOGIN_BYPASS,
+ EventType.LOGIN_BYPASS_UNAUTHORIZED,
+ EventType.INDICTMENT_CONFIRMED,
]
@Injectable()
@@ -27,7 +29,10 @@ export class EventLogService {
private readonly logger: Logger,
) {}
- async create(event: CreateEventLogDto): Promise {
+ async create(
+ event: CreateEventLogDto,
+ transaction?: Transaction,
+ ): Promise {
const { eventType, caseId, userRole, nationalId } = event
if (!allowMultiple.includes(event.eventType)) {
@@ -45,28 +50,16 @@ export class EventLogService {
}
try {
- await this.eventLogModel.create({
- eventType,
- caseId,
- nationalId,
- userRole,
- })
+ await this.eventLogModel.create(
+ { eventType, caseId, nationalId, userRole },
+ { transaction },
+ )
} catch (error) {
// Tolerate failure but log error
this.logger.error('Failed to create event log', error)
}
}
- async findEventTypeByCaseId(eventType: EventType, caseId: string) {
- return this.eventLogModel.findOne({
- where: {
- eventType,
- caseId,
- },
- order: [['created', 'DESC']],
- })
- }
-
async loginMap(
nationalIds: string[],
): Promise
diff --git a/apps/auth-admin-web/components/Resource/forms/ApiScopeCreateForm.tsx b/apps/auth-admin-web/components/Resource/forms/ApiScopeCreateForm.tsx
index dde72376e27b..8db1effd7312 100644
--- a/apps/auth-admin-web/components/Resource/forms/ApiScopeCreateForm.tsx
+++ b/apps/auth-admin-web/components/Resource/forms/ApiScopeCreateForm.tsx
@@ -449,186 +449,16 @@ const ApiScopeCreateForm: React.FC
> = (
diff --git a/apps/services/auth/admin-api/src/app/modules/resources/resources.controller.ts b/apps/services/auth/admin-api/src/app/modules/resources/resources.controller.ts
index afa315bc7653..5cbb3c41c8af 100644
--- a/apps/services/auth/admin-api/src/app/modules/resources/resources.controller.ts
+++ b/apps/services/auth/admin-api/src/app/modules/resources/resources.controller.ts
@@ -2,7 +2,6 @@ import {
ApiResource,
ApiScope,
ApiScopeUserClaim,
- ApiScopeDTO,
IdentityResource,
IdentityResourcesDTO,
ResourcesService,
@@ -19,6 +18,7 @@ import {
PagedRowsDto,
Domain,
DomainDTO,
+ CreateApiScopeDTO,
} from '@island.is/auth-api-lib'
import { NoContentException } from '@island.is/nest/problem'
import {
@@ -434,7 +434,7 @@ export class ResourcesController {
@Audit
({
resources: (scope) => scope.name,
})
- async createApiScope(@Body() apiScope: ApiScopeDTO): Promise {
+ async createApiScope(@Body() apiScope: CreateApiScopeDTO): Promise {
return this.resourcesService.createApiScope(apiScope)
}
@@ -456,7 +456,7 @@ export class ResourcesController {
@Put('api-scope/:name')
@ApiOkResponse({ type: ApiScope })
async updateApiScope(
- @Body() apiScope: ApiScopeDTO,
+ @Body() apiScope: CreateApiScopeDTO,
@Param('name') name: string,
@CurrentUser() user: User,
): Promise {
diff --git a/libs/auth-api-lib/src/index.ts b/libs/auth-api-lib/src/index.ts
index 6360687933cd..6e320235aa40 100644
--- a/libs/auth-api-lib/src/index.ts
+++ b/libs/auth-api-lib/src/index.ts
@@ -68,6 +68,7 @@ export * from './lib/resources/models/api-resource-scope.model'
export * from './lib/resources/models/api-resource-secret.model'
export * from './lib/resources/models/api-resource-user-claim.model'
export * from './lib/resources/models/api-scope.model'
+export * from './lib/resources/dto/api-scope-create.dto'
export * from './lib/resources/models/api-scope-user-claim.model'
export * from './lib/resources/models/api-scope-delegation-type.model'
export * from './lib/resources/models/api-scope-group.model'
diff --git a/libs/auth-api-lib/src/lib/clients/dto/base/client-base.dto.ts b/libs/auth-api-lib/src/lib/clients/dto/base/client-base.dto.ts
index 0428455aef06..f6fcfd4981be 100644
--- a/libs/auth-api-lib/src/lib/clients/dto/base/client-base.dto.ts
+++ b/libs/auth-api-lib/src/lib/clients/dto/base/client-base.dto.ts
@@ -8,7 +8,7 @@ import {
} from 'class-validator'
import { ApiProperty } from '@nestjs/swagger'
-export abstract class ClientBaseDTO {
+export class ClientBaseDTO {
@IsString()
@IsNotEmpty()
@ApiProperty({
diff --git a/libs/auth-api-lib/src/lib/clients/dto/client-update.dto.ts b/libs/auth-api-lib/src/lib/clients/dto/client-update.dto.ts
index afda516eeb71..a4e01aac2936 100644
--- a/libs/auth-api-lib/src/lib/clients/dto/client-update.dto.ts
+++ b/libs/auth-api-lib/src/lib/clients/dto/client-update.dto.ts
@@ -1,3 +1,11 @@
import { ClientBaseDTO } from './base/client-base.dto'
+import { OmitType } from '@nestjs/swagger'
-export class ClientUpdateDTO extends ClientBaseDTO {}
+export class ClientUpdateDTO extends OmitType(ClientBaseDTO, [
+ 'supportedDelegationTypes',
+ 'supportsCustomDelegation',
+ 'supportsProcuringHolders',
+ 'supportsLegalGuardians',
+ 'supportsPersonalRepresentatives',
+ 'promptDelegations',
+]) {}
diff --git a/libs/auth-api-lib/src/lib/clients/dto/client.dto.ts b/libs/auth-api-lib/src/lib/clients/dto/client.dto.ts
index 69dd8b88cb80..f4b07fbc04a4 100644
--- a/libs/auth-api-lib/src/lib/clients/dto/client.dto.ts
+++ b/libs/auth-api-lib/src/lib/clients/dto/client.dto.ts
@@ -1,8 +1,15 @@
import { IsString, IsNotEmpty } from 'class-validator'
-import { ApiProperty } from '@nestjs/swagger'
+import { ApiProperty, OmitType } from '@nestjs/swagger'
import { ClientBaseDTO } from './base/client-base.dto'
-export class ClientDTO extends ClientBaseDTO {
+export class ClientDTO extends OmitType(ClientBaseDTO, [
+ 'supportedDelegationTypes',
+ 'supportsCustomDelegation',
+ 'supportsProcuringHolders',
+ 'supportsLegalGuardians',
+ 'supportsPersonalRepresentatives',
+ 'promptDelegations',
+]) {
@IsNotEmpty()
@IsString()
@ApiProperty({
diff --git a/libs/auth-api-lib/src/lib/resources/dto/api-scope-create.dto.ts b/libs/auth-api-lib/src/lib/resources/dto/api-scope-create.dto.ts
new file mode 100644
index 000000000000..a18927e7625b
--- /dev/null
+++ b/libs/auth-api-lib/src/lib/resources/dto/api-scope-create.dto.ts
@@ -0,0 +1,27 @@
+import { ApiProperty, OmitType } from '@nestjs/swagger'
+import { ApiScopeBaseDTO } from './base/api-scope-base.dto'
+import { IsNotEmpty, IsString } from 'class-validator'
+
+export class CreateApiScopeDTO extends OmitType(ApiScopeBaseDTO, [
+ 'supportedDelegationTypes',
+ 'grantToPersonalRepresentatives',
+ 'grantToProcuringHolders',
+ 'grantToLegalGuardians',
+ 'allowExplicitDelegationGrant',
+ 'grantToAuthenticatedUser',
+ 'automaticDelegationGrant',
+ 'alsoForDelegatedUser',
+]) {
+ @IsString()
+ @IsNotEmpty()
+ @ApiProperty({
+ example: 'set_display_name',
+ })
+ readonly displayName!: string
+
+ @IsString()
+ @ApiProperty({
+ example: 'set_description',
+ })
+ readonly description!: string
+}
diff --git a/libs/auth-api-lib/src/lib/resources/resources.service.ts b/libs/auth-api-lib/src/lib/resources/resources.service.ts
index a69fc8b3a18b..9a582ae7fa25 100644
--- a/libs/auth-api-lib/src/lib/resources/resources.service.ts
+++ b/libs/auth-api-lib/src/lib/resources/resources.service.ts
@@ -36,6 +36,7 @@ import { ResourceTranslationService } from './resource-translation.service'
import type { User } from '@island.is/auth-nest-tools'
import type { Logger } from '@island.is/logging'
import type { ConfigType } from '@island.is/nest/config'
+import { CreateApiScopeDTO } from './dto/api-scope-create.dto'
@Injectable()
export class ResourcesService {
constructor(
@@ -516,7 +517,7 @@ export class ResourcesService {
}
/** Creates a new Api Scope */
- async createApiScope(apiScope: ApiScopeDTO): Promise {
+ async createApiScope(apiScope: CreateApiScopeDTO): Promise {
this.logger.debug('Creating a new api scope')
await this.assertSameAsGroup(apiScope)
@@ -526,7 +527,7 @@ export class ResourcesService {
/** Updates an existing API scope */
async updateApiScope(
- apiScopeData: ApiScopeDTO,
+ apiScopeData: CreateApiScopeDTO,
name: string,
): Promise {
this.logger.debug('Updating api scope with name: ', name)
@@ -959,7 +960,7 @@ export class ResourcesService {
// #endregion Domain
- private async assertSameAsGroup(apiScope: ApiScopeDTO) {
+ private async assertSameAsGroup(apiScope: CreateApiScopeDTO) {
if (apiScope.groupId) {
const scopeGroup = await this.apiScopeGroupModel.findByPk(
apiScope.groupId,
From 3c42d8216ea081044b5f2fc9265f8d29c392f033 Mon Sep 17 00:00:00 2001
From: albinagu <47886428+albinagu@users.noreply.github.com>
Date: Fri, 31 May 2024 10:15:15 +0000
Subject: [PATCH 31/82] fix(inheritance-report): efs updates 30/5 (#15023)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
---
.../inheritance-report.service.ts | 2 +-
.../inheritance-report/utils/mappers.ts | 2 +
.../fields/Overview/OverviewAssets/index.tsx | 2 +-
.../src/forms/sections/deceased.ts | 15 ++++---
.../src/forms/sections/heirs.ts | 4 +-
.../inheritance-report/src/lib/dataSchema.ts | 43 +++++++++++++++++--
.../inheritance-report/src/lib/messages.ts | 16 ++++---
.../src/lib/utils/calculateTotalAssets.ts | 2 +-
.../src/lib/utils/helpers.ts | 10 ++---
9 files changed, 72 insertions(+), 24 deletions(-)
diff --git a/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/inheritance-report.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/inheritance-report.service.ts
index 9b73084d4509..bc0e5a2811ee 100644
--- a/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/inheritance-report.service.ts
+++ b/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/inheritance-report.service.ts
@@ -68,7 +68,7 @@ export class InheritanceReportService extends BaseTemplateApiService {
return new Promise((resolve) => {
this.syslumennService
- .getInheritanceTax(inheritanceDate)
+ .getInheritanceTax(new Date())
.then((inheritanceTax) => {
inheritanceReportInfo.inheritanceTax = inheritanceTax
resolve()
diff --git a/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts b/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts
index 4a154e52cb6b..082bb0f59e38 100644
--- a/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts
+++ b/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts
@@ -290,6 +290,8 @@ export const expandAnswers = (
answers?.customShare?.hasCustomSpouseSharePercentage ?? 'No',
customSpouseSharePercentage:
answers?.customShare?.customSpouseSharePercentage ?? '50',
+ deceasedWasMarried: answers?.customShare?.deceasedWasMarried ?? '',
+ deceasedHadAssets: answers?.customShare?.deceasedHadAssets ?? '',
},
}
}
diff --git a/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/index.tsx b/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/index.tsx
index eb45507e36e6..0f75030c6e3a 100644
--- a/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/index.tsx
+++ b/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/index.tsx
@@ -89,7 +89,7 @@ export const OverviewAssets: FC> = ({
// Bank accounts
const bankAccountsDataRow = getBankAccountsDataRow(answers)
const bankAccountsDataTotal = formatCurrency(
- String(getValueViaPath(answers, 'assets.inventory.value')) ?? '',
+ String(getValueViaPath(answers, 'assets.bankAccounts.total')) ?? '',
)
sections.push({
diff --git a/libs/application/templates/inheritance-report/src/forms/sections/deceased.ts b/libs/application/templates/inheritance-report/src/forms/sections/deceased.ts
index 1dd18ffe0fd0..e3b386ac889d 100644
--- a/libs/application/templates/inheritance-report/src/forms/sections/deceased.ts
+++ b/libs/application/templates/inheritance-report/src/forms/sections/deceased.ts
@@ -69,11 +69,13 @@ export const deceased = buildSection({
title: '',
}),
buildRadioField({
- id: 'deceasedWasMarried',
+ id: 'customShare.deceasedWasMarried',
title: m.wasInCohabitation,
largeButtons: false,
backgroundColor: 'white',
+ defaultValue: '',
width: 'half',
+ required: true,
options: [
{ value: YES, label: m.yes },
{ value: NO, label: m.no },
@@ -85,13 +87,14 @@ export const deceased = buildSection({
title: '',
}),
buildRadioField({
- id: 'deceasedHadAssets',
+ id: 'customShare.deceasedHadAssets',
title: m.hadSeparateProperty,
largeButtons: false,
backgroundColor: 'white',
width: 'half',
+ required: true,
condition: (answers) =>
- getValueViaPath(answers, 'deceasedWasMarried') === YES,
+ getValueViaPath(answers, 'customShare.deceasedWasMarried') === YES,
options: [
{ value: YES, label: m.yes },
{ value: NO, label: m.no },
@@ -108,8 +111,9 @@ export const deceased = buildSection({
largeButtons: false,
backgroundColor: 'white',
width: 'half',
+ required: true,
condition: (answers) =>
- getValueViaPath(answers, 'deceasedWasMarried') === YES,
+ getValueViaPath(answers, 'customShare.deceasedWasMarried') === YES,
options: [
{ value: YES, label: m.spouseSharePart },
{ value: NO, label: m.spouseShareFull },
@@ -126,7 +130,8 @@ export const deceased = buildSection({
getValueViaPath(
answers,
'customShare.hasCustomSpouseSharePercentage',
- ) === YES,
+ ) === YES &&
+ getValueViaPath(answers, 'customShare.deceasedWasMarried') === YES,
}),
],
}),
diff --git a/libs/application/templates/inheritance-report/src/forms/sections/heirs.ts b/libs/application/templates/inheritance-report/src/forms/sections/heirs.ts
index 1053f617fbdf..3fac8faf7869 100644
--- a/libs/application/templates/inheritance-report/src/forms/sections/heirs.ts
+++ b/libs/application/templates/inheritance-report/src/forms/sections/heirs.ts
@@ -174,7 +174,7 @@ export const heirs = buildSection({
buildFileUploadField({
id: 'heirsAdditionalInfoPrivateTransferFiles',
uploadAccept: '.pdf, .doc, .docx, .jpg, .jpeg, .png, .xls, .xlsx',
- uploadDescription: m.fileUploadFileTypes,
+ uploadDescription: m.uploadPrivateTransferDescription,
uploadMultiple: false,
title: '',
uploadHeader: '',
@@ -189,7 +189,7 @@ export const heirs = buildSection({
buildFileUploadField({
id: 'heirsAdditionalInfoFilesOtherDocuments',
uploadAccept: '.pdf, .doc, .docx, .jpg, .jpeg, .png, .xls, .xlsx',
- uploadDescription: m.fileUploadFileTypes,
+ uploadDescription: m.uploadOtherDocumentsDescription,
title: '',
uploadHeader: '',
}),
diff --git a/libs/application/templates/inheritance-report/src/lib/dataSchema.ts b/libs/application/templates/inheritance-report/src/lib/dataSchema.ts
index 3c464403435b..649d954c57c9 100644
--- a/libs/application/templates/inheritance-report/src/lib/dataSchema.ts
+++ b/libs/application/templates/inheritance-report/src/lib/dataSchema.ts
@@ -593,12 +593,48 @@ export const inheritanceReportSchema = z.object({
netPropertyForExchange: z.number(),
customShare: z
.object({
+ deceasedWasMarried: z.string().min(1),
+ deceasedHadAssets: z.string().optional(),
hasCustomSpouseSharePercentage: z.string().optional(),
customSpouseSharePercentage: z.string().optional(),
})
.refine(
- ({ hasCustomSpouseSharePercentage, customSpouseSharePercentage }) => {
- if (hasCustomSpouseSharePercentage === YES) {
+ ({ deceasedWasMarried, hasCustomSpouseSharePercentage }) => {
+ if (deceasedWasMarried === YES) {
+ return (
+ hasCustomSpouseSharePercentage &&
+ [YES, NO].includes(hasCustomSpouseSharePercentage)
+ )
+ }
+
+ return true
+ },
+ {
+ path: ['hasCustomSpouseSharePercentage'],
+ },
+ )
+ .refine(
+ ({ deceasedWasMarried, deceasedHadAssets }) => {
+ if (deceasedWasMarried === YES) {
+ return deceasedHadAssets && [YES, NO].includes(deceasedHadAssets)
+ }
+
+ return true
+ },
+ {
+ path: ['deceasedHadAssets'],
+ },
+ )
+ .refine(
+ ({
+ hasCustomSpouseSharePercentage,
+ customSpouseSharePercentage,
+ deceasedWasMarried,
+ }) => {
+ if (
+ hasCustomSpouseSharePercentage === YES &&
+ deceasedWasMarried === YES
+ ) {
const val = valueToNumber(customSpouseSharePercentage)
return val >= 50 && val <= 100
}
@@ -609,8 +645,7 @@ export const inheritanceReportSchema = z.object({
path: ['customSpouseSharePercentage'],
params: m.assetsToShareHasCustomSpousePercentageError,
},
- )
- .optional(),
+ ),
confirmAction: z.array(z.enum([YES])).length(1),
})
diff --git a/libs/application/templates/inheritance-report/src/lib/messages.ts b/libs/application/templates/inheritance-report/src/lib/messages.ts
index d5b8a29d4e2b..5a0ef2f34f1a 100644
--- a/libs/application/templates/inheritance-report/src/lib/messages.ts
+++ b/libs/application/templates/inheritance-report/src/lib/messages.ts
@@ -1399,15 +1399,21 @@ export const m = defineMessages({
defaultMessage: 'Einkaskiptagerð',
description: '',
},
- fileUploadFileTypes: {
- id: 'ir.application:fileUploadFileTypes',
+ fileUploadOtherDocuments: {
+ id: 'ir.application:fileUploadOtherDocuments',
+ defaultMessage: 'Önnur fylgigögn',
+ description: '',
+ },
+ uploadPrivateTransferDescription: {
+ id: 'ir.application:uploadPrivateTransferDescription',
defaultMessage:
'Samþykktar skráargerðir eru .pdf, .doc, .docx, .jpg, .jpeg, .png, .xls og .xlsx',
description: '',
},
- fileUploadOtherDocuments: {
- id: 'ir.application:fileUploadOtherDocuments',
- defaultMessage: 'Önnur fylgigögn',
+ uploadOtherDocumentsDescription: {
+ id: 'ir.application:uploadOtherDocumentsDescription',
+ defaultMessage:
+ 'Samþykktar skráargerðir eru .pdf, .doc, .docx, .jpg, .jpeg, .png, .xls og .xlsx',
description: '',
},
heirShare: {
diff --git a/libs/application/templates/inheritance-report/src/lib/utils/calculateTotalAssets.ts b/libs/application/templates/inheritance-report/src/lib/utils/calculateTotalAssets.ts
index 25cacfbe0c17..56887e0b0122 100644
--- a/libs/application/templates/inheritance-report/src/lib/utils/calculateTotalAssets.ts
+++ b/libs/application/templates/inheritance-report/src/lib/utils/calculateTotalAssets.ts
@@ -18,7 +18,7 @@ export const calculateTotalAssets = (answers: FormValue): number => {
const stocksTotal =
getValueViaPath(answers, 'assets.stocks.total') || 0
const otherAssetsTotal = valueToNumber(
- getValueViaPath(answers, 'assets.otherAssets.value') || 0,
+ getValueViaPath(answers, 'assets.otherAssets.total') || 0,
)
const realEstateTotal =
getValueViaPath(answers, 'assets.realEstate.total') || 0
diff --git a/libs/application/templates/inheritance-report/src/lib/utils/helpers.ts b/libs/application/templates/inheritance-report/src/lib/utils/helpers.ts
index d09229d172a0..c5b5ce8dd661 100644
--- a/libs/application/templates/inheritance-report/src/lib/utils/helpers.ts
+++ b/libs/application/templates/inheritance-report/src/lib/utils/helpers.ts
@@ -97,23 +97,23 @@ export const getDeceasedWasMarriedAndHadAssets = (
export const getDeceasedHadAssets = (application: Application): boolean =>
application?.answers &&
- getValueViaPath(application.answers, 'deceasedHadAssets') === YES
+ getValueViaPath(application.answers, 'customShare.deceasedHadAssets') === YES
export const getDeceasedWasInCohabitation = (
application: Application,
): boolean =>
application?.answers &&
- getValueViaPath(application.answers, 'deceasedWasMarried') === YES
+ getValueViaPath(application.answers, 'customShare.deceasedWasMarried') === YES
export const hasYes = (arr?: string[]) =>
Array.isArray(arr) && arr.includes(YES)
export const shouldShowDeceasedShareField = (answers: FormValue) =>
- getValueViaPath(answers, 'deceasedHadAssets') === YES &&
- getValueViaPath(answers, 'deceasedWasMarried') === YES
+ getValueViaPath(answers, 'customShare.deceasedHadAssets') === YES &&
+ getValueViaPath(answers, 'customShare.deceasedWasMarried') === YES
export const shouldShowCustomSpouseShare = (answers: FormValue) =>
- getValueViaPath(answers, 'deceasedWasMarried') === YES
+ getValueViaPath(answers, 'customShare.deceasedWasMarried') === YES
export const roundedValueToNumber = (value: unknown) =>
Math.round(valueToNumber(value))
From 1174b71dc2cc92a5d8d82ff4f6645577093d792c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B3n=20Ingi?=
<42949613+joningi98@users.noreply.github.com>
Date: Fri, 31 May 2024 11:10:38 +0000
Subject: [PATCH 32/82] feat(endorsement-confirmation): Endorsement
confirmation email (#15007)
* send email when creating list
* Update email
* Update libs/application/template-api-modules/src/lib/modules/templates/general-petition/emailGenerators/generalPetitionNotification.ts
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update libs/application/template-api-modules/src/lib/modules/templates/general-petition/emailGenerators/generalPetitionNotification.ts
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Remove test code
* Remove comment
* Fix
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
---
.../generalPetitionNotification.ts | 66 +++++++++++++++++++
.../general-petition.service.ts | 10 +++
2 files changed, 76 insertions(+)
create mode 100644 libs/application/template-api-modules/src/lib/modules/templates/general-petition/emailGenerators/generalPetitionNotification.ts
diff --git a/libs/application/template-api-modules/src/lib/modules/templates/general-petition/emailGenerators/generalPetitionNotification.ts b/libs/application/template-api-modules/src/lib/modules/templates/general-petition/emailGenerators/generalPetitionNotification.ts
new file mode 100644
index 000000000000..ce0ddee9eb1a
--- /dev/null
+++ b/libs/application/template-api-modules/src/lib/modules/templates/general-petition/emailGenerators/generalPetitionNotification.ts
@@ -0,0 +1,66 @@
+import { getValueViaPath } from '@island.is/application/core'
+import { EmailTemplateGeneratorProps } from '../../../../types'
+import { SendMailOptions } from 'nodemailer'
+
+type GeneralPetitionNotificationEmail = (
+ props: EmailTemplateGeneratorProps,
+) => SendMailOptions
+
+export const generalPetitionNotificationEmail: GeneralPetitionNotificationEmail =
+ (props) => {
+ const {
+ application,
+ options: {
+ email = { sender: 'Ísland.is', address: 'no-reply@island.is' },
+ },
+ } = props
+
+ const answers = application.answers
+ const subject = 'Staðfesting á undirskriftalista'
+ const { dateFrom, dateTil } = getValueViaPath(
+ application.answers,
+ 'dates',
+ ) as { dateFrom: string; dateTil: string }
+ const applicant = application.externalData.nationalRegistry?.data as {
+ fullName?: string
+ }
+ if (!applicant.fullName) {
+ throw new Error('Failed to get full name from national registry')
+ }
+
+ return {
+ from: {
+ name: email.sender,
+ address: email.address,
+ },
+ to: [
+ {
+ name: applicant.fullName as string,
+ address: answers.email as string,
+ },
+ ],
+ subject,
+ template: {
+ title: subject,
+ body: [
+ {
+ component: 'Heading',
+ align: 'center',
+ context: { copy: `Undirskriftalisti ${answers.listName}` },
+ },
+ {
+ component: 'Copy',
+ align: 'center',
+ context: {
+ copy: `Lýsing: ${answers.aboutList}
+ Tímabil lista: ${dateFrom} - ${dateTil}
+ Stofnandi lista: ${applicant.fullName}
+ Netfang stofnenda: ${answers.email}
+ Sími notenda: ${answers.phone}
+ Kær kveðja,
Ísland.is
`,
+ },
+ },
+ ],
+ },
+ }
+ }
diff --git a/libs/application/template-api-modules/src/lib/modules/templates/general-petition/general-petition.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/general-petition/general-petition.service.ts
index a304c342d24c..9e5bdbaa4b74 100644
--- a/libs/application/template-api-modules/src/lib/modules/templates/general-petition/general-petition.service.ts
+++ b/libs/application/template-api-modules/src/lib/modules/templates/general-petition/general-petition.service.ts
@@ -12,6 +12,7 @@ import { AuthHeaderMiddleware } from '@island.is/auth-nest-tools'
import { getValueViaPath } from '@island.is/application/core'
import { BaseTemplateApiService } from '../../base-template-api.service'
import { ApplicationTypes } from '@island.is/application/types'
+import { generalPetitionNotificationEmail } from './emailGenerators/generalPetitionNotification'
const CREATE_ENDORSEMENT_LIST_QUERY = `
mutation EndorsementSystemCreateEndorsementList($input: CreateEndorsementListDto!) {
@@ -88,6 +89,15 @@ export class GeneralPetitionService extends BaseTemplateApiService {
throw new Error('Failed to create endorsement list')
}
+ await this.sharedTemplateAPIService
+ .sendEmail(
+ (props) => generalPetitionNotificationEmail(props),
+ application,
+ )
+ .catch((error) => {
+ this.logger.error('Failed to send email', error)
+ })
+
// This gets written to externalData under the key createEndorsementList
return {
id: endorsementListResponse.data?.endorsementSystemCreateEndorsementList
From dc451322afde99e12ddb2c4cfc3315e3fa417702 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=8Dvar=20Oddsson?=
Date: Fri, 31 May 2024 11:50:05 +0000
Subject: [PATCH 33/82] feat(j-s): digital-mailbox-api. Get case by id (#14949)
* Checkpoint
* Verify token
* Remove unused code
* feat(j-s): Digital mailbox auth guard draft
* Update app.controller.ts
* feat(j-s): Passport for auth
* fix(j-s)
* Renamed app module
* Update auth.guard.ts
* audience added again
* chore(j-s): User decorator for nationalid info after jwt verification
* chore: nx format:write update dirty files
* First draft get cases
* Add models
* Checkpoint
* Send nationalId via POST
* Use type for payload
* Better error handling
* Checkpoint
* fix(j-s): config
* Use nationalId from JWT token
* Implement tags
* Fix types
* Checkpoint
* Cleanup
* Remove console.log
* Fix lint
* checkpoint
* Checkpoint
* Add defendant info
* Add data to API
* Add linkType email and tel
* Fix types
* Merge
* Cleanup
* Cleanup
* Cleanup
* Refactor
* Cleanup
* Error handling
* case number optional
* Error handling
* Fix types
* Refactoring
* 404 if case is not found
* Fix formatCase function
* Cases courtCaseNumber cannot be null
* Cases courtCaseNumber cannot be null
* Fix interface
* await res.text
* await res.text
* Fix codegen
* Fix build
* Fix build
---------
Co-authored-by: unakb
Co-authored-by: andes-it
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
---
.../modules/case/internalCase.controller.ts | 15 ++
.../app/modules/case/internalCase.service.ts | 31 ++-
.../src/app/modules/cases/case.controller.ts | 28 ++-
.../src/app/modules/cases/case.service.ts | 201 +++++++++++++++---
.../app/modules/cases/models/case.response.ts | 33 +++
.../cases/models/internalCase.response.ts | 23 ++
.../audit-trail/src/lib/auditTrail.service.ts | 3 +-
libs/judicial-system/types/src/index.ts | 3 +-
.../types/src/lib/defendant.ts | 18 ++
9 files changed, 326 insertions(+), 29 deletions(-)
create mode 100644 apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/case.response.ts
create mode 100644 apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internalCase.response.ts
diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts
index b122cb8a7630..7df5b1887435 100644
--- a/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts
@@ -85,6 +85,21 @@ export class InternalCaseController {
return this.internalCaseService.getIndictmentCases(nationalId)
}
+ @Post('cases/indictment/:caseId')
+ @ApiOkResponse({
+ type: Case,
+ description: 'Gets indictment case by id',
+ })
+ getIndictmentCase(
+ @Param('caseId') caseId: string,
+ @Body() internalCasesDto: InternalCasesDto,
+ ): Promise {
+ this.logger.debug(`Getting indictment case ${caseId}`)
+ const nationalId = formatNationalId(internalCasesDto.nationalId)
+
+ return this.internalCaseService.getIndictmentCase(caseId, nationalId)
+ }
+
@UseGuards(CaseExistsGuard)
@Post(
`case/:caseId/${messageEndpoint[MessageType.DELIVERY_TO_COURT_PROSECUTOR]}`,
diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts
index 020a22acc5e8..91f73348df61 100644
--- a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts
+++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts
@@ -10,6 +10,7 @@ import {
Inject,
Injectable,
InternalServerErrorException,
+ NotFoundException,
} from '@nestjs/common'
import { InjectConnection, InjectModel } from '@nestjs/sequelize'
@@ -49,8 +50,9 @@ import { CaseEvent, EventService } from '../event'
import { EventLogService } from '../event-log'
import { CaseFile, FileService } from '../file'
import { IndictmentCount, IndictmentCountService } from '../indictment-count'
+import { Institution } from '../institution'
import { PoliceDocument, PoliceDocumentType, PoliceService } from '../police'
-import { UserService } from '../user'
+import { User, UserService } from '../user'
import { InternalCreateCaseDto } from './dto/internalCreateCase.dto'
import { archiveFilter } from './filters/case.archiveFilter'
import { ArchiveResponse } from './models/archive.response'
@@ -1059,4 +1061,31 @@ export class InternalCaseService {
},
})
}
+
+ async getIndictmentCase(
+ caseId: string,
+ nationalId: string,
+ ): Promise {
+ const caseById = await this.caseModel.findOne({
+ include: [
+ { model: Defendant, as: 'defendants' },
+ { model: Institution, as: 'court' },
+ { model: Institution, as: 'prosecutorsOffice' },
+ { model: User, as: 'judge' },
+ { model: User, as: 'prosecutor' },
+ ],
+ attributes: ['courtCaseNumber'],
+ where: {
+ type: CaseType.INDICTMENT,
+ id: caseId,
+ '$defendants.national_id$': nationalId,
+ },
+ })
+
+ if (!caseById) {
+ throw new NotFoundException(`Case ${caseId} not found`)
+ }
+
+ return caseById
+ }
}
diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts
index b0080f9a84ce..3f6a516708d4 100644
--- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts
+++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts
@@ -1,10 +1,18 @@
-import { Controller, Get, Inject, Query, UseGuards } from '@nestjs/common'
+import {
+ Controller,
+ Get,
+ Inject,
+ Param,
+ Query,
+ UseGuards,
+} from '@nestjs/common'
import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'
import type { User } from '@island.is/auth-nest-tools'
import { CurrentUser, IdsUserGuard } from '@island.is/auth-nest-tools'
import { type Logger, LOGGER_PROVIDER } from '@island.is/logging'
+import { CaseResponse } from './models/case.response'
import { CasesResponse } from './models/cases.response'
import { CaseService } from './case.service'
@@ -26,7 +34,11 @@ export class CaseController {
}
@Get('cases')
- @ApiCreatedResponse({ type: String, description: 'Get all cases' })
+ @ApiCreatedResponse({
+ type: CasesResponse,
+ isArray: true,
+ description: 'Get all cases',
+ })
async getAllCases(
@CurrentUser() user: User,
@Query() query?: { lang: string },
@@ -35,4 +47,16 @@ export class CaseController {
return this.caseService.getCases(user.nationalId, query?.lang)
}
+
+ @Get('case/:caseId')
+ @ApiCreatedResponse({ type: CaseResponse, description: 'Get case by id' })
+ async getCase(
+ @Param('caseId') caseId: string,
+ @CurrentUser() user: User,
+ @Query() query?: { lang: string },
+ ): Promise {
+ this.logger.debug('Getting case by id')
+
+ return this.caseService.getCaseById(caseId, user.nationalId, query?.lang)
+ }
}
diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts
index 9258fe724f37..7da411be3eb3 100644
--- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts
+++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts
@@ -1,4 +1,9 @@
-import { BadGatewayException, Inject, Injectable } from '@nestjs/common'
+import {
+ BadGatewayException,
+ Inject,
+ Injectable,
+ NotFoundException,
+} from '@nestjs/common'
import { type ConfigType } from '@island.is/nest/config'
@@ -8,7 +13,9 @@ import {
} from '@island.is/judicial-system/audit-trail'
import { isCompletedCase } from '@island.is/judicial-system/types'
+import { CaseResponse } from './models/case.response'
import { CasesResponse } from './models/cases.response'
+import { InternalCaseResponse } from './models/internalCase.response'
import { InternalCasesResponse } from './models/internalCases.response'
import { caseModuleConfig } from './case.config'
@@ -49,6 +56,89 @@ export class CaseService {
})
}
+ private formatCase(res: InternalCaseResponse, lang?: string): CaseResponse {
+ const language = lang?.toLowerCase()
+ const defendant = res.defendants[0]
+
+ return {
+ data: {
+ caseNumber:
+ language === 'en'
+ ? `Case number ${res.courtCaseNumber}`
+ : `Málsnúmer ${res.courtCaseNumber}`,
+ groups: [
+ {
+ label: language === 'en' ? 'Defendant' : 'Varnaraðili',
+ items: [
+ [language === 'en' ? 'Name' : 'Nafn', defendant.name],
+ [
+ language === 'en' ? 'National ID' : 'Kennitala',
+ defendant.nationalId,
+ ],
+ [
+ language === 'en' ? 'Address' : 'Heimilisfang',
+ defendant.address,
+ ],
+ ].map((item) => ({
+ label: item[0] ?? '',
+ value: item[1] ?? (language === 'en' ? 'N/A' : 'Ekki skráð'),
+ })),
+ },
+ {
+ label: language === 'en' ? 'Defender' : 'Verjandi',
+ items: [
+ [language === 'en' ? 'Name' : 'Nafn', defendant.defenderName],
+ [
+ language === 'en' ? 'Email' : 'Netfang',
+ defendant.defenderEmail,
+ 'email',
+ ],
+ [
+ language === 'en' ? 'Phone Nr.' : 'Símanúmer',
+ defendant.defenderPhoneNumber,
+ 'tel',
+ ],
+ ].map((item) => ({
+ label: item[0] ?? '',
+ value: item[1] ?? (language === 'en' ? 'N/A' : 'Ekki skráð'),
+ linkType: item[2] ?? undefined,
+ })),
+ },
+ {
+ label: language === 'en' ? 'Information' : 'Málsupplýsingar',
+ items: [
+ {
+ label: language === 'en' ? 'Type' : 'Tegund',
+ value: language === 'en' ? 'Indictment' : 'Ákæra',
+ },
+ {
+ label:
+ language === 'en' ? 'Case number' : 'Málsnúmer héraðsdóms',
+ value: res.courtCaseNumber,
+ },
+ {
+ label: language === 'en' ? 'Court' : 'Dómstóll',
+ value: res.court.name,
+ },
+ {
+ label: language === 'en' ? 'Judge' : 'Dómari',
+ value: res.judge.name,
+ },
+ {
+ label: language === 'en' ? 'Institution' : 'Embætti',
+ value: res.prosecutorsOffice.name,
+ },
+ {
+ label: language === 'en' ? 'Prosecutor' : 'Ákærandi',
+ value: res.prosecutor.name,
+ },
+ ],
+ },
+ ],
+ },
+ }
+ }
+
private async test(nationalId: string): Promise {
return `OK ${nationalId}`
}
@@ -61,34 +151,84 @@ export class CaseService {
nationalId: string,
lang?: string,
): Promise {
- return fetch(`${this.config.backendUrl}/api/internal/cases/indictments`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- authorization: `Bearer ${this.config.secretToken}`,
- },
- body: JSON.stringify({ nationalId }),
- })
- .then(async (res) => {
- const response = await res.json()
+ try {
+ const res = await fetch(
+ `${this.config.backendUrl}/api/internal/cases/indictments`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ authorization: `Bearer ${this.config.secretToken}`,
+ },
+ body: JSON.stringify({ nationalId }),
+ },
+ )
+ const response = await res.json()
- if (res.ok) {
- return this.format(response, lang)
- }
+ if (!res.ok) {
+ throw new BadGatewayException(
+ response?.detail ||
+ 'Unexpected error occurred while fetching all cases',
+ )
+ }
- if (res.status < 500) {
- throw new BadGatewayException(response?.detail)
- }
+ return this.format(response, lang)
+ } catch (reason) {
+ if (reason instanceof BadGatewayException) {
+ throw reason
+ }
+
+ throw new BadGatewayException(
+ `Failed to fetch all cases: ${reason.message}`,
+ )
+ }
+ }
- throw response
- })
- .catch((reason) => {
- if (reason instanceof BadGatewayException) {
- throw reason
+ private async getCase(
+ id: string,
+ nationalId: string,
+ lang?: string,
+ ): Promise {
+ try {
+ const res = await fetch(
+ `${this.config.backendUrl}/api/internal/cases/indictment/${id}`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ authorization: `Bearer ${this.config.secretToken}`,
+ },
+ body: JSON.stringify({ nationalId }),
+ },
+ )
+
+ if (!res.ok) {
+ if (res.status === 404) {
+ throw new NotFoundException(`Case ${id} not found`)
}
- throw new BadGatewayException(reason)
- })
+ const reason = await res.text()
+
+ throw new BadGatewayException(
+ reason || 'Unexpected error occurred while fetching case by ID',
+ )
+ }
+
+ const response = await res.json()
+
+ return this.formatCase(response, lang)
+ } catch (reason) {
+ if (
+ reason instanceof BadGatewayException ||
+ reason instanceof NotFoundException
+ ) {
+ throw reason
+ }
+
+ throw new BadGatewayException(
+ `Failed to fetch case by id: ${reason.message}`,
+ )
+ }
}
async getCases(nationalId: string, lang?: string): Promise {
@@ -99,4 +239,17 @@ export class CaseService {
nationalId,
)
}
+
+ async getCaseById(
+ id: string,
+ nationalId: string,
+ lang?: string,
+ ): Promise {
+ return this.auditTrailService.audit(
+ 'digital-mailbox-api',
+ AuditedAction.GET_INDICTMENT,
+ this.getCase(id, nationalId, lang),
+ () => id,
+ )
+ }
}
diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/case.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/case.response.ts
new file mode 100644
index 000000000000..921110188c86
--- /dev/null
+++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/case.response.ts
@@ -0,0 +1,33 @@
+import { ApiProperty } from '@nestjs/swagger'
+
+class IndictmentCaseData {
+ @ApiProperty({ type: String })
+ caseNumber!: string
+
+ @ApiProperty({ type: Object })
+ groups!: Groups[]
+}
+
+class Groups {
+ @ApiProperty({ type: String })
+ label!: string
+
+ @ApiProperty({ type: Object })
+ items!: Items[]
+}
+
+class Items {
+ @ApiProperty({ type: String })
+ label!: string
+
+ @ApiProperty({ type: String })
+ value?: string
+
+ @ApiProperty({ type: String })
+ linkType?: 'email' | 'tel'
+}
+
+export class CaseResponse {
+ @ApiProperty({ type: Object })
+ data!: IndictmentCaseData
+}
diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internalCase.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internalCase.response.ts
new file mode 100644
index 000000000000..a72077190c97
--- /dev/null
+++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internalCase.response.ts
@@ -0,0 +1,23 @@
+import { ApiProperty } from '@nestjs/swagger'
+
+import { Defendant, Institution, User } from '@island.is/judicial-system/types'
+
+export class InternalCaseResponse {
+ @ApiProperty({ type: String })
+ courtCaseNumber!: string
+
+ @ApiProperty({ type: Object })
+ defendants!: Defendant[]
+
+ @ApiProperty({ type: Object })
+ court!: Institution
+
+ @ApiProperty({ type: Object })
+ judge!: User
+
+ @ApiProperty({ type: Object })
+ prosecutorsOffice!: Institution
+
+ @ApiProperty({ type: Object })
+ prosecutor!: User
+}
diff --git a/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts b/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts
index cfe3b161dd05..da2e10b99704 100644
--- a/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts
+++ b/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts
@@ -13,8 +13,9 @@ import { auditTrailModuleConfig } from './auditTrail.config'
export enum AuditedAction {
LOGIN = 'LOGIN',
GET_CASES = 'GET_CASES',
- GET_INDICTMENTS = 'GET_INDICTMENTS',
GET_CASE = 'GET_CASE',
+ GET_INDICTMENTS = 'GET_INDICTMENTS',
+ GET_INDICTMENT = 'GET_INDICTMENT',
CREATE_CASE = 'CREATE_CASE',
UPDATE_CASE = 'UPDATE_CASE',
TRANSITION_CASE = 'TRANSITION_CASE',
diff --git a/libs/judicial-system/types/src/index.ts b/libs/judicial-system/types/src/index.ts
index c7b78aa76a00..4b6eb0e29d6c 100644
--- a/libs/judicial-system/types/src/index.ts
+++ b/libs/judicial-system/types/src/index.ts
@@ -1,9 +1,10 @@
export { Feature } from './lib/feature'
export { Gender } from './lib/defendant'
-
+export type { Defendant } from './lib/defendant'
export { InstitutionType } from './lib/institution'
export { NotificationType } from './lib/notification'
+export type { Institution } from './lib/institution'
export { EventType } from './lib/eventLog'
export { DateType } from './lib/dateLog'
export { CommentType } from './lib/comment'
diff --git a/libs/judicial-system/types/src/lib/defendant.ts b/libs/judicial-system/types/src/lib/defendant.ts
index a6868ce68d22..49b33c5eec6a 100644
--- a/libs/judicial-system/types/src/lib/defendant.ts
+++ b/libs/judicial-system/types/src/lib/defendant.ts
@@ -3,3 +3,21 @@ export enum Gender {
FEMALE = 'FEMALE',
OTHER = 'OTHER',
}
+
+export interface Defendant {
+ id: string
+ created: string
+ modified: string
+ caseId: string
+ defendantWaivesRightToCounsel: boolean
+ noNationalId?: boolean
+ nationalId?: string
+ name?: string
+ gender?: Gender
+ address?: string
+ citizenship?: string
+ defenderName?: string
+ defenderNationalId?: string
+ defenderEmail?: string
+ defenderPhoneNumber?: string
+}
From e9b44d9821a94782e1a2154d532ab891aad9743b Mon Sep 17 00:00:00 2001
From: albinagu <47886428+albinagu@users.noreply.github.com>
Date: Fri, 31 May 2024 12:19:32 +0000
Subject: [PATCH 34/82] fix(inheritance-report): Foreign bank account fix
(#15036)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
---
.../src/fields/ReportFieldsRepeater/index.tsx | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libs/application/templates/inheritance-report/src/fields/ReportFieldsRepeater/index.tsx b/libs/application/templates/inheritance-report/src/fields/ReportFieldsRepeater/index.tsx
index a332fe271c74..d5e5bd55ca75 100644
--- a/libs/application/templates/inheritance-report/src/fields/ReportFieldsRepeater/index.tsx
+++ b/libs/application/templates/inheritance-report/src/fields/ReportFieldsRepeater/index.tsx
@@ -47,6 +47,7 @@ type RepeaterProps = {
calcWithShareValue?: boolean
hideDeceasedShare?: boolean
skipPushRight?: boolean
+ assetKey?: string
}
}
}
@@ -56,7 +57,7 @@ const valueKeys = ['exchangeRateOrInterest', 'amount']
export const ReportFieldsRepeater: FC<
React.PropsWithChildren & RepeaterProps>
> = ({ application, field, errors }) => {
- const { answers, externalData } = application
+ const { answers } = application
const { id, props } = field
@@ -347,7 +348,7 @@ export const ReportFieldsRepeater: FC<
/>
) : field.type !== 'nationalId' &&
field.id === 'assetNumber' &&
- field.props?.assetKey === 'bankAccounts' ? (
+ props?.assetKey === 'bankAccounts' ? (
Date: Fri, 31 May 2024 13:05:50 +0000
Subject: [PATCH 35/82] fix(inheritance-report): ui fix for required additional
assets (#15039)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
---
.../inheritance-report/src/fields/AssetsRepeater/index.tsx | 5 +++--
.../inheritance-report/src/forms/sections/assets.ts | 4 ++++
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/libs/application/templates/inheritance-report/src/fields/AssetsRepeater/index.tsx b/libs/application/templates/inheritance-report/src/fields/AssetsRepeater/index.tsx
index 0ca425e5eba4..646c4baf63ab 100644
--- a/libs/application/templates/inheritance-report/src/fields/AssetsRepeater/index.tsx
+++ b/libs/application/templates/inheritance-report/src/fields/AssetsRepeater/index.tsx
@@ -274,6 +274,7 @@ const FieldComponent = ({
let content = null
const defaultProps = {
+ ...field,
id: fieldName,
name: fieldName,
format: field.format,
@@ -283,13 +284,12 @@ const FieldComponent = ({
placeholder: field.placeholder,
backgroundColor: field.color ? field.color : 'blue',
currency: field.currency,
- required: field.required,
+ required: readOnly ? false : field.required,
loading: fieldName === loadingFieldName,
suffix: field.suffix,
onChange: () => onAfterChange?.(),
error: error,
readOnly: readOnly,
- ...field,
}
switch (field.id) {
@@ -339,6 +339,7 @@ const FieldComponent = ({
onAfterChange={onAfterChange}
readOnly={readOnly}
hasError={!!error}
+ required={field.required && !readOnly}
/>
)
break
diff --git a/libs/application/templates/inheritance-report/src/forms/sections/assets.ts b/libs/application/templates/inheritance-report/src/forms/sections/assets.ts
index ee20d38ee8fb..83ddadc81142 100644
--- a/libs/application/templates/inheritance-report/src/forms/sections/assets.ts
+++ b/libs/application/templates/inheritance-report/src/forms/sections/assets.ts
@@ -56,23 +56,27 @@ export const assets = buildSection({
title: m.assetNumber,
id: 'assetNumber',
placeholder: 'F1234567',
+ required: true,
},
{
title: m.assetAddress,
id: 'description',
backgroundColor: 'white',
readOnly: true,
+ required: true,
},
{
title: m.propertyShare,
id: 'share',
type: 'number',
suffix: '%',
+ required: true,
},
{
title: m.propertyValuation,
id: 'propertyValuation',
currency: true,
+ required: true,
},
],
assetKey: 'assets',
From cae44e1a20066f46b3ad6bbca41f41e07cdcc503 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=BAnar=20Vestmann?=
<43557895+RunarVestmann@users.noreply.github.com>
Date: Fri, 31 May 2024 13:35:48 +0000
Subject: [PATCH 36/82] feat(web): Housing benefit calculator, display 5 and 6
or more household member options (#15033)
* Add two more options
* Add feature flag
---------
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
---
.../HousingBenefitCalculator.tsx | 79 +++++++++----------
1 file changed, 36 insertions(+), 43 deletions(-)
diff --git a/apps/web/components/connected/HousingBenefitCalculator/HousingBenefitCalculator.tsx b/apps/web/components/connected/HousingBenefitCalculator/HousingBenefitCalculator.tsx
index 1f00070b8afb..3217c6dde785 100644
--- a/apps/web/components/connected/HousingBenefitCalculator/HousingBenefitCalculator.tsx
+++ b/apps/web/components/connected/HousingBenefitCalculator/HousingBenefitCalculator.tsx
@@ -6,8 +6,8 @@ import {
AlertMessage,
Box,
Button,
- Inline,
- RadioButton,
+ Option,
+ Select,
Stack,
Text,
} from '@island.is/island-ui/core'
@@ -40,7 +40,7 @@ const HousingBenefitCalculator = ({ slice }: HousingBenefitCalculatorProps) => {
income: '',
housingCost: '',
assets: '',
- householdMemberCount: 0,
+ householdMemberCount: 1,
})
const [data, setData] = useState()
const updateInputState = (key: keyof InputState, value: string | number) => {
@@ -97,13 +97,31 @@ const HousingBenefitCalculator = ({ slice }: HousingBenefitCalculatorProps) => {
householdMemberCount === 1 ||
householdMemberCount === 2 ||
householdMemberCount === 3 ||
- householdMemberCount === 4
+ householdMemberCount === 4 ||
+ householdMemberCount === 5 ||
+ householdMemberCount === 6
)
}
return Boolean(inputState[key as keyof InputState])
})
}, [inputState])
+ const householdMemberOptions = useMemo