From d99fa85258f5185b2c1eb7097b6a0e7d15c78c5f Mon Sep 17 00:00:00 2001 From: achimber-moj <161360519+achimber-moj@users.noreply.github.com> Date: Tue, 14 May 2024 17:22:45 +0100 Subject: [PATCH] Feature/pi 2158 mas get unpaid work time (#3741) * PI-2158 return unpaid time worked Signed-off-by: Amardeep Chimber * PI-2158 update integration layer Signed-off-by: Amardeep Chimber * PI-2158 update service Signed-off-by: Amardeep Chimber * PI-2158 update tests Signed-off-by: Amardeep Chimber * PI-2158 update integration test and dataloader Signed-off-by: Amardeep Chimber * PI-2158 update service test Signed-off-by: Amardeep Chimber * PI-2158 change minutes to hour calculation Signed-off-by: Amardeep Chimber * PI-2158 amend logic Signed-off-by: Amardeep Chimber * PI-2158 apply review comments to amend sql Signed-off-by: Amardeep Chimber * Formatting changes * PI-2158 convert query from native to jpql Signed-off-by: Amardeep Chimber * PI-2158 remove commented code Signed-off-by: Amardeep Chimber * PI-2158 add more service test to cover unpaid work progress Signed-off-by: Amardeep Chimber * Formatting changes * PI-2158 update service logic Signed-off-by: Amardeep Chimber * PI-2158 update service logic Signed-off-by: Amardeep Chimber * PI-2158 update service logic Signed-off-by: Amardeep Chimber * PI-2158 add unpaid work progress to new section of sentence object Signed-off-by: Amardeep Chimber * PI-2158 add unpaid work progress to new section of sentence object Signed-off-by: Amardeep Chimber * PI-2158 refactor sql to no longer be a native query Signed-off-by: Amardeep Chimber * PI-2158 refactor sql to no longer be a native query Signed-off-by: Amardeep Chimber * Formatting changes * PI-2158 add scenario where no unpaid work has been completed Signed-off-by: Amardeep Chimber * PI-2158 update integration test Signed-off-by: Amardeep Chimber * Formatting changes * PI-2158 apply review comments Signed-off-by: Amardeep Chimber * PI-2158 apply review comments Signed-off-by: Amardeep Chimber * PI-2158 apply review comments Signed-off-by: Amardeep Chimber --------- Signed-off-by: Amardeep Chimber Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../justice/digital/hmpps/data/DataLoader.kt | 8 +- .../hmpps/data/generator/PersonGenerator.kt | 10 +- .../data/generator/UnpaidWorkApptGenerator.kt | 13 + .../digital/hmpps/SentenceIntegrationTest.kt | 19 +- .../hmpps/api/model/sentence/Sentence.kt | 3 +- .../delius/overview/entity/Requirement.kt | 12 + .../delius/sentence/entity/UpwAppointment.kt | 55 ++++ .../digital/hmpps/service/SentenceService.kt | 42 ++- .../hmpps/service/SentenceServiceTest.kt | 266 ++++++++++++++++-- 9 files changed, 398 insertions(+), 30 deletions(-) create mode 100644 projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/UnpaidWorkApptGenerator.kt create mode 100644 projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/UpwAppointment.kt diff --git a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt index c946525715..552129d12c 100644 --- a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt +++ b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt @@ -130,7 +130,9 @@ class DataLoader( PersonGenerator.ADD_OFF_2, PersonGenerator.ADDITIONAL_OFFENCE_2, PersonGenerator.MAIN_CAT_F, + PersonGenerator.MAIN_CAT_W, PersonGenerator.REQUIREMENT, + PersonGenerator.REQUIREMENT_UNPAID_WORK, PersonGenerator.REQUIREMENT_CONTACT_1, PersonGenerator.REQUIREMENT_CONTACT_2, PersonGenerator.REGISTER_TYPE_1, @@ -143,11 +145,15 @@ class DataLoader( PersonGenerator.REGISTRATION_REVIEW_3, PersonGenerator.DEREGISTRATION_1, PersonGenerator.MAIN_CAT_F_TYPE, + PersonGenerator.MAIN_CAT_W_TYPE, PersonGenerator.NSI_BREACH_TYPE, PersonGenerator.NSI_STATUS, PersonGenerator.BREACH_PREVIOUS_ORDER_1, PersonGenerator.BREACH_PREVIOUS_ORDER_2, - PersonGenerator.BREACH_ON_ACTIVE_ORDER + PersonGenerator.BREACH_ON_ACTIVE_ORDER, + UnpaidWorkApptGenerator.UNPAID_WORK_DETAILS_1, + UnpaidWorkApptGenerator.APPT1, + UnpaidWorkApptGenerator.APPT2, ) personalDetailsData() diff --git a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt index 512c9ba038..ba18a6c818 100644 --- a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt +++ b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt @@ -126,7 +126,13 @@ object PersonGenerator { val MAIN_CAT_F_SUB_ID = IdGenerator.getAndIncrement(); val MAIN_CAT_F_TYPE = ReferenceData(MAIN_CAT_F_SUB_ID, "G03", "High Intensity") val MAIN_CAT_F = RequirementMainCategory(IdGenerator.getAndIncrement(), "F", "Main", 1) - val REQUIREMENT = generateRequirement(ACTIVE_ORDER, MAIN_CAT_F_SUB_ID) + val REQUIREMENT = generateRequirement(ACTIVE_ORDER, MAIN_CAT_F_SUB_ID, mainCategory = MAIN_CAT_F) + + val MAIN_CAT_W_SUB_ID = IdGenerator.getAndIncrement(); + val MAIN_CAT_W_TYPE = ReferenceData(MAIN_CAT_W_SUB_ID, "W02", "Intensive") + val MAIN_CAT_W = RequirementMainCategory(IdGenerator.getAndIncrement(), "W", "Unpaid Work", 1107) + val REQUIREMENT_UNPAID_WORK = generateRequirement(ACTIVE_ORDER, MAIN_CAT_W_SUB_ID, mainCategory = MAIN_CAT_W) + val REQUIREMENT_CONTACT_1 = ContactGenerator.generateContact( OVERVIEW, ContactGenerator.APPT_CT_1, @@ -314,7 +320,7 @@ object PersonGenerator { subCategoryId: Long, length: Long = 12, notes: String? = "my notes", - mainCategory: RequirementMainCategory = MAIN_CAT_F, + mainCategory: RequirementMainCategory, active: Boolean = true, softDeleted: Boolean = false, expectedStartDate: LocalDate? = LocalDate.now().minusDays(1), diff --git a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/UnpaidWorkApptGenerator.kt b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/UnpaidWorkApptGenerator.kt new file mode 100644 index 0000000000..6a838af1ac --- /dev/null +++ b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/UnpaidWorkApptGenerator.kt @@ -0,0 +1,13 @@ +package uk.gov.justice.digital.hmpps.data.generator + +import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator.ACTIVE_ORDER +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.UpwAppointment +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.UpwDetails + +object UnpaidWorkApptGenerator { + + val UNPAID_WORK_DETAILS_1 = UpwDetails(IdGenerator.getAndIncrement(), ACTIVE_ORDER, 0) + + val APPT1 = UpwAppointment(IdGenerator.getAndIncrement(), 3, "Y", 0, UNPAID_WORK_DETAILS_1) + val APPT2 = UpwAppointment(IdGenerator.getAndIncrement(), 4, "Y", 1, UNPAID_WORK_DETAILS_1) +} \ No newline at end of file diff --git a/projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/SentenceIntegrationTest.kt b/projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/SentenceIntegrationTest.kt index 1e48da53e0..4bb9178505 100644 --- a/projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/SentenceIntegrationTest.kt +++ b/projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/SentenceIntegrationTest.kt @@ -81,12 +81,26 @@ class SentenceIntegrationTest { null, "my notes", Rar(completed = 1, scheduled = 0, totalDays = 1) + ), + Requirement( + "W", + LocalDate.now().minusDays(1), + LocalDate.now(), + LocalDate.now().minusDays(2), + LocalDate.now().minusDays(3), + null, + "Unpaid Work - Intensive", + 12, + null, + "my notes", + null ) ), listOf( CourtDocument(COURT_DOCUMENT.alfrescoId, LocalDate.now().minusDays(1), "court report"), CourtDocument(EVENT_DOCUMENT.alfrescoId, LocalDate.now().minusDays(3), "event report") - ) + ), + "3 minutes completed (of 12 hours)" ), Sentence( OffenceDetails( @@ -99,7 +113,8 @@ class SentenceIntegrationTest { Conviction(null, null, null, listOf()), null, listOf(), - listOf() + listOf(), + null ) ), ProbationHistory(2, LocalDate.now().minusDays(7), 2, 2) diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/Sentence.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/Sentence.kt index c9dd149c6b..ace75da39f 100644 --- a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/Sentence.kt +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/Sentence.kt @@ -7,5 +7,6 @@ data class Sentence( val conviction: Conviction? = null, val order: Order? = null, val requirements: List = listOf(), - val courtDocuments: List = listOf() + val courtDocuments: List = listOf(), + val unpaidWorkProgress: String? ) \ No newline at end of file diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/overview/entity/Requirement.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/overview/entity/Requirement.kt index c90176ef05..1921c60795 100644 --- a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/overview/entity/Requirement.kt +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/overview/entity/Requirement.kt @@ -157,6 +157,18 @@ interface RequirementRepository : JpaRepository { """, nativeQuery = true ) fun getRequirements(id: Long, eventNumber: String): List + + @Query( + """ + SELECT SUM(r.length) + FROM Requirement r + JOIN r.mainCategory mc + JOIN r.disposal + WHERE r.disposal.id = :id + AND mc.code = 'W' + """ + ) + fun sumTotalUnpaidWorkHoursByDisposal(id: Long): Long } fun RequirementRepository.getRar(disposalId: Long): Rar { diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/UpwAppointment.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/UpwAppointment.kt new file mode 100644 index 0000000000..36b068d85e --- /dev/null +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/UpwAppointment.kt @@ -0,0 +1,55 @@ +package uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity + +import jakarta.persistence.* +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.integrations.delius.overview.entity.Disposal + +@Entity +@Immutable +class UpwAppointment( + @Id + @Column(name = "upw_appointment_id") + val id: Long, + + val minutesCredited: Long?, + + @Column(columnDefinition = "char(1)") + val attended: String?, + + val softDeleted: Long, + + @JoinColumn(name = "upw_details_id") + @ManyToOne + val upwDetails: UpwDetails, +) + +@Entity +@Immutable +class UpwDetails( + @Id + @Column(name = "upw_details_id") + val id: Long, + + @JoinColumn(name = "disposal_id") + @ManyToOne + val disposal: Disposal, + + val softDeleted: Long, +) + +interface UpwAppointmentRepository : JpaRepository { + + @Query( + """ + SELECT SUM(u.minutesCredited) + FROM UpwAppointment u + JOIN u.upwDetails upd + JOIN upd.disposal d + WHERE d.id = :id + AND u.softDeleted = 0 + """ + ) + fun calculateUnpaidTimeWorked(id: Long): Long +} \ No newline at end of file diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/SentenceService.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/SentenceService.kt index 6a9f182346..b08df7ea98 100644 --- a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/SentenceService.kt +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/SentenceService.kt @@ -1,7 +1,6 @@ package uk.gov.justice.digital.hmpps.service import org.springframework.stereotype.Service -import uk.gov.justice.digital.hmpps.api.model.Name import uk.gov.justice.digital.hmpps.api.model.overview.Order import uk.gov.justice.digital.hmpps.api.model.overview.Rar import uk.gov.justice.digital.hmpps.api.model.sentence.* @@ -11,7 +10,9 @@ import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.* import uk.gov.justice.digital.hmpps.integrations.delius.personalDetails.entity.CourtDocumentDetails import uk.gov.justice.digital.hmpps.integrations.delius.personalDetails.entity.DocumentRepository import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.* +import java.time.Duration import java.time.LocalDate +import kotlin.time.toKotlinDuration import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.AdditionalSentence as ExtraSentence @Service @@ -22,7 +23,8 @@ class SentenceService( private val personRepository: PersonRepository, private val requirementRepository: RequirementRepository, private val documentRepository: DocumentRepository, - private val offenderManagerRepository: OffenderManagerRepository + private val offenderManagerRepository: OffenderManagerRepository, + private val upwAppointmentRepository: UpwAppointmentRepository ) { fun getEvents(crn: String): SentenceOverview { val person = personRepository.getPerson(crn) @@ -62,16 +64,15 @@ class SentenceService( additionalSentences.map { it.toAdditionalSentence() } ), order = disposal?.toOrder(), - requirements = requirementRepository.getRequirements(id, eventNumber).map { it.toRequirement() }, - courtDocuments = documentRepository.getCourtDocuments(id, eventNumber).map { it.toCourtDocument() } + requirements = requirementRepository.getRequirements(id, eventNumber) + .map { it.toRequirement() }, + courtDocuments = documentRepository.getCourtDocuments(id, eventNumber).map { it.toCourtDocument() }, + disposal?.id?.let { getUnpaidWorkTime(it) } ) fun ExtraSentence.toAdditionalSentence(): AdditionalSentence = AdditionalSentence(length, amount, notes, type.description) - fun Person.toName() = - Name(forename, secondName, surname) - fun Disposal.toOrder() = Order(description = type.description, length = length, startDate = date, endDate = expectedEndDate()) @@ -116,6 +117,33 @@ class SentenceService( return null } + fun getUnpaidWorkTime(disposalId: Long): String? { + val totalHoursOrdered = requirementRepository.sumTotalUnpaidWorkHoursByDisposal(disposalId) + + if (totalHoursOrdered == 0L) { + return null + } + + val durationInMinutes: Long = upwAppointmentRepository.calculateUnpaidTimeWorked(disposalId) + + return getUnpaidWorkTime(totalHoursOrdered, durationInMinutes) + } + + fun getUnpaidWorkTime(hoursOrdered: Long, minutesCredited: Long): String { + val totalMessage = hoursOrdered + .let { " (of $hoursOrdered hour${if (hoursOrdered != 1L) "s" else ""})" } + + val creditedMessage = Duration.ofMinutes(minutesCredited).toKotlinDuration() + .toComponents { hours, minutes, _, _ -> + when { + hours == 0L -> "$minutes minute${if (minutes != 1) "s" else ""} completed" + minutes == 0 -> "$hours hour${if (hours != 1L) "s" else ""} completed" + else -> "$hours hour${if (hours != 1L) "s" else ""} $minutes minute${if (minutes != 1) "s" else ""} completed" + } + } + return "$creditedMessage$totalMessage" + } + fun CourtDocumentDetails.toCourtDocument(): CourtDocument = CourtDocument(id, lastSaved, documentName) fun getMostRecentTerminatedDateFromInactiveEvents(events: List): LocalDate? { diff --git a/projects/manage-supervision-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/SentenceServiceTest.kt b/projects/manage-supervision-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/SentenceServiceTest.kt index 104faf8065..ba1f43b813 100644 --- a/projects/manage-supervision-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/SentenceServiceTest.kt +++ b/projects/manage-supervision-and-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/service/SentenceServiceTest.kt @@ -10,6 +10,7 @@ import org.mockito.kotlin.* import uk.gov.justice.digital.hmpps.api.model.overview.Order import uk.gov.justice.digital.hmpps.api.model.overview.Rar import uk.gov.justice.digital.hmpps.api.model.sentence.* +import uk.gov.justice.digital.hmpps.api.model.sentence.AdditionalSentence import uk.gov.justice.digital.hmpps.data.generator.AdditionalSentenceGenerator import uk.gov.justice.digital.hmpps.data.generator.CourtAppearanceGenerator import uk.gov.justice.digital.hmpps.data.generator.CourtGenerator @@ -18,10 +19,7 @@ import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.PersonRe import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.RequirementRepository import uk.gov.justice.digital.hmpps.integrations.delius.personalDetails.entity.CourtDocumentDetails import uk.gov.justice.digital.hmpps.integrations.delius.personalDetails.entity.DocumentRepository -import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.AdditionalSentenceRepository -import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CourtAppearanceRepository -import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.EventSentenceRepository -import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.OffenderManagerRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.* import java.time.LocalDate @ExtendWith(MockitoExtension::class) @@ -48,9 +46,39 @@ class SentenceServiceTest { @Mock lateinit var offenderManagerRepository: OffenderManagerRepository + @Mock + lateinit var upwAppointmentRepository: UpwAppointmentRepository + @InjectMocks lateinit var service: SentenceService + private val event = PersonGenerator.generateEvent( + person = PersonGenerator.OVERVIEW, + active = true, + inBreach = true, + disposal = PersonGenerator.ACTIVE_ORDER, + eventNumber = "123457", + mainOffence = PersonGenerator.MAIN_OFFENCE_1, + notes = "overview", + additionalOffences = listOf(PersonGenerator.ADDITIONAL_OFFENCE_1) + ) + + private val requirement1 = RequirementDetails( + 1, + LocalDate.now().minusDays(21), + LocalDate.now(), + LocalDate.now().minusDays(14), + LocalDate.now().minusDays(7), + LocalDate.now().minusDays(3), + "Expired (Normal)", + 12, + "Weeks", + "W", + "Drug Rehabilitation", + "Medium Intensity", + "new requirement" + ) + @Test fun `no active sentences`() { @@ -80,22 +108,12 @@ class SentenceServiceTest { verifyNoInteractions(additionalSentenceRepository) verifyNoInteractions(requirementRepository) verifyNoInteractions(documentRepository) + verifyNoInteractions(upwAppointmentRepository) } @Test fun `recent active sentences`() { - val event = PersonGenerator.generateEvent( - person = PersonGenerator.OVERVIEW, - active = true, - inBreach = true, - disposal = PersonGenerator.ACTIVE_ORDER, - eventNumber = "123457", - mainOffence = PersonGenerator.MAIN_OFFENCE_1, - notes = "overview", - additionalOffences = listOf(PersonGenerator.ADDITIONAL_OFFENCE_1) - ) - val requirement1 = RequirementDetails( 1, LocalDate.now().minusDays(21), @@ -106,7 +124,7 @@ class SentenceServiceTest { "Expired (Normal)", 12, "Weeks", - "G", + "W", "Drug Rehabilitation", "Medium Intensity", "new requirement" @@ -175,6 +193,9 @@ class SentenceServiceTest { ) ) + whenever(requirementRepository.sumTotalUnpaidWorkHoursByDisposal(event.disposal!!.id)).thenReturn(70) + whenever(upwAppointmentRepository.calculateUnpaidTimeWorked(event.disposal!!.id)).thenReturn(3936) + val response = service.getEvents(PersonGenerator.OVERVIEW.crn) val expected = SentenceOverview( @@ -241,7 +262,8 @@ class SentenceServiceTest { null ) ), - listOf(CourtDocument("A001", LocalDate.now(), "Pre Sentence Event")) + listOf(CourtDocument("A001", LocalDate.now(), "Pre Sentence Event")), + "65 hours 36 minutes completed (of 70 hours)" ) ), ProbationHistory(0, null, 0, 0) @@ -252,11 +274,221 @@ class SentenceServiceTest { verify(additionalSentenceRepository, times(1)).getAllByEventId(event.id) verify(courtAppearanceRepository, times(1)).getFirstCourtAppearanceByEventIdOrderByDate(event.id) verify(documentRepository, times(1)).getCourtDocuments(event.id, event.eventNumber) + verify(requirementRepository, times(1)).sumTotalUnpaidWorkHoursByDisposal(event.disposal!!.id) + verify(upwAppointmentRepository, times(1)).calculateUnpaidTimeWorked(event.disposal!!.id) verifyNoMoreInteractions(eventRepository) verifyNoMoreInteractions(additionalSentenceRepository) verifyNoMoreInteractions(courtAppearanceRepository) verifyNoMoreInteractions(documentRepository) + verifyNoMoreInteractions(requirementRepository) + verifyNoMoreInteractions(upwAppointmentRepository) + } + + @Test + fun `unpaid work 0 time recorded`() { + + whenever(personRepository.findByCrn(PersonGenerator.OVERVIEW.crn)).thenReturn(PersonGenerator.OVERVIEW) + + whenever(eventRepository.findSentencesByPersonId(PersonGenerator.OVERVIEW.id)).thenReturn(listOf(event)) + + whenever(requirementRepository.getRequirements(event.id, event.eventNumber)) + .thenReturn(listOf(requirement1)) + + whenever(requirementRepository.sumTotalUnpaidWorkHoursByDisposal(event.disposal!!.id)).thenReturn(1) + whenever(upwAppointmentRepository.calculateUnpaidTimeWorked(event.disposal!!.id)).thenReturn(0) + + val response = service.getEvents(PersonGenerator.OVERVIEW.crn) + + val expected = "0 minutes completed (of 1 hour)" + + + assertEquals(expected, response.sentences[0].unpaidWorkProgress) + } + + @Test + fun `unpaid work one minute`() { + + whenever(personRepository.findByCrn(PersonGenerator.OVERVIEW.crn)).thenReturn(PersonGenerator.OVERVIEW) + + whenever(eventRepository.findSentencesByPersonId(PersonGenerator.OVERVIEW.id)).thenReturn(listOf(event)) + + whenever(requirementRepository.getRequirements(event.id, event.eventNumber)) + .thenReturn(listOf(requirement1)) + + whenever(requirementRepository.sumTotalUnpaidWorkHoursByDisposal(event.disposal!!.id)).thenReturn(1) + whenever(upwAppointmentRepository.calculateUnpaidTimeWorked(event.disposal!!.id)).thenReturn(1) + + val response = service.getEvents(PersonGenerator.OVERVIEW.crn) + + val expected = "1 minute completed (of 1 hour)" + + + assertEquals(expected, response.sentences[0].unpaidWorkProgress) + } + + @Test + fun `unpaid work two minutes`() { + + whenever(personRepository.findByCrn(PersonGenerator.OVERVIEW.crn)).thenReturn(PersonGenerator.OVERVIEW) + + whenever(eventRepository.findSentencesByPersonId(PersonGenerator.OVERVIEW.id)).thenReturn(listOf(event)) + + whenever(requirementRepository.getRequirements(event.id, event.eventNumber)) + .thenReturn(listOf(requirement1)) + + whenever(requirementRepository.sumTotalUnpaidWorkHoursByDisposal(event.disposal!!.id)).thenReturn(2) + whenever(upwAppointmentRepository.calculateUnpaidTimeWorked(event.disposal!!.id)).thenReturn(2) + + val response = service.getEvents(PersonGenerator.OVERVIEW.crn) + + val expected = "2 minutes completed (of 2 hours)" + + assertEquals(expected, response.sentences[0].unpaidWorkProgress) + } + + @Test + fun `unpaid work 60 minutes`() { + + whenever(personRepository.findByCrn(PersonGenerator.OVERVIEW.crn)).thenReturn(PersonGenerator.OVERVIEW) + + whenever(eventRepository.findSentencesByPersonId(PersonGenerator.OVERVIEW.id)).thenReturn(listOf(event)) + + whenever(requirementRepository.getRequirements(event.id, event.eventNumber)) + .thenReturn(listOf(requirement1)) + + whenever(requirementRepository.sumTotalUnpaidWorkHoursByDisposal(event.disposal!!.id)).thenReturn(1) + whenever(upwAppointmentRepository.calculateUnpaidTimeWorked(event.disposal!!.id)).thenReturn(60) + + val response = service.getEvents(PersonGenerator.OVERVIEW.crn) + + val expected = Requirement( + requirement1._code, + requirement1._expectedStartDate, + requirement1._startDate, + requirement1._expectedEndDate, + requirement1._terminationDate, + requirement1._terminationReason, + "${requirement1._description} - ${requirement1._codeDescription}", + requirement1._length, + requirement1.lengthUnitValue, + requirement1._notes, + null + ) + + assertEquals(expected, response.sentences[0].requirements[0]) + } + + @Test + fun `unpaid work 61 minutes`() { + + whenever(personRepository.findByCrn(PersonGenerator.OVERVIEW.crn)).thenReturn(PersonGenerator.OVERVIEW) + + whenever(eventRepository.findSentencesByPersonId(PersonGenerator.OVERVIEW.id)).thenReturn(listOf(event)) + + whenever(requirementRepository.getRequirements(event.id, event.eventNumber)) + .thenReturn(listOf(requirement1)) + + whenever(requirementRepository.sumTotalUnpaidWorkHoursByDisposal(event.disposal!!.id)).thenReturn(2) + whenever(upwAppointmentRepository.calculateUnpaidTimeWorked(event.disposal!!.id)).thenReturn(61) + + val response = service.getEvents(PersonGenerator.OVERVIEW.crn) + + val expected = "1 hour 1 minute completed (of 2 hours)" + + assertEquals(expected, response.sentences[0].unpaidWorkProgress) + } + + @Test + fun `unpaid work 62 minutes`() { + + whenever(personRepository.findByCrn(PersonGenerator.OVERVIEW.crn)).thenReturn(PersonGenerator.OVERVIEW) + + whenever(eventRepository.findSentencesByPersonId(PersonGenerator.OVERVIEW.id)).thenReturn(listOf(event)) + + whenever(requirementRepository.getRequirements(event.id, event.eventNumber)) + .thenReturn(listOf(requirement1)) + + whenever(requirementRepository.sumTotalUnpaidWorkHoursByDisposal(event.disposal!!.id)).thenReturn(2) + whenever(upwAppointmentRepository.calculateUnpaidTimeWorked(event.disposal!!.id)).thenReturn(62) + + val response = service.getEvents(PersonGenerator.OVERVIEW.crn) + + val expected = "1 hour 2 minutes completed (of 2 hours)" + + assertEquals(expected, response.sentences[0].unpaidWorkProgress) + } + + @Test + fun `unpaid work 120 minutes`() { + + whenever(personRepository.findByCrn(PersonGenerator.OVERVIEW.crn)).thenReturn(PersonGenerator.OVERVIEW) + + whenever(eventRepository.findSentencesByPersonId(PersonGenerator.OVERVIEW.id)).thenReturn(listOf(event)) + + whenever(requirementRepository.getRequirements(event.id, event.eventNumber)) + .thenReturn(listOf(requirement1)) + + whenever(requirementRepository.sumTotalUnpaidWorkHoursByDisposal(event.disposal!!.id)).thenReturn(1) + whenever(upwAppointmentRepository.calculateUnpaidTimeWorked(event.disposal!!.id)).thenReturn(120) + + val response = service.getEvents(PersonGenerator.OVERVIEW.crn) + + val expected = Requirement( + requirement1._code, + requirement1._expectedStartDate, + requirement1._startDate, + requirement1._expectedEndDate, + requirement1._terminationDate, + requirement1._terminationReason, + "${requirement1._description} - ${requirement1._codeDescription}", + requirement1._length, + requirement1.lengthUnitValue, + requirement1._notes, + null + ) + + assertEquals(expected, response.sentences[0].requirements[0]) + } + + @Test + fun `unpaid work 121 minutes`() { + + whenever(personRepository.findByCrn(PersonGenerator.OVERVIEW.crn)).thenReturn(PersonGenerator.OVERVIEW) + + whenever(eventRepository.findSentencesByPersonId(PersonGenerator.OVERVIEW.id)).thenReturn(listOf(event)) + + whenever(requirementRepository.getRequirements(event.id, event.eventNumber)) + .thenReturn(listOf(requirement1)) + + whenever(requirementRepository.sumTotalUnpaidWorkHoursByDisposal(event.disposal!!.id)).thenReturn(3) + whenever(upwAppointmentRepository.calculateUnpaidTimeWorked(event.disposal!!.id)).thenReturn(121) + + val response = service.getEvents(PersonGenerator.OVERVIEW.crn) + + val expected = "2 hours 1 minute completed (of 3 hours)" + + assertEquals(expected, response.sentences[0].unpaidWorkProgress) + } + + @Test + fun `unpaid work 122 minutes`() { + + whenever(personRepository.findByCrn(PersonGenerator.OVERVIEW.crn)).thenReturn(PersonGenerator.OVERVIEW) + + whenever(eventRepository.findSentencesByPersonId(PersonGenerator.OVERVIEW.id)).thenReturn(listOf(event)) + + whenever(requirementRepository.getRequirements(event.id, event.eventNumber)) + .thenReturn(listOf(requirement1)) + + whenever(requirementRepository.sumTotalUnpaidWorkHoursByDisposal(event.disposal!!.id)).thenReturn(3) + whenever(upwAppointmentRepository.calculateUnpaidTimeWorked(event.disposal!!.id)).thenReturn(122) + + val response = service.getEvents(PersonGenerator.OVERVIEW.crn) + + val expected = "2 hours 2 minutes completed (of 3 hours)" + + assertEquals(expected, response.sentences[0].unpaidWorkProgress) } data class RequirementDetails(