Skip to content

Commit

Permalink
Merge pull request #2836 from ministryofjustice/APS-1800-amend-room-c…
Browse files Browse the repository at this point in the history
…haracteristics

APS-1800 Updated amend booking to change room characteristics.
  • Loading branch information
vbala-moj authored Jan 22, 2025
2 parents 955a8f3 + 94ea48d commit 8f14fd5
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,21 @@ class Cas1SpaceBookingController(

val user = userService.getUserForRequest()

val characteristics = (cas1UpdateSpaceBooking.characteristics ?: emptyList())
.map { it.value }
.let { values ->
characteristicService.getCharacteristicsByPropertyNames(values, ServiceName.approvedPremises)
}
.filter { it.isModelScopeRoom() }

ensureEntityFromCasResultIsSuccess(
cas1SpaceBookingService.updateSpaceBooking(
UpdateSpaceBookingDetails(
bookingId = bookingId,
premisesId = premisesId,
arrivalDate = cas1UpdateSpaceBooking.arrivalDate,
departureDate = cas1UpdateSpaceBooking.departureDate,
characteristics = characteristics,
updatedBy = user,
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class DomainEventDescriber(
val eventDetails = domainEvent.data.eventDetails
val previousArrival = eventDetails.previousArrivalOn
val previousDeparture = eventDetails.previousDepartureOn
val previousCharacteristics = eventDetails.previousCharacteristics
val changes = mutableListOf<String>()

fun addDateChangeMessage(previousDate: LocalDate, newDate: LocalDate, changeType: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ data class Cas1SpaceBookingEntity(
joinColumns = [JoinColumn(name = "space_booking_id")],
inverseJoinColumns = [JoinColumn(name = "characteristic_id")],
)
val criteria: MutableList<CharacteristicEntity>,
var criteria: MutableList<CharacteristicEntity>,
var nonArrivalConfirmedAt: Instant?,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "non_arrival_reason_id")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,5 @@ data class CharacteristicEntity(
fun matches(entityServiceScope: String, entityModelScope: String) = serviceMatches(entityServiceScope) && modelMatches(entityModelScope)
fun serviceMatches(entityServiceScope: String) = serviceScope == "*" || entityServiceScope == serviceScope
fun modelMatches(entityModelScope: String) = modelScope == "*" || entityModelScope == modelScope
fun isModelScopePremises() = modelMatches("premises")
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.Bo
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.BookingMadeEnvelope
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.BookingNotMade
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.BookingNotMadeEnvelope
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.Cas1SpaceCharacteristic
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.Cru
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.EventType
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.PersonReference
Expand All @@ -23,6 +24,7 @@ import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.BookingEntity
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.CancellationEntity
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.CancellationReasonEntity
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.Cas1SpaceBookingEntity
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.CharacteristicEntity
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.MetaDataName
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.PlacementRequestEntity
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.UserEntity
Expand Down Expand Up @@ -144,6 +146,7 @@ class Cas1BookingDomainEventService(
bookingChangedAt: OffsetDateTime,
previousArrivalDateIfChanged: LocalDate?,
previousDepartureDateIfChanged: LocalDate?,
previousCharacteristicsIfChanged: List<CharacteristicEntity>?,
) = bookingChanged(
BookingChangedInfo(
bookingId = booking.id,
Expand All @@ -157,6 +160,8 @@ class Cas1BookingDomainEventService(
previousArrivalDateIfChanged = previousArrivalDateIfChanged,
previousDepartureDateIfChanged = previousDepartureDateIfChanged,
isSpaceBooking = true,
characteristics = booking.criteria.toCas1SpaceCharacteristics(),
previousCharacteristics = previousCharacteristicsIfChanged?.toCas1SpaceCharacteristics(),
),
)

Expand Down Expand Up @@ -249,8 +254,10 @@ class Cas1BookingDomainEventService(
),
arrivalOn = bookingChangedInfo.arrivalDate,
departureOn = bookingChangedInfo.departureDate,
characteristics = bookingChangedInfo.characteristics,
previousArrivalOn = bookingChangedInfo.previousArrivalDateIfChanged,
previousDepartureOn = bookingChangedInfo.previousDepartureDateIfChanged,
previousCharacteristics = bookingChangedInfo.previousCharacteristics,
),
),
),
Expand Down Expand Up @@ -524,5 +531,13 @@ class Cas1BookingDomainEventService(
val previousArrivalDateIfChanged: LocalDate?,
val previousDepartureDateIfChanged: LocalDate?,
val isSpaceBooking: Boolean,
val characteristics: List<Cas1SpaceCharacteristic>? = null,
val previousCharacteristics: List<Cas1SpaceCharacteristic>? = null,
)

private fun List<CharacteristicEntity>.toCas1SpaceCharacteristics(): List<Cas1SpaceCharacteristic> =
this.map { it.asCas1SpaceCharacteristic() }

private fun CharacteristicEntity.asCas1SpaceCharacteristic() =
Cas1SpaceCharacteristic.entries.first { it.value == this.propertyName }
}
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ class Cas1SpaceBookingService(

val previousArrivalDate = bookingToUpdate.expectedArrivalDate
val previousDepartureDate = bookingToUpdate.expectedDepartureDate
val previousCharacteristics = bookingToUpdate.criteria.toList()

val updatedBooking = updateExistingSpaceBooking(bookingToUpdate, updateSpaceBookingDetails)

Expand All @@ -567,6 +568,7 @@ class Cas1SpaceBookingService(
bookingChangedAt = OffsetDateTime.now(),
previousArrivalDateIfChanged = if (previousArrivalDate != updatedBooking.expectedArrivalDate) previousArrivalDate else null,
previousDepartureDateIfChanged = if (previousDepartureDate != updatedBooking.expectedDepartureDate) previousDepartureDate else null,
previousCharacteristicsIfChanged = if (previousCharacteristics.sortedBy { it.id } != updatedBooking.criteria.sortedBy { it.id }) previousCharacteristics else null,
)

success(updatedBooking)
Expand Down Expand Up @@ -615,9 +617,23 @@ class Cas1SpaceBookingService(
updateFullBookingDates(bookingToUpdate, newArrivalDate, newDepartureDate)
}

if (updateSpaceBookingDetails.characteristics?.isNotEmpty() == true) {
updateRoomCharacteristics(bookingToUpdate, updateSpaceBookingDetails.characteristics)
}

return cas1SpaceBookingRepository.save(bookingToUpdate)
}

private fun updateRoomCharacteristics(
booking: Cas1SpaceBookingEntity,
newRoomCharacteristics: List<CharacteristicEntity>,
) {
booking.criteria.apply {
retainAll { it.isModelScopePremises() }
addAll(newRoomCharacteristics)
}
}

private fun updateDepartureDates(booking: Cas1SpaceBookingEntity, newDepartureDate: LocalDate) {
booking.expectedDepartureDate = newDepartureDate
booking.canonicalDepartureDate = newDepartureDate
Expand All @@ -642,6 +658,7 @@ class Cas1SpaceBookingService(
val premisesId: UUID,
val arrivalDate: LocalDate?,
val departureDate: LocalDate?,
val characteristics: List<CharacteristicEntity>?,
val updatedBy: UserEntity,
)

Expand Down
53 changes: 53 additions & 0 deletions src/main/resources/static/domain-events-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,51 @@ components:
required:
- crn
- noms
Cas1SpaceCharacteristic:
type: string
description: All of the characteristics of both premises and rooms
enum:
- acceptsChildSexOffenders
- acceptsHateCrimeOffenders
- acceptsNonSexualChildOffenders
- acceptsSexOffenders
- additionalRestrictions
- hasArsonInsuranceConditions
- hasBrailleSignage
- hasCallForAssistance
- hasCrib7Bedding
- hasEnSuite
- hasFixedMobilityAids
- hasHearingLoop
- hasLift
- hasNearbySprinkler
- hasSmokeDetector
- hasStepFreeAccess
- hasStepFreeAccessToCommunalAreas
- hasTactileFlooring
- hasTurningSpace
- hasWheelChairAccessibleBathrooms
- hasWideAccessToCommunalAreas
- hasWideDoor
- hasWideStepFreeAccess
- isArsonDesignated
- isArsonSuitable
- isCatered
- isESAP
- isFullyFm
- isGroundFloor
- isGroundFloorNrOffice
- isIAP
- isPIPE
- isRecoveryFocussed
- isSemiSpecialistMentalHealth
- isSingle
- isStepFreeDesignated
- isSuitableForVulnerable
- isSuitedForSexOffenders
- isTopFloorVulnerable
- isWheelchairAccessible
- isWheelchairDesignated
ApplicationSubmittedEnvelope:
type: object
properties:
Expand Down Expand Up @@ -1251,6 +1296,14 @@ components:
description: Only set if the expected departure on has changed
example: '2023-01-30'
format: date
characteristics:
type: array
items:
$ref: '#/components/schemas/Cas1SpaceCharacteristic'
previousCharacteristics:
type: array
items:
$ref: '#/components/schemas/Cas1SpaceCharacteristic'
required:
- applicationId
- applicationUrl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package uk.gov.justice.digital.hmpps.approvedpremisesapi.factory.events
import io.github.bluegroundltd.kfactory.Factory
import io.github.bluegroundltd.kfactory.Yielded
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.BookingChanged
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.Cas1SpaceCharacteristic
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.PersonReference
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.Premises
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.events.cas1.model.StaffMember
Expand All @@ -24,6 +25,7 @@ class BookingChangedFactory : Factory<BookingChanged> {
private var departureOn: Yielded<LocalDate> = { LocalDate.now() }
private var previousArrivalOn: Yielded<LocalDate?> = { null }
private var previousDepartureOn: Yielded<LocalDate?> = { null }
private var characteristics: Yielded<List<Cas1SpaceCharacteristic>> = { emptyList() }

fun withApplicationId(applicationId: UUID) = apply {
this.applicationId = { applicationId }
Expand Down Expand Up @@ -73,6 +75,10 @@ class BookingChangedFactory : Factory<BookingChanged> {
this.previousDepartureOn = { previousDepartureOn }
}

fun withCharacteristics(characteristics: List<Cas1SpaceCharacteristic>) = apply {
this.characteristics = { characteristics }
}

override fun produce() = BookingChanged(
applicationId = this.applicationId(),
applicationUrl = this.applicationUrl(),
Expand All @@ -86,5 +92,6 @@ class BookingChangedFactory : Factory<BookingChanged> {
departureOn = this.departureOn(),
previousArrivalOn = this.previousArrivalOn(),
previousDepartureOn = this.previousDepartureOn(),
characteristics = this.characteristics(),
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.cas1

import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.tuple
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
Expand All @@ -17,6 +18,7 @@ import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1NewSpaceBo
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1NewSpaceBookingCancellation
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1NonArrival
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1SpaceBooking
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1SpaceBookingCharacteristic
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1SpaceBookingRequirements
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1SpaceBookingSummary
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1SpaceBookingSummaryStatus
Expand Down Expand Up @@ -2008,6 +2010,8 @@ class Cas1SpaceBookingTest {

@BeforeAll
fun setupTestData() {
characteristicRepository.deleteAll()

region = givenAProbationRegion()

premises = approvedPremisesEntityFactory.produceAndPersist {
Expand Down Expand Up @@ -2077,6 +2081,93 @@ class Cas1SpaceBookingTest {

domainEventAsserter.assertDomainEventOfTypeStored(updatedSpaceBooking.application?.id!!, DomainEventType.APPROVED_PREMISES_BOOKING_CHANGED)
}

@Test
fun `Update space booking returns OK, and correctly update the room characteristics`() {
val (_, jwt) = givenAUser(roles = listOf(UserRole.CAS1_CRU_MEMBER_FIND_AND_BOOK_BETA))

val (user) = givenAUser()

val (placementRequest) = givenAPlacementRequest(
placementRequestAllocatedTo = user,
assessmentAllocatedTo = user,
createdByUser = user,
)

val premises = approvedPremisesEntityFactory.produceAndPersist {
withSupportsSpaceBookings(true)
withYieldedProbationRegion { region }
withYieldedLocalAuthorityArea { localAuthorityEntityFactory.produceAndPersist() }
}

var characteristics = mutableListOf(
characteristicEntityFactory.produceAndPersist {
withName("Arson Room")
withPropertyName("isArsonSuitable")
withServiceScope("approved-premises")
withModelScope("room")
},
characteristicEntityFactory.produceAndPersist {
withName("En-Suit")
withPropertyName("hasEnSuite")
withServiceScope("approved-premises")
withModelScope("room")
},
characteristicEntityFactory.produceAndPersist {
withName("Single room")
withPropertyName("isSingle")
withServiceScope("approved-premises")
withModelScope("room")
},
characteristicEntityFactory.produceAndPersist {
withName("Wheelchair accessible")
withPropertyName("isWheelchairAccessible")
withServiceScope("approved-premises")
withModelScope("premises")
},
)

val spaceBookingBeforeUpdate = cas1SpaceBookingEntityFactory.produceAndPersist {
withPremises(premises)
withPlacementRequest(placementRequest)
withApplication(placementRequest.application)
withCreatedBy(user)
withCriteria(characteristics)
}

assertThat(spaceBookingBeforeUpdate.criteria.size).isEqualTo(4)
assertThat(spaceBookingBeforeUpdate.criteria)
.extracting("modelScope", "propertyName")
.containsExactlyInAnyOrder(
tuple("room", "isArsonSuitable"),
tuple("room", "hasEnSuite"),
tuple("room", "isSingle"),
tuple("premises", "isWheelchairAccessible"),
)

webTestClient.patch()
.uri("/cas1/premises/${premises.id}/space-bookings/${spaceBookingBeforeUpdate.id}")
.header("Authorization", "Bearer $jwt")
.bodyValue(
Cas1UpdateSpaceBooking(
characteristics = listOf(Cas1SpaceBookingCharacteristic.HAS_EN_SUITE),
),
)
.exchange()
.expectStatus()
.isOk

val updatedSpaceBooking = cas1SpaceBookingRepository.findByIdOrNull(spaceBookingBeforeUpdate.id)!!
assertThat(updatedSpaceBooking.criteria.size).isEqualTo(2)
assertThat(updatedSpaceBooking.criteria)
.extracting("modelScope", "propertyName")
.containsExactlyInAnyOrder(
tuple("room", "hasEnSuite"),
tuple("premises", "isWheelchairAccessible"),
)

domainEventAsserter.assertDomainEventOfTypeStored(updatedSpaceBooking.application?.id!!, DomainEventType.APPROVED_PREMISES_BOOKING_CHANGED)
}
}
}

Expand Down
Loading

0 comments on commit 8f14fd5

Please sign in to comment.