diff --git a/projects/court-case-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/CourtCaseNoteGenerator.kt b/projects/court-case-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/CourtCaseNoteGenerator.kt index d11005ed18..697b61fdf7 100644 --- a/projects/court-case-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/CourtCaseNoteGenerator.kt +++ b/projects/court-case-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/CourtCaseNoteGenerator.kt @@ -1,6 +1,7 @@ package uk.gov.justice.digital.hmpps.data.generator import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.CaseNote +import java.time.LocalDate import java.time.ZonedDateTime object CourtCaseNoteGenerator { @@ -10,8 +11,8 @@ object CourtCaseNoteGenerator { PersonGenerator.CURRENTLY_MANAGED.id, ContactTypeGenerator.CONTACT_TYPE, "Existing notes", - ZonedDateTime.now(), - ZonedDateTime.now(), + LocalDate.now(), + ZonedDateTime.now().minusMinutes(1), StaffGenerator.ALLOCATED.id, StaffGenerator.ALLOCATED.id, 1, diff --git a/projects/court-case-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/CourtCaseNotesIntegrationTest.kt b/projects/court-case-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/CourtCaseNotesIntegrationTest.kt index 34469fc560..72ef369373 100644 --- a/projects/court-case-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/CourtCaseNotesIntegrationTest.kt +++ b/projects/court-case-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/CourtCaseNotesIntegrationTest.kt @@ -1,8 +1,8 @@ package uk.gov.justice.digital.hmpps import com.github.tomakehurst.wiremock.WireMockServer -import org.hamcrest.MatcherAssert -import org.hamcrest.Matchers +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo import org.junit.jupiter.api.Test import org.mockito.ArgumentMatchers.anyMap import org.mockito.kotlin.eq @@ -51,7 +51,7 @@ class CaseNotesIntegrationTest { ) val caseNote = caseNoteRepository.findByExternalReferenceAndOffenderIdAndSoftDeletedIsFalse("1111", PersonGenerator.NEW_TO_PROBATION.id) - MatcherAssert.assertThat(caseNote!!.notes, Matchers.equalTo("Some new notes about the court case.")) + assertThat(caseNote!!.notes, equalTo("Some new notes about the court case.")) verify(telemetryService).trackEvent(eq(COURT_CASE_NOTE_MERGED), anyMap(), anyMap()) } @@ -65,7 +65,7 @@ class CaseNotesIntegrationTest { ) val caseNote = caseNoteRepository.findByExternalReferenceAndOffenderIdAndSoftDeletedIsFalse("2222", PersonGenerator.CURRENTLY_MANAGED.id) - MatcherAssert.assertThat(caseNote!!.notes, Matchers.equalTo("Overwritten the existing notes about the court case.")) + assertThat(caseNote!!.notes, equalTo("Overwritten the existing notes about the court case.")) verify(telemetryService).trackEvent(eq(COURT_CASE_NOTE_MERGED), anyMap(), anyMap()) } diff --git a/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/contact/entity/CaseNote.kt b/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/contact/entity/CaseNote.kt index 71c2f5ddd2..53363700c9 100644 --- a/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/contact/entity/CaseNote.kt +++ b/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/contact/entity/CaseNote.kt @@ -18,6 +18,7 @@ import org.hibernate.type.YesNoConverter import org.springframework.data.annotation.CreatedBy import org.springframework.data.annotation.LastModifiedBy import org.springframework.data.jpa.domain.support.AuditingEntityListener +import java.time.LocalDate import java.time.ZonedDateTime @EntityListeners(AuditingEntityListener::class) @@ -43,7 +44,7 @@ class CaseNote( var notes: String, @Column(name = "contact_date") - var date: ZonedDateTime, + var date: LocalDate, @Column(name = "contact_start_time") var startTime: ZonedDateTime, diff --git a/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/DeliusIntegrationService.kt b/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/DeliusIntegrationService.kt index e1b656dd72..8a4fb60f58 100644 --- a/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/DeliusIntegrationService.kt +++ b/projects/court-case-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/service/DeliusIntegrationService.kt @@ -54,7 +54,7 @@ class DeliusIntegrationService( val current = occurredAt.truncatedTo(ChronoUnit.SECONDS) return if (last.isBefore(current)) { notes = caseNote.notes - date = occurredAt + date = occurredAt.toLocalDate() startTime = occurredAt this } else { @@ -79,7 +79,7 @@ class DeliusIntegrationService( offenderId = personId, type = caseNoteType, notes = notes, - date = occurredAt, + date = occurredAt.toLocalDate(), startTime = occurredAt, isSensitive = caseNoteType.isSensitive, probationAreaId = comDetails.probationAreaId, diff --git a/projects/create-and-vary-a-licence-and-delius/build.gradle.kts b/projects/create-and-vary-a-licence-and-delius/build.gradle.kts index 4d4d955504..8c911f308d 100644 --- a/projects/create-and-vary-a-licence-and-delius/build.gradle.kts +++ b/projects/create-and-vary-a-licence-and-delius/build.gradle.kts @@ -5,6 +5,8 @@ apply(plugin = "com.google.cloud.tools.jib") dependencies { implementation(project(":libs:audit")) implementation(project(":libs:commons")) + implementation(project(":libs:messaging")) + implementation(project(":libs:oauth-client")) implementation(project(":libs:oauth-server")) implementation("org.springframework.boot:spring-boot-starter-actuator") @@ -18,6 +20,7 @@ dependencies { implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation(libs.springdoc) implementation(libs.opentelemetry.annotations) + implementation(libs.openfeign) dev(project(":libs:dev-tools")) dev("com.unboundid:unboundid-ldapsdk") diff --git a/projects/create-and-vary-a-licence-and-delius/deploy/values-dev.yml b/projects/create-and-vary-a-licence-and-delius/deploy/values-dev.yml index 306c5c3389..de4493a0c3 100644 --- a/projects/create-and-vary-a-licence-and-delius/deploy/values-dev.yml +++ b/projects/create-and-vary-a-licence-and-delius/deploy/values-dev.yml @@ -7,6 +7,7 @@ generic-service: env: SENTRY_ENVIRONMENT: dev + SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_HMPPS-AUTH_TOKEN-URI: https://sign-in-dev.hmpps.service.justice.gov.uk/auth/oauth/token SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: https://sign-in-dev.hmpps.service.justice.gov.uk/auth/.well-known/jwks.json SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: https://sign-in-dev.hmpps.service.justice.gov.uk/auth/issuer diff --git a/projects/create-and-vary-a-licence-and-delius/deploy/values-preprod.yml b/projects/create-and-vary-a-licence-and-delius/deploy/values-preprod.yml index 56da2da81a..5520555a27 100644 --- a/projects/create-and-vary-a-licence-and-delius/deploy/values-preprod.yml +++ b/projects/create-and-vary-a-licence-and-delius/deploy/values-preprod.yml @@ -1,5 +1,3 @@ -enabled: false # TODO set this to true when you're ready to deploy your service - generic-service: ingress: host: create-and-vary-a-licence-and-delius-preprod.hmpps.service.justice.gov.uk @@ -9,6 +7,7 @@ generic-service: env: SENTRY_ENVIRONMENT: preprod + SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_HMPPS-AUTH_TOKEN-URI: https://sign-in-preprod.hmpps.service.justice.gov.uk/auth/oauth/token SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: https://sign-in-preprod.hmpps.service.justice.gov.uk/auth/.well-known/jwks.json SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: https://sign-in-preprod.hmpps.service.justice.gov.uk/auth/issuer diff --git a/projects/create-and-vary-a-licence-and-delius/deploy/values-prod.yml b/projects/create-and-vary-a-licence-and-delius/deploy/values-prod.yml index 27874e6b70..cc9a1fd2b4 100644 --- a/projects/create-and-vary-a-licence-and-delius/deploy/values-prod.yml +++ b/projects/create-and-vary-a-licence-and-delius/deploy/values-prod.yml @@ -1,10 +1,9 @@ -enabled: false # TODO set this to true when you're ready to deploy your service - generic-service: ingress: host: create-and-vary-a-licence-and-delius.hmpps.service.justice.gov.uk env: SENTRY_ENVIRONMENT: prod + SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_HMPPS-AUTH_TOKEN-URI: https://sign-in.hmpps.service.justice.gov.uk/auth/oauth/token SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: https://sign-in.hmpps.service.justice.gov.uk/auth/.well-known/jwks.json SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: https://sign-in.hmpps.service.justice.gov.uk/auth/issuer diff --git a/projects/create-and-vary-a-licence-and-delius/deploy/values.yaml b/projects/create-and-vary-a-licence-and-delius/deploy/values.yaml index f4777e8054..d54d14de15 100644 --- a/projects/create-and-vary-a-licence-and-delius/deploy/values.yaml +++ b/projects/create-and-vary-a-licence-and-delius/deploy/values.yaml @@ -19,6 +19,8 @@ generic-service: SPRING_DATASOURCE_PASSWORD: DB_PASSWORD create-and-vary-a-licence-and-delius-sentry: SENTRY_DSN: SENTRY_DSN + create-and-vary-a-licence-and-delius-queue: + MESSAGING_CONSUMER_QUEUE: QUEUE_NAME generic-prometheus-alerts: targetApplication: create-and-vary-a-licence-and-delius diff --git a/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt b/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt index 516ae6f853..307025fcf2 100644 --- a/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt +++ b/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt @@ -10,8 +10,11 @@ import org.springframework.transaction.annotation.Transactional import uk.gov.justice.digital.hmpps.data.generator.AddressGenerator import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator import uk.gov.justice.digital.hmpps.data.generator.ProviderGenerator +import uk.gov.justice.digital.hmpps.data.generator.ReferenceDataGenerator +import uk.gov.justice.digital.hmpps.data.generator.SentenceGenerator import uk.gov.justice.digital.hmpps.data.generator.StaffGenerator import uk.gov.justice.digital.hmpps.data.generator.UserGenerator +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping import uk.gov.justice.digital.hmpps.user.AuditUserRepository @Component @@ -47,13 +50,14 @@ class DataLoader( entityManager.persist(StaffGenerator.DEFAULT) entityManager.persist(StaffGenerator.DEFAULT_STAFF_USER) + entityManager.flush() entityManager.persist(PersonGenerator.DEFAULT_PERSON) entityManager.persist(PersonGenerator.DEFAULT_CM) val person = PersonGenerator.generatePerson("N123456").also(entityManager::persist) PersonGenerator.generateManager(person).also(entityManager::persist) - listOf( + entityManager.persistAll( AddressGenerator.ADDRESS_STATUS_MAIN, AddressGenerator.ADDRESS_STATUS_PREVIOUS, AddressGenerator.ADDRESS_STATUS_OTHER, @@ -61,6 +65,35 @@ class DataLoader( AddressGenerator.ADDRESS_PREVIOUS, AddressGenerator.ADDRESS_OTHER, AddressGenerator.ADDRESS_DELETED - ).forEach(entityManager::persist) + ) + + createForAddingLicenceConditions() + } + + private fun createForAddingLicenceConditions() { + entityManager.persistAll( + SentenceGenerator.SENTENCE_TYPE_SC, + ReferenceDataGenerator.DATASET_LC_SUB_CAT, + ReferenceDataGenerator.LC_STANDARD_CATEGORY, + ReferenceDataGenerator.LC_STANDARD_SUB_CATEGORY, + ReferenceDataGenerator.LC_BESPOKE_CATEGORY, + ReferenceDataGenerator.LC_BESPOKE_SUB_CATEGORY, + ReferenceDataGenerator.CONTACT_TYPE_LPOP, + PersonGenerator.PERSON_CREATE_LC, + SentenceGenerator.EVENT_CREATE_LC, + SentenceGenerator.SENTENCE_CREATE_LC, + PersonGenerator.generateManager(PersonGenerator.PERSON_CREATE_LC) + ) + entityManager.saveCvlMappings(ReferenceDataGenerator.CVL_MAPPINGS) + } + + private fun EntityManager.persistAll(vararg entities: Any) { + entities.forEach { persist(it) } + } + + private fun EntityManager.saveCvlMappings(mappings: List) { + mappings.forEach { + persistAll(it.mainCategory, it.subCategory, it) + } } } diff --git a/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt b/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt index e941bb3ffa..d0b337aa66 100644 --- a/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt +++ b/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt @@ -10,6 +10,8 @@ object PersonGenerator { val DEFAULT_PERSON = generatePerson("T123456") val DEFAULT_CM = generateManager(DEFAULT_PERSON) + val PERSON_CREATE_LC = generatePerson("L453621") + fun generatePerson( crn: String, softDeleted: Boolean = false, diff --git a/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/ReferenceDataGenerator.kt b/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/ReferenceDataGenerator.kt new file mode 100644 index 0000000000..988f0c2619 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/ReferenceDataGenerator.kt @@ -0,0 +1,51 @@ +package uk.gov.justice.digital.hmpps.data.generator + +import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.ContactType +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.Dataset +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionCategory +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.ReferenceData + +object ReferenceDataGenerator { + val DATASET_LC_SUB_CAT = generateDataset("LICENCE CONDITION SUB CATEGORY") + val LC_STANDARD_CATEGORY = generateLcCategory(CvlMapping.STANDARD_CATEGORY_CODE) + val LC_STANDARD_SUB_CATEGORY = + generateReferenceData(CvlMapping.STANDARD_SUB_CATEGORY_CODE, dataset = DATASET_LC_SUB_CAT) + val LC_BESPOKE_CATEGORY = generateLcCategory(CvlMapping.BESPOKE_CATEGORY_CODE) + val LC_BESPOKE_SUB_CATEGORY = + generateReferenceData(CvlMapping.BESPOKE_SUB_CATEGORY_CODE, dataset = DATASET_LC_SUB_CAT) + val CVL_MAPPINGS = listOf( + generateCvlMapping( + "AdditionalLcOne", + generateLcCategory("ADD1"), + generateLcSubCategory("ADD1S") + ), + generateCvlMapping( + "AdditionalLcTwo", + generateLcCategory("ADD2"), + generateLcSubCategory("ADD2S") + ) + ) + val CONTACT_TYPE_LPOP = generateContactType(ContactType.LPOP) + + fun generateLcCategory(code: String, id: Long = IdGenerator.getAndIncrement()) = LicenceConditionCategory(code, id) + fun generateDataset(code: String, id: Long = IdGenerator.getAndIncrement()) = Dataset(code, id) + fun generateReferenceData( + code: String, + description: String = "Description of $code", + dataset: Dataset, + id: Long = IdGenerator.getAndIncrement() + ) = ReferenceData(code, description, dataset.id, id) + + fun generateLcSubCategory(code: String, description: String = "LC SubCategory $code") = + generateReferenceData(code, description, DATASET_LC_SUB_CAT) + + fun generateCvlMapping( + cvlCode: String, + mainCategory: LicenceConditionCategory, + subCategory: ReferenceData, + id: Long = IdGenerator.getAndIncrement() + ) = CvlMapping(cvlCode, mainCategory, subCategory, id) + + fun generateContactType(code: String, id: Long = IdGenerator.getAndIncrement()) = ContactType(code, id) +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/SentenceGenerator.kt b/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/SentenceGenerator.kt new file mode 100644 index 0000000000..ac09098b6f --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/SentenceGenerator.kt @@ -0,0 +1,32 @@ +package uk.gov.justice.digital.hmpps.data.generator + +import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.Person +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.Disposal +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.DisposalType +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.Event + +object SentenceGenerator { + val SENTENCE_TYPE_SC = generateSentenceType("SC") + val EVENT_CREATE_LC = generateEvent("1", PersonGenerator.PERSON_CREATE_LC) + val SENTENCE_CREATE_LC = generate(EVENT_CREATE_LC) + + fun generateSentenceType(sentenceType: String, id: Long = IdGenerator.getAndIncrement()) = + DisposalType(sentenceType, id) + + fun generateEvent( + number: String, + person: Person, + disposal: Disposal? = null, + active: Boolean = true, + softDeleted: Boolean = false, + id: Long = IdGenerator.getAndIncrement() + ) = Event(number, person, disposal, active, softDeleted, id) + + fun generate( + event: Event, + type: DisposalType = SENTENCE_TYPE_SC, + active: Boolean = true, + softDeleted: Boolean = false, + id: Long = IdGenerator.getAndIncrement() + ) = Disposal(event, type, active, softDeleted, id) +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/StaffGenerator.kt b/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/StaffGenerator.kt index 6a64ca5132..2555f4706d 100644 --- a/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/StaffGenerator.kt +++ b/projects/create-and-vary-a-licence-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/StaffGenerator.kt @@ -8,7 +8,7 @@ import uk.gov.justice.digital.hmpps.set object StaffGenerator { val PDUHEAD = generateStaff("N01BDT2", "Bob", "Smith") val DEFAULT_PDUSTAFF_USER = generateStaffUser("bob-smith", PDUHEAD) - var DEFAULT = generateStaff("N01BDT1", "John", "Smith") + var DEFAULT = generateStaff("N01BDT1", "John", "Smith", teams = listOf(ProviderGenerator.DEFAULT_TEAM)) val DEFAULT_STAFF_USER = generateStaffUser("john-smith", DEFAULT) fun generateStaff( diff --git a/projects/create-and-vary-a-licence-and-delius/src/dev/resources/messages/licence-activated-L453621.json b/projects/create-and-vary-a-licence-and-delius/src/dev/resources/messages/licence-activated-L453621.json new file mode 100644 index 0000000000..2669a391bf --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/dev/resources/messages/licence-activated-L453621.json @@ -0,0 +1,15 @@ +{ + "eventType": "create-and-vary-a-licence.licence.activated", + "version": 1, + "description": "A licence has been activated", + "detailUrl": "http://localhost:{wiremock.port}/cvl/events/licence-condition-applied/df2d3748-2619-4e27-b864-946e125ffb3e", + "occurredAt": "2022-12-04T10:42:43+00:00", + "personReference": { + "identifiers": [ + { + "type": "CRN", + "value": "L453621" + } + ] + } +} \ No newline at end of file diff --git a/projects/create-and-vary-a-licence-and-delius/src/dev/resources/simulations/__files/licence-activated-L453621.json b/projects/create-and-vary-a-licence-and-delius/src/dev/resources/simulations/__files/licence-activated-L453621.json new file mode 100644 index 0000000000..d24b854b1a --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/dev/resources/simulations/__files/licence-activated-L453621.json @@ -0,0 +1,39 @@ +{ + "crn": "L453621", + "releaseDate": "2023-10-13", + "startDate": "2023-10-14", + "expiryDate": "2023-12-14", + "standardLicenceConditions": [ + { + "code": "2891a984-b8a3-4584-8ae0-a1d327226a5f", + "description": "A Standard Condition", + "pssCondition": false + }, + { + "code": "18a098f3-a4d5-4490-b623-287bab511542f", + "description": "Another Standard Condition", + "pssCondition": false + } + ], + "additionalLicenceConditions": [ + { + "code": "AdditionalLcOne", + "description": "Additional Licence Condition One", + "pssCondition": false + }, + { + "code": "AdditionalLcTwo", + "description": "Additional Licence Condition Two", + "pssCondition": true + } + ], + "bespokeLicenceConditions": [ + { + "description": "First Bespoke Condition" + }, + { + "description": "Second Bespoke Condition" + } + ], + "prisonCode": "SWI" +} \ No newline at end of file diff --git a/projects/create-and-vary-a-licence-and-delius/src/dev/resources/simulations/mappings/cvl.json b/projects/create-and-vary-a-licence-and-delius/src/dev/resources/simulations/mappings/cvl.json new file mode 100644 index 0000000000..702805183f --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/dev/resources/simulations/mappings/cvl.json @@ -0,0 +1,13 @@ +{ + "request": { + "method": "GET", + "urlPath": "/cvl/events/licence-condition-applied/df2d3748-2619-4e27-b864-946e125ffb3e" + }, + "response": { + "headers": { + "Content-Type": "application/json" + }, + "status": 200, + "bodyFileName": "licence-activated-L453621.json" + } +} \ No newline at end of file diff --git a/projects/create-and-vary-a-licence-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/LicenceActivatedIntegrationTest.kt b/projects/create-and-vary-a-licence-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/LicenceActivatedIntegrationTest.kt new file mode 100644 index 0000000000..566d12fadb --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/LicenceActivatedIntegrationTest.kt @@ -0,0 +1,141 @@ +package uk.gov.justice.digital.hmpps + +import com.github.tomakehurst.wiremock.WireMockServer +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.containsInAnyOrder +import org.hamcrest.Matchers.equalTo +import org.junit.jupiter.api.Test +import org.mockito.kotlin.verify +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import uk.gov.justice.digital.hmpps.data.generator.SentenceGenerator +import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.ContactRepository +import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.ContactType +import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.PersonManagerRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping.Companion.BESPOKE_CATEGORY_CODE +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping.Companion.STANDARD_CATEGORY_CODE +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionManagerRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionRepository +import uk.gov.justice.digital.hmpps.messaging.HmppsChannelManager +import uk.gov.justice.digital.hmpps.resourceloader.ResourceLoader +import uk.gov.justice.digital.hmpps.service.ActionResult +import uk.gov.justice.digital.hmpps.telemetry.TelemetryService +import uk.gov.justice.digital.hmpps.test.CustomMatchers.isCloseTo +import java.time.ZonedDateTime + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class LicenceActivatedIntegrationTest { + @Value("\${messaging.consumer.queue}") + lateinit var queueName: String + + @Autowired + lateinit var channelManager: HmppsChannelManager + + @Autowired + lateinit var wireMockServer: WireMockServer + + @MockBean + lateinit var telemetryService: TelemetryService + + @Autowired + lateinit var lcr: LicenceConditionRepository + + @Autowired + lateinit var lcmr: LicenceConditionManagerRepository + + @Autowired + lateinit var pmr: PersonManagerRepository + + @Autowired + lateinit var contactRepository: ContactRepository + + @Test + fun `add licence conditions`() { + val sentence = SentenceGenerator.SENTENCE_CREATE_LC + val person = sentence.event.person + + val notification = prepMessage( + ResourceLoader.event("licence-activated-L453621"), + wireMockServer.port() + ) + + channelManager.getChannel(queueName).publishAndWait(notification) + + val telemetryProperties = mapOf( + "crn" to "L453621", + "eventNumber" to "1", + "releaseDate" to "2023-10-13", + "standardConditions" to "2", + "additionalConditions" to "2", + "bespokeConditions" to "2" + ) + + verify(telemetryService).trackEvent(ActionResult.Type.StandardLicenceConditionAdded.name, telemetryProperties) + verify(telemetryService).trackEvent( + ActionResult.Type.AdditionalLicenceConditionsAdded.name, + telemetryProperties + ) + verify(telemetryService).trackEvent(ActionResult.Type.BespokeLicenceConditionAdded.name, telemetryProperties) + + val conditions = lcr.findByDisposalId(sentence.id) + assertThat(conditions.size, equalTo(4)) + + val com = pmr.findByPersonCrn(person.crn)!! + conditions.forEach { + val lcm = lcmr.findByLicenceConditionId(it.id) + assertThat(lcm?.providerId, equalTo(com.provider.id)) + assertThat(lcm?.teamId, equalTo(com.team.id)) + assertThat(lcm?.staffId, equalTo(com.staff.id)) + } + + val standard = conditions.first { it.mainCategory.code == STANDARD_CATEGORY_CODE } + assertThat( + standard.notes, + equalTo( + """ + |A Standard Condition + |Another Standard Condition + """.trimMargin() + ) + ) + + val bespoke = conditions.first { it.mainCategory.code == BESPOKE_CATEGORY_CODE } + assertThat( + bespoke.notes, + equalTo( + """ + |First Bespoke Condition + |Second Bespoke Condition + """.trimMargin() + ) + ) + + val additional = + conditions.filter { it.mainCategory.code !in listOf(STANDARD_CATEGORY_CODE, BESPOKE_CATEGORY_CODE) } + assertThat( + additional.map { it.notes }, + containsInAnyOrder("Additional Licence Condition One", "Additional Licence Condition Two") + ) + + val lpop = contactRepository.findAll().filter { it.personId == person.id && it.type.code == ContactType.LPOP } + assertThat(lpop.size, equalTo(1)) + assertThat( + lpop.first().notes, + equalTo( + """ + |Delius has been updated with licence conditions entered in the Create and Vary a licence service. + |Select the following link to navigate to Create and Vary a licence service and view the licence. + """.trimMargin() + ) + ) + val occurredAt = ZonedDateTime.parse("2022-12-04T10:42:43+00:00") + assertThat(lpop.first().date, equalTo(occurredAt.toLocalDate())) + assertThat(lpop.first().startTime!!, isCloseTo(occurredAt)) + + // send again to confirm licence conditions are not duplicated + channelManager.getChannel(queueName).publishAndWait(notification) + verify(telemetryService).trackEvent(ActionResult.Type.NoChangeToLicenceConditions.name, telemetryProperties) + } +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/config/FeignOAuth2Config.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/config/FeignOAuth2Config.kt new file mode 100644 index 0000000000..943f5334df --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/config/FeignOAuth2Config.kt @@ -0,0 +1,15 @@ +package uk.gov.justice.digital.hmpps.config + +import org.springframework.cloud.openfeign.EnableFeignClients +import org.springframework.context.annotation.Configuration +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager +import uk.gov.justice.digital.hmpps.config.feign.FeignConfig +import uk.gov.justice.digital.hmpps.integrations.cvl.CvlClient + +@Configuration +@EnableFeignClients(clients = [CvlClient::class]) +class FeignOAuth2Config( + authorizedClientManager: OAuth2AuthorizedClientManager +) : FeignConfig(authorizedClientManager) { + override fun registrationId() = "create-and-vary-a-licence-and-delius" +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/cvl/CvlClient.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/cvl/CvlClient.kt new file mode 100644 index 0000000000..dec968ef74 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/cvl/CvlClient.kt @@ -0,0 +1,51 @@ +package uk.gov.justice.digital.hmpps.integrations.cvl + +import org.springframework.cloud.openfeign.FeignClient +import org.springframework.web.bind.annotation.GetMapping +import java.net.URI +import java.time.LocalDate + +@FeignClient(name = "create-and-vary-a-licence", url = "https://dummy-url/to/be/overridden") +interface CvlClient { + @GetMapping + fun getActivatedLicence(uri: URI): ActivatedLicence? +} + +data class ActivatedLicence( + val crn: String, + val releaseDate: LocalDate, + val startDate: LocalDate?, + val endDate: LocalDate?, + val standardLicenceConditions: List, + val additionalLicenceConditions: List, + val bespokeLicenceConditions: List +) + +interface Describable { + val description: String +} + +data class StandardLicenceCondition( + val code: String, + override val description: String, + val pssCondition: Boolean +) : Describable + +data class AdditionalLicenceCondition( + val code: String, + override val description: String, + val pssCondition: Boolean +) : Describable + +data class BespokeLicenceCondition( + override val description: String +) : Describable + +fun ActivatedLicence.telemetryProperties(eventNumber: String): Map = mapOf( + "crn" to crn, + "eventNumber" to eventNumber, + "releaseDate" to releaseDate.toString(), + "standardConditions" to standardLicenceConditions.size.toString(), + "additionalConditions" to additionalLicenceConditions.size.toString(), + "bespokeConditions" to bespokeLicenceConditions.size.toString() +) diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/contact.entity/Contact.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/contact.entity/Contact.kt new file mode 100644 index 0000000000..ab4fa76898 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/contact.entity/Contact.kt @@ -0,0 +1,103 @@ +package uk.gov.justice.digital.hmpps.integrations.delius.contact.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EntityListeners +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.Lob +import jakarta.persistence.ManyToOne +import jakarta.persistence.SequenceGenerator +import jakarta.persistence.Table +import jakarta.persistence.Version +import org.hibernate.annotations.Immutable +import org.springframework.data.annotation.CreatedBy +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.LastModifiedBy +import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.jpa.domain.support.AuditingEntityListener +import org.springframework.data.jpa.repository.JpaRepository +import uk.gov.justice.digital.hmpps.exception.NotFoundException +import java.time.LocalDate +import java.time.ZonedDateTime + +@Entity +@EntityListeners(AuditingEntityListener::class) +@Table(name = "contact") +@SequenceGenerator(name = "contact_id_generator", sequenceName = "contact_id_seq", allocationSize = 1) +class Contact( + + @Column(name = "offender_id") + val personId: Long, + val eventId: Long?, + + @Column(name = "contact_date") + val date: LocalDate, + + @Column(name = "contact_start_time") + val startTime: ZonedDateTime?, + + @ManyToOne + @JoinColumn(name = "contact_type_id") + val type: ContactType, + + val teamId: Long, + val staffId: Long, + + @Lob + @Column + val notes: String?, + + @Column(columnDefinition = "number") + val softDeleted: Boolean = false, + + @Version + @Column(name = "row_version") + val version: Long = 0, + + @Id + @Column(name = "contact_id") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "contact_id_generator") + val id: Long = 0 +) { + val partitionAreaId: Long = 0 + + @CreatedDate + var createdDatetime: ZonedDateTime = ZonedDateTime.now() + + @CreatedBy + var createdByUserId: Long = 0 + + @LastModifiedDate + var lastUpdatedDatetime: ZonedDateTime = ZonedDateTime.now() + + @LastModifiedBy + var lastUpdatedUserId: Long = 0 +} + +@Immutable +@Entity +@Table(name = "r_contact_type") +class ContactType( + + val code: String, + + @Id + @Column(name = "contact_type_id") + val id: Long +) { + companion object { + val LPOP = "LPOP" + } +} + +interface ContactRepository : JpaRepository + +interface ContactTypeRepository : JpaRepository { + fun findByCode(code: String): ContactType? +} + +fun ContactTypeRepository.getByCode(code: String): ContactType = + findByCode(code) ?: throw NotFoundException("ContactType", "code", code) diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/manager/entity/PersonManager.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/manager/entity/PersonManager.kt index c1461775d0..7e65d51584 100644 --- a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/manager/entity/PersonManager.kt +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/manager/entity/PersonManager.kt @@ -10,6 +10,7 @@ import org.hibernate.annotations.Immutable import org.hibernate.annotations.Where import org.springframework.data.jpa.repository.EntityGraph import org.springframework.data.jpa.repository.JpaRepository +import uk.gov.justice.digital.hmpps.exception.NotFoundException import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.Person import uk.gov.justice.digital.hmpps.integrations.delius.provider.entity.Provider import uk.gov.justice.digital.hmpps.integrations.delius.provider.entity.Staff @@ -52,3 +53,6 @@ interface PersonManagerRepository : JpaRepository { @EntityGraph(attributePaths = ["person", "provider", "team", "staff.user"]) fun findByPersonCrn(crn: String): PersonManager? } + +fun PersonManagerRepository.getByCrn(crn: String) = + findByPersonCrn(crn) ?: throw NotFoundException("Person", "crn", crn) diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/CvlMapping.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/CvlMapping.kt new file mode 100644 index 0000000000..d64ccf5340 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/CvlMapping.kt @@ -0,0 +1,44 @@ +package uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import org.hibernate.annotations.Immutable +import org.springframework.data.jpa.repository.JpaRepository + +@Immutable +@Entity +@Table(name = "r_cvl_lic_cond_mapping") +class CvlMapping( + + val cvlCode: String, + + @ManyToOne + @JoinColumn(name = "lic_cond_type_main_cat_id") + val mainCategory: LicenceConditionCategory, + + @ManyToOne + @JoinColumn(name = "lic_cond_type_sub_cat_id") + val subCategory: ReferenceData, + + @Id + @Column(name = "cvl_lic_cond_mapping_id") + val id: Long +) { + companion object { + val STANDARD_CATEGORY_CODE = "SL1" + val STANDARD_SUB_CATEGORY_CODE = "SL1" + val BESPOKE_CATEGORY_CODE = "BESP" + val BESPOKE_SUB_CATEGORY_CODE = "NSTT9" + } +} + +interface CvlMappingRepository : JpaRepository { + fun findByCvlCode(code: String): CvlMapping? +} + +fun CvlMappingRepository.getByCvlCode(code: String) = + findByCvlCode(code) ?: throw uk.gov.justice.digital.hmpps.exception.NotFoundException("CvlMapping", "cvlCode", code) diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/Disposal.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/Disposal.kt new file mode 100644 index 0000000000..b984712922 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/Disposal.kt @@ -0,0 +1,88 @@ +package uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToOne +import jakarta.persistence.Table +import org.hibernate.annotations.Immutable +import org.hibernate.annotations.Where +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query +import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.Person + +@Immutable +@Entity +@Where(clause = "active_flag = 1 and soft_deleted = 0") +class Disposal( + + @OneToOne + @JoinColumn(name = "event_id") + val event: Event, + + @ManyToOne + @JoinColumn(name = "disposal_type_id") + val type: DisposalType, + + @Column(name = "active_flag", columnDefinition = "number") + val active: Boolean, + + @Column(columnDefinition = "number") + val softDeleted: Boolean, + + @Id + @Column(name = "disposal_id") + val id: Long +) + +@Immutable +@Entity +@Where(clause = "active_flag = 1 and soft_deleted = 0") +class Event( + + @Column(name = "event_number") + val number: String, + + @ManyToOne + @JoinColumn(name = "offender_id") + val person: Person, + + @OneToOne(mappedBy = "event") + val disposal: Disposal?, + + @Column(name = "active_flag", columnDefinition = "number") + val active: Boolean, + + @Column(columnDefinition = "number") + val softDeleted: Boolean, + + @Id + @Column(name = "event_id") + val id: Long +) + +@Immutable +@Entity +@Table(name = "r_disposal_type") +class DisposalType( + + @Column(name = "sentence_type") + val sentenceType: String, + + @Id + @Column(name = "disposal_type_id") + val id: Long +) + +interface DisposalRepository : JpaRepository { + @Query( + """ + select d from Disposal d + where d.event.person.crn = :crn + and d.type.sentenceType in ('NC', 'SC') + """ + ) + fun findCustodialSentences(crn: String): List +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/LicenceCondition.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/LicenceCondition.kt new file mode 100644 index 0000000000..8decb6cd88 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/LicenceCondition.kt @@ -0,0 +1,137 @@ +package uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EntityListeners +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.Lob +import jakarta.persistence.ManyToOne +import jakarta.persistence.SequenceGenerator +import jakarta.persistence.Table +import jakarta.persistence.Version +import org.hibernate.annotations.Where +import org.springframework.data.annotation.CreatedBy +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.LastModifiedBy +import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.jpa.domain.support.AuditingEntityListener +import org.springframework.data.jpa.repository.JpaRepository +import java.time.LocalDate +import java.time.ZonedDateTime + +@Entity +@EntityListeners(AuditingEntityListener::class) +@Table(name = "lic_condition") +@Where(clause = "active_flag = 1 and soft_deleted = 0") +@SequenceGenerator(name = "lic_condition_id_seq", sequenceName = "lic_condition_id_seq", allocationSize = 1) +class LicenceCondition( + + @Column(name = "disposal_id") + val disposalId: Long, + + val startDate: LocalDate, + + @ManyToOne + @JoinColumn(name = "lic_cond_type_main_cat_id") + val mainCategory: LicenceConditionCategory, + + @ManyToOne + @JoinColumn(name = "lic_cond_type_sub_cat_id") + val subCategory: ReferenceData, + + @Lob + @Column(name = "lic_condition_notes") + val notes: String?, + + @Column(columnDefinition = "number") + val pendingTransfer: Boolean = false, + + @Column(name = "active_flag", columnDefinition = "number") + val active: Boolean = true, + + @Column(columnDefinition = "number") + val softDeleted: Boolean = false, + + @Version + @Column(name = "row_version") + val version: Long = 0, + + @Id + @Column(name = "lic_condition_id") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "lic_condition_id_seq") + val id: Long = 0 +) { + @Column + @CreatedBy + var createdByUserId: Long = 0 + + @Column + @LastModifiedBy + var lastUpdatedUserId: Long = 0 + + @Column + @CreatedDate + var createdDatetime: ZonedDateTime = ZonedDateTime.now() + + @Column + @LastModifiedDate + var lastUpdatedDatetime: ZonedDateTime = ZonedDateTime.now() +} + +@Entity +@EntityListeners(AuditingEntityListener::class) +@Table(name = "lic_condition_manager") +@Where(clause = "active_flag = 1 and soft_deleted = 0") +@SequenceGenerator(name = "lic_condition_manager_id_seq", sequenceName = "lic_condition_manager_id_seq", allocationSize = 1) +class LicenceConditionManager( + + @Column(name = "lic_condition_id") + val licenceConditionId: Long, + + @Column(name = "probation_area_id") + val providerId: Long, + val teamId: Long, + val staffId: Long, + + @Column(name = "active_flag", columnDefinition = "number") + val active: Boolean = true, + + @Column(columnDefinition = "number") + val softDeleted: Boolean = false, + + @Version + @Column(name = "row_version") + val version: Long = 0, + + @Id + @Column(name = "lic_condition_manager_id") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "lic_condition_manager_id_seq") + val id: Long = 0 +) { + @Column + @CreatedBy + var createdByUserId: Long = 0 + + @Column + @LastModifiedBy + var lastUpdatedUserId: Long = 0 + + @Column + @CreatedDate + var createdDatetime: ZonedDateTime = ZonedDateTime.now() + + @Column + @LastModifiedDate + var lastUpdatedDatetime: ZonedDateTime = ZonedDateTime.now() +} + +interface LicenceConditionRepository : JpaRepository { + fun findByDisposalId(disposalId: Long): List +} + +interface LicenceConditionManagerRepository : JpaRepository { + fun findByLicenceConditionId(licenceConditionId: Long): LicenceConditionManager? +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/ReferenceData.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/ReferenceData.kt new file mode 100644 index 0000000000..8883d6b857 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/ReferenceData.kt @@ -0,0 +1,78 @@ +package uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.Table +import org.hibernate.annotations.Immutable +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query +import uk.gov.justice.digital.hmpps.exception.NotFoundException + +@Immutable +@Entity +@Table(name = "r_lic_cond_type_main_cat") +class LicenceConditionCategory( + val code: String, + + @Id + @Column(name = "lic_cond_type_main_cat_id") + val id: Long +) + +@Immutable +@Entity +@Table(name = "r_standard_reference_list") +class ReferenceData( + + @Column(name = "code_value") + val code: String, + + @Column(name = "code_description") + val description: String, + + @Column(name = "reference_data_master_id") + val datasetId: Long, + + @Id + @Column(name = "standard_reference_list_id") + val id: Long +) + +@Immutable +@Entity +@Table(name = "r_reference_data_master") +class Dataset( + + @Column(name = "code_set_name") + val code: String, + + @Id + @Column(name = "reference_data_master_id") + val id: Long +) + +interface LicenceConditionCategoryRepository : JpaRepository { + fun findByCode(code: String): LicenceConditionCategory? +} + +fun LicenceConditionCategoryRepository.getByCode(code: String) = + findByCode(code) ?: throw NotFoundException("LicenceConditionMainCategory", "code", code) + +interface ReferenceDataRepository : JpaRepository { + @Query( + """ + select rd from ReferenceData rd + join Dataset ds on rd.datasetId = ds.id + where ds.code = :datasetCode and rd.code = :code + """ + ) + fun findByCodeAndDatasetCode(code: String, datasetCode: String): ReferenceData? +} + +fun ReferenceDataRepository.getByCodeAndDatasetCode(code: String, datasetCode: String) = + findByCodeAndDatasetCode(code, datasetCode) + ?: throw NotFoundException("Reference Data Not Found: $datasetCode => $code") + +fun ReferenceDataRepository.getLicenceConditionSubCategory(code: String) = + getByCodeAndDatasetCode(code, "LICENCE CONDITION SUB CATEGORY") diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/ActionResult.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/ActionResult.kt new file mode 100644 index 0000000000..15cd26cbae --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/ActionResult.kt @@ -0,0 +1,16 @@ +package uk.gov.justice.digital.hmpps.service + +sealed interface ActionResult { + val properties: Map + + data class Success(val type: Type, override val properties: Map = mapOf()) : ActionResult + data class Failure(val exception: Exception, override val properties: Map = mapOf()) : ActionResult + data class Ignored(val reason: String, override val properties: Map = mapOf()) : ActionResult + + enum class Type { + StandardLicenceConditionAdded, + AdditionalLicenceConditionsAdded, + BespokeLicenceConditionAdded, + NoChangeToLicenceConditions + } +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/ContactService.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/ContactService.kt new file mode 100644 index 0000000000..a60ac30167 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/ContactService.kt @@ -0,0 +1,35 @@ +package uk.gov.justice.digital.hmpps.service + +import org.springframework.stereotype.Service +import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.Contact +import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.ContactRepository +import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.ContactType +import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.ContactTypeRepository +import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.getByCode +import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.PersonManager +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.Disposal +import java.time.ZonedDateTime + +@Service +class ContactService( + private val contactTypeRepository: ContactTypeRepository, + private val contactRepository: ContactRepository +) { + fun createContact(disposal: Disposal, com: PersonManager, startDateTime: ZonedDateTime): Contact { + return contactRepository.save( + Contact( + disposal.event.person.id, + disposal.event.id, + startDateTime.toLocalDate(), + startDateTime, + contactTypeRepository.getByCode(ContactType.LPOP), + com.team.id, + com.staff.id, + """ + |Delius has been updated with licence conditions entered in the Create and Vary a licence service. + |Select the following link to navigate to Create and Vary a licence service and view the licence. + """.trimMargin() + ) + ) + } +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/CvlHandler.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/CvlHandler.kt new file mode 100644 index 0000000000..ef92253ae2 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/CvlHandler.kt @@ -0,0 +1,49 @@ +package uk.gov.justice.digital.hmpps.service + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.stereotype.Component +import uk.gov.justice.digital.hmpps.converter.NotificationConverter +import uk.gov.justice.digital.hmpps.flags.FeatureFlags +import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent +import uk.gov.justice.digital.hmpps.message.Notification +import uk.gov.justice.digital.hmpps.messaging.NotificationHandler +import uk.gov.justice.digital.hmpps.telemetry.TelemetryService + +@Component +@ConditionalOnProperty("cvl.handler.active") +class CvlHandler( + override val converter: NotificationConverter, + private val telemetryService: TelemetryService, + private val licenceActivatedHandler: LicenceActivatedHandler, + private val featureFlags: FeatureFlags +) : NotificationHandler { + + override fun handle(notification: Notification) { + if (!featureFlags.enabled("cvl-licence-activated")) { + return + } + val results = + when (val eventType = (notification.eventType ?: notification.message.eventType).let { DomainEventType.of(it) }) { + is DomainEventType.LicenceActivated -> licenceActivatedHandler.licenceActivated(notification.message) + else -> listOf( + ActionResult.Ignored( + "UnexpectedEventType", + mapOf("eventType" to eventType.name) + ) + ) + } + + val failure = results.firstOrNull { it is ActionResult.Failure } as ActionResult.Failure? + if (failure == null) { + results.forEach { + if (it is ActionResult.Success) { + telemetryService.trackEvent(it.type.name, it.properties) + } else if (it is ActionResult.Ignored) { + telemetryService.trackEvent(it.reason, it.properties) + } + } + } else { + throw failure.exception + } + } +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/DomainEventType.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/DomainEventType.kt new file mode 100644 index 0000000000..c38bd96a28 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/DomainEventType.kt @@ -0,0 +1,18 @@ +package uk.gov.justice.digital.hmpps.service + +sealed interface DomainEventType { + val name: String + + data object LicenceActivated : DomainEventType { + override val name: String = "create-and-vary-a-licence.licence.activated" + } + + data class Other(override val name: String) : DomainEventType + + companion object { + private val types = listOf(LicenceActivated) + .associateBy { it.name } + + fun of(name: String): DomainEventType = types[name] ?: Other(name) + } +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/LicenceActivatedHandler.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/LicenceActivatedHandler.kt new file mode 100644 index 0000000000..e347e77e68 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/LicenceActivatedHandler.kt @@ -0,0 +1,28 @@ +package uk.gov.justice.digital.hmpps.service + +import org.springframework.stereotype.Component +import uk.gov.justice.digital.hmpps.exception.NotFoundException +import uk.gov.justice.digital.hmpps.integrations.cvl.CvlClient +import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent +import java.net.URI + +@Component +class LicenceActivatedHandler( + private val cvlClient: CvlClient, + private val lca: LicenceConditionApplier +) { + fun licenceActivated(domainEvent: HmppsDomainEvent): List = try { + val (crn, url) = validateEvent(domainEvent) + val activatedLicence = cvlClient.getActivatedLicence(url) + ?: throw NotFoundException("Activated Licence", "detailUrl", url) + lca.applyLicenceConditions(crn, activatedLicence, domainEvent.occurredAt) + } catch (e: Exception) { + listOf(ActionResult.Failure(e)) + } + + private fun validateEvent(domainEvent: HmppsDomainEvent): Pair { + val crn = domainEvent.personReference.findCrn() ?: throw IllegalArgumentException("No CRN Provided") + val url = domainEvent.detailUrl ?: throw IllegalArgumentException("No Detail Url Provided") + return crn to URI.create(url) + } +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/LicenceConditionApplier.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/LicenceConditionApplier.kt new file mode 100644 index 0000000000..be8be9ba1b --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/LicenceConditionApplier.kt @@ -0,0 +1,156 @@ +package uk.gov.justice.digital.hmpps.service + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import uk.gov.justice.digital.hmpps.integrations.cvl.ActivatedLicence +import uk.gov.justice.digital.hmpps.integrations.cvl.Describable +import uk.gov.justice.digital.hmpps.integrations.cvl.telemetryProperties +import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.PersonManager +import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.PersonManagerRepository +import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.getByCrn +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping.Companion.BESPOKE_CATEGORY_CODE +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping.Companion.BESPOKE_SUB_CATEGORY_CODE +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping.Companion.STANDARD_CATEGORY_CODE +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping.Companion.STANDARD_SUB_CATEGORY_CODE +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMappingRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.Disposal +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.DisposalRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceCondition +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionCategory +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionCategoryRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.ReferenceData +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.ReferenceDataRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.getByCode +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.getByCvlCode +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.getLicenceConditionSubCategory +import java.time.ZonedDateTime + +@Service +class LicenceConditionApplier( + private val disposalRepository: DisposalRepository, + private val personManagerRepository: PersonManagerRepository, + private val cvlMappingRepository: CvlMappingRepository, + private val licenceConditionCategoryRepository: LicenceConditionCategoryRepository, + private val referenceDataRepository: ReferenceDataRepository, + private val licenceConditionService: LicenceConditionService, + private val contactService: ContactService +) { + @Transactional + fun applyLicenceConditions( + crn: String, + activatedLicence: ActivatedLicence, + occurredAt: ZonedDateTime + ): List { + val com = personManagerRepository.getByCrn(crn) + return disposalRepository.findCustodialSentences(crn).ifEmpty { + throw IllegalStateException("No Custodial Sentences to apply Licence Conditions") + }.flatMap { + applyLicenceConditions( + SentencedCase(com, it, licenceConditionService.findByDisposalId(it.id)), + activatedLicence, + occurredAt + ) + } + } + + private fun applyLicenceConditions( + sentencedCase: SentencedCase, + activatedLicence: ActivatedLicence, + occurredAt: ZonedDateTime + ): List { + val standardResult = activatedLicence.groupedConditions( + sentencedCase, + licenceConditionCategoryRepository.getByCode(STANDARD_CATEGORY_CODE), + referenceDataRepository.getLicenceConditionSubCategory(STANDARD_SUB_CATEGORY_CODE), + activatedLicence.standardLicenceConditions, + ActionResult.Type.StandardLicenceConditionAdded + ) + val additionalResult = activatedLicence.additionalConditions(sentencedCase) + val bespokeResult = activatedLicence.groupedConditions( + sentencedCase, + licenceConditionCategoryRepository.getByCode(BESPOKE_CATEGORY_CODE), + referenceDataRepository.getLicenceConditionSubCategory(BESPOKE_SUB_CATEGORY_CODE), + activatedLicence.bespokeLicenceConditions, + ActionResult.Type.BespokeLicenceConditionAdded + ) + val results = listOfNotNull(standardResult, additionalResult, bespokeResult) + if (results.isNotEmpty()) { + contactService.createContact(sentencedCase.sentence, sentencedCase.com, occurredAt) + } + return results.ifEmpty { + listOf( + ActionResult.Success( + ActionResult.Type.NoChangeToLicenceConditions, + activatedLicence.telemetryProperties(sentencedCase.sentence.event.number) + ) + ) + } + } + + private fun ActivatedLicence.groupedConditions( + sentencedCase: SentencedCase, + category: LicenceConditionCategory, + subCategory: ReferenceData, + described: List, + successType: ActionResult.Type + ): ActionResult? { + return if ( + sentencedCase.licenceConditions.none { + it.mainCategory.code == category.code && it.subCategory.code == subCategory.code + } + ) { + licenceConditionService.createLicenceCondition( + sentencedCase.sentence, + releaseDate, + category, + subCategory, + described.joinToString(System.lineSeparator()) { it.description }, + sentencedCase.com + ) + ActionResult.Success( + successType, + telemetryProperties(sentencedCase.sentence.event.number) + ) + } else { + null + } + } + + private fun ActivatedLicence.additionalConditions( + sentencedCase: SentencedCase + ): ActionResult? { + val additions = additionalLicenceConditions.mapNotNull { condition -> + val cvlMapping = cvlMappingRepository.getByCvlCode(condition.code) + if ( + sentencedCase.licenceConditions.none { + it.mainCategory.code == cvlMapping.mainCategory.code && it.subCategory.code == cvlMapping.subCategory.code + } + ) { + licenceConditionService.createLicenceCondition( + sentencedCase.sentence, + releaseDate, + cvlMapping.mainCategory, + cvlMapping.subCategory, + condition.description, + sentencedCase.com + ) + } else { + null + } + } + return if (additions.isNotEmpty()) { + ActionResult.Success( + ActionResult.Type.AdditionalLicenceConditionsAdded, + telemetryProperties(sentencedCase.sentence.event.number) + ) + } else { + null + } + } +} + +class SentencedCase( + val com: PersonManager, + val sentence: Disposal, + val licenceConditions: List +) diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/LicenceConditionService.kt b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/LicenceConditionService.kt new file mode 100644 index 0000000000..5f9bdcde2c --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/LicenceConditionService.kt @@ -0,0 +1,48 @@ +package uk.gov.justice.digital.hmpps.service + +import org.springframework.stereotype.Service +import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.PersonManager +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.Disposal +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceCondition +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionCategory +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionManager +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionManagerRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.ReferenceData +import java.time.LocalDate + +@Service +class LicenceConditionService( + private val licenceConditionRepository: LicenceConditionRepository, + private val licenceConditionManagerRepository: LicenceConditionManagerRepository +) { + + fun findByDisposalId(id: Long) = licenceConditionRepository.findByDisposalId(id) + fun createLicenceCondition( + disposal: Disposal, + releaseDate: LocalDate, + category: LicenceConditionCategory, + subCategory: ReferenceData, + notes: String, + com: PersonManager + ): LicenceCondition { + val lc = licenceConditionRepository.save( + LicenceCondition( + disposal.id, + releaseDate, + category, + subCategory, + notes + ) + ) + licenceConditionManagerRepository.save( + LicenceConditionManager( + lc.id, + com.provider.id, + com.team.id, + com.staff.id + ) + ) + return lc + } +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/main/resources/application.yml b/projects/create-and-vary-a-licence-and-delius/src/main/resources/application.yml index 92bed07486..880f7ad57c 100644 --- a/projects/create-and-vary-a-licence-and-delius/src/main/resources/application.yml +++ b/projects/create-and-vary-a-licence-and-delius/src/main/resources/application.yml @@ -30,6 +30,14 @@ spring: provider: hmpps-auth: token-uri: http://localhost:${wiremock.port}/auth/oauth/token + cloud.openfeign.client.config: + default: + logger-level: full + connect-timeout: 5000 + read-timeout: 5000 + default-request-headers: + Accept: application/json + Content-Type: application/json springdoc.default-produces-media-type: application/json delius.db.username: CreateAndVaryALicenceAndDelius # Should match value in [deploy/database/access.yml]. @@ -52,6 +60,10 @@ seed.database: true wiremock.enabled: true context.initializer.classes: uk.gov.justice.digital.hmpps.wiremock.WireMockInitialiser +oauth2: + client-id: create-and-vary-a-licence-and-delius + client-secret: create-and-vary-a-licence-and-delius + logging.level: uk.gov.justice.digital.hmpps: DEBUG org.hibernate.tool.schema: ERROR @@ -61,6 +73,10 @@ logging.level: spring.config.activate.on-profile: integration-test spring.datasource.url: jdbc:h2:mem:./test;MODE=Oracle;DEFAULT_NULL_ORDERING=HIGH +messaging.consumer.queue: message-queue + +cvl.handler.active: true + --- spring.config.activate.on-profile: oracle spring.datasource.url: 'jdbc:tc:oracle:slim-faststart:///XEPDB1' diff --git a/projects/create-and-vary-a-licence-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/CvlHandlerTest.kt b/projects/create-and-vary-a-licence-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/CvlHandlerTest.kt new file mode 100644 index 0000000000..b2b2d97741 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/CvlHandlerTest.kt @@ -0,0 +1,75 @@ +package uk.gov.justice.digital.hmpps.service + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import uk.gov.justice.digital.hmpps.converter.NotificationConverter +import uk.gov.justice.digital.hmpps.flags.FeatureFlags +import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent +import uk.gov.justice.digital.hmpps.message.MessageAttributes +import uk.gov.justice.digital.hmpps.message.Notification +import uk.gov.justice.digital.hmpps.telemetry.TelemetryService + +@ExtendWith(MockitoExtension::class) +internal class CvlHandlerTest { + @Mock + internal lateinit var converter: NotificationConverter + + @Mock + internal lateinit var telemetryService: TelemetryService + + @Mock + internal lateinit var licenceActivatedHandler: LicenceActivatedHandler + + @Mock + internal lateinit var featureFlags: FeatureFlags + + @InjectMocks + internal lateinit var handler: CvlHandler + + @BeforeEach + fun setUp() { + whenever(featureFlags.enabled("cvl-licence-activated")).thenReturn(true) + } + + @Test + fun `unexpected event types are ignored`() { + val notification = Notification( + HmppsDomainEvent("unknown.type.event-type", 1), + MessageAttributes("unknown.type.event-type") + ) + + handler.handle(notification) + + verify(telemetryService).trackEvent( + "UnexpectedEventType", + mapOf("eventType" to "unknown.type.event-type"), + mapOf() + ) + } + + @Test + fun `action results with failure throw exception`() { + val notification = Notification( + HmppsDomainEvent(DomainEventType.LicenceActivated.name, 1), + MessageAttributes(DomainEventType.LicenceActivated.name) + ) + + whenever(licenceActivatedHandler.licenceActivated(any())) + .thenReturn(listOf(ActionResult.Failure(RuntimeException("Unknown Exception Happened")))) + + val ex = assertThrows { + handler.handle(notification) + } + assertThat(ex.message, equalTo("Unknown Exception Happened")) + } +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/LicenceActivatedHandlerTest.kt b/projects/create-and-vary-a-licence-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/LicenceActivatedHandlerTest.kt new file mode 100644 index 0000000000..23d9db93fe --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/LicenceActivatedHandlerTest.kt @@ -0,0 +1,73 @@ +package uk.gov.justice.digital.hmpps.service + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.instanceOf +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any +import org.mockito.kotlin.whenever +import uk.gov.justice.digital.hmpps.exception.NotFoundException +import uk.gov.justice.digital.hmpps.integrations.cvl.CvlClient +import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent +import uk.gov.justice.digital.hmpps.message.PersonIdentifier +import uk.gov.justice.digital.hmpps.message.PersonReference + +@ExtendWith(MockitoExtension::class) +internal class LicenceActivatedHandlerTest { + @Mock + internal lateinit var cvlClient: CvlClient + + @Mock + internal lateinit var lca: LicenceConditionApplier + + @InjectMocks + internal lateinit var lah: LicenceActivatedHandler + + @Test + fun `exception returned when crn not found in message`() { + val event = HmppsDomainEvent(DomainEventType.LicenceActivated.name, 1) + val res = lah.licenceActivated(event).first() + assertThat(res, instanceOf(ActionResult.Failure::class.java)) + val fail = res as ActionResult.Failure + assertThat(fail.exception, instanceOf(IllegalArgumentException::class.java)) + assertThat(fail.exception.message, equalTo("No CRN Provided")) + } + + @Test + fun `exception returned when detail url not found in message`() { + val event = HmppsDomainEvent( + DomainEventType.LicenceActivated.name, + 1, + personReference = PersonReference(listOf(PersonIdentifier("CRN", "X123456"))) + ) + val res = lah.licenceActivated(event).first() + assertThat(res, instanceOf(ActionResult.Failure::class.java)) + val fail = res as ActionResult.Failure + assertThat(fail.exception, instanceOf(IllegalArgumentException::class.java)) + assertThat(fail.exception.message, equalTo("No Detail Url Provided")) + } + + @Test + fun `exception returned when activated licence not found`() { + val event = HmppsDomainEvent( + DomainEventType.LicenceActivated.name, + 1, + detailUrl = "https://cvl.service.co.uk/licence-activated/58eb2a20-6b0e-416b-b91d-5b98b0c1be7f", + personReference = PersonReference(listOf(PersonIdentifier("CRN", "X123456"))) + ) + whenever(cvlClient.getActivatedLicence(any())).thenReturn(null) + + val res = lah.licenceActivated(event).first() + assertThat(res, instanceOf(ActionResult.Failure::class.java)) + val fail = res as ActionResult.Failure + assertThat(fail.exception, instanceOf(NotFoundException::class.java)) + assertThat( + fail.exception.message, + equalTo("Activated Licence with detailUrl of https://cvl.service.co.uk/licence-activated/58eb2a20-6b0e-416b-b91d-5b98b0c1be7f not found") + ) + } +} diff --git a/projects/create-and-vary-a-licence-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/LicenceConditionApplierTest.kt b/projects/create-and-vary-a-licence-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/LicenceConditionApplierTest.kt new file mode 100644 index 0000000000..6bf87ea726 --- /dev/null +++ b/projects/create-and-vary-a-licence-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/LicenceConditionApplierTest.kt @@ -0,0 +1,63 @@ +package uk.gov.justice.digital.hmpps.service + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.whenever +import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator +import uk.gov.justice.digital.hmpps.integrations.cvl.ActivatedLicence +import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.PersonManagerRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMappingRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.DisposalRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionCategoryRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.ReferenceDataRepository +import java.time.LocalDate +import java.time.ZonedDateTime + +@ExtendWith(MockitoExtension::class) +internal class LicenceConditionApplierTest { + @Mock + internal lateinit var disposalRepository: DisposalRepository + + @Mock + internal lateinit var personManagerRepository: PersonManagerRepository + + @Mock + internal lateinit var cvlMappingRepository: CvlMappingRepository + + @Mock + internal lateinit var licenceConditionCategoryRepository: LicenceConditionCategoryRepository + + @Mock + internal lateinit var referenceDataRepository: ReferenceDataRepository + + @Mock + internal lateinit var licenceConditionService: LicenceConditionService + + @Mock + internal lateinit var contactService: ContactService + + @InjectMocks + internal lateinit var licenceConditionApplier: LicenceConditionApplier + + @Test + fun `when no active custodial sentence an error is thrown`() { + val crn = "N678461" + whenever(personManagerRepository.findByPersonCrn(crn)).thenReturn(PersonGenerator.DEFAULT_CM) + whenever(disposalRepository.findCustodialSentences(crn)).thenReturn(listOf()) + + val ex = assertThrows { + licenceConditionApplier.applyLicenceConditions( + crn, + ActivatedLicence(crn, LocalDate.now(), null, null, listOf(), listOf(), listOf()), + ZonedDateTime.now() + ) + } + assertThat(ex.message, equalTo("No Custodial Sentences to apply Licence Conditions")) + } +}