Skip to content

Commit 1364ea8

Browse files
committed
Init new endpoint, service, gateway route for prisoner transactions
1 parent ef8e520 commit 1364ea8

File tree

4 files changed

+199
-1
lines changed

4 files changed

+199
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.controllers.v1
2+
3+
import io.swagger.v3.oas.annotations.Parameter
4+
import jakarta.validation.ValidationException
5+
import org.springframework.beans.factory.annotation.Autowired
6+
import org.springframework.web.bind.annotation.GetMapping
7+
import org.springframework.web.bind.annotation.PathVariable
8+
import org.springframework.web.bind.annotation.RequestAttribute
9+
import org.springframework.web.bind.annotation.RequestMapping
10+
import org.springframework.web.bind.annotation.RequestParam
11+
import org.springframework.web.bind.annotation.RestController
12+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.exception.EntityNotFoundException
13+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.DataResponse
14+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Transactions
15+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApiError
16+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.roleconfig.ConsumerFilters
17+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.GetTransactionsForPersonService
18+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.internal.AuditService
19+
import java.time.LocalDate
20+
21+
@RestController
22+
@RequestMapping("/v1/prison/{prisonId}/prisoners/{hmppsId}/transactions/{accountCode}")
23+
class TransactionsController(
24+
@Autowired val auditService: AuditService,
25+
@Autowired val getTransactionsForPersonService: GetTransactionsForPersonService,
26+
) {
27+
// @Operation(
28+
// summary = "Returns all accounts for a prisoner that they have at a prison.",
29+
// description = "<b>Applicable filters</b>: <ul><li>prisons</li></ul>",
30+
// responses = [
31+
// ApiResponse(responseCode = "200", useReturnTypeSchema = true, description = "Successfully found a prisoner's accounts."),
32+
// ApiResponse(
33+
// responseCode = "400",
34+
// description = "The HMPPS ID provided has an invalid format or the prisoner does hot have accounts at the specified prison.",
35+
// content = [Content(schema = Schema(ref = "#/components/schemas/BadRequest"))],
36+
// ),
37+
// ApiResponse(responseCode = "404", content = [Content(schema = Schema(ref = "#/components/schemas/PersonNotFound"))]),
38+
// ApiResponse(responseCode = "500", content = [Content(schema = Schema(ref = "#/components/schemas/InternalServerError"))]),
39+
// ],
40+
// )
41+
@GetMapping()
42+
fun getAccountCodeForPerson(
43+
@PathVariable hmppsId: String,
44+
@PathVariable prisonId: String,
45+
@PathVariable accountCode: String,
46+
@RequestAttribute filters: ConsumerFilters?,
47+
@Parameter(description = "Start date for transactions (defaults to today if not supplied)") @RequestParam(required = false, name = "fromDate") fromDate: String?,
48+
@Parameter(description = "To date for transactions (defaults to today if not supplied)") @RequestParam(required = false, name = "toDate") toDate: String?,
49+
): DataResponse<Transactions?> {
50+
var startDate = LocalDate.now().toString()
51+
var endDate = LocalDate.now().toString()
52+
53+
if (fromDate == null && toDate != null || toDate == null && fromDate != null) {
54+
throw ValidationException("Both fromDate and toDate must be supplied if one is populated")
55+
}
56+
// respect filters
57+
// catch parse failures
58+
if (fromDate != null && toDate != null) {
59+
startDate = fromDate
60+
endDate = toDate
61+
}
62+
63+
if (accountCode !in listOf("spends", "savings", "cash")) {
64+
throw ValidationException("Account code must either be 'spends', 'savings', or 'cash'")
65+
}
66+
67+
val response = getTransactionsForPersonService.execute(hmppsId, prisonId, accountCode, startDate, endDate, filters)
68+
69+
if (response.hasError(UpstreamApiError.Type.ENTITY_NOT_FOUND)) {
70+
throw EntityNotFoundException("Could not find transactions with id: $hmppsId")
71+
}
72+
73+
if (response.hasError(UpstreamApiError.Type.BAD_REQUEST)) {
74+
throw ValidationException("Either invalid HMPPS ID: $hmppsId at or incorrect prison: $prisonId")
75+
}
76+
77+
auditService.createEvent("GET_TRANSACTIONS_FOR_PERSON", mapOf("hmppsId" to hmppsId, "prisonId" to prisonId, "fromDate" to fromDate, "toDate" to toDate))
78+
return DataResponse(response.data)
79+
}
80+
}

src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/NomisGateway.kt

+34-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.RiskCategor
1616
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Sentence
1717
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.SentenceAdjustment
1818
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.SentenceKeyDates
19+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Transactions
1920
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApi
2021
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.nomis.NomisAccounts
2122
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.nomis.NomisAddress
@@ -201,7 +202,11 @@ class NomisGateway(
201202

202203
return when (result) {
203204
is WebClientWrapperResponse.Success -> {
204-
Response(data = result.data.latestPrisonTerm.sentenceAdjustments.toSentenceAdjustment())
205+
Response(
206+
data =
207+
result.data.latestPrisonTerm.sentenceAdjustments
208+
.toSentenceAdjustment(),
209+
)
205210
}
206211

207212
is WebClientWrapperResponse.Error -> {
@@ -331,6 +336,34 @@ class NomisGateway(
331336
}
332337
}
333338

339+
fun getTransactionsForPerson(
340+
prisonId: String,
341+
nomisNumber: String,
342+
accountCode: String,
343+
fromDate: String,
344+
toDate: String,
345+
): Response<Transactions?> {
346+
val result =
347+
webClient.request<Transactions>(
348+
HttpMethod.GET,
349+
"/api/v1/prison/$prisonId/offenders/$nomisNumber/accounts/$accountCode/transactions?from_date=$fromDate&to_date=$toDate",
350+
authenticationHeader(),
351+
UpstreamApi.NOMIS,
352+
)
353+
return when (result) {
354+
is WebClientWrapperResponse.Success -> {
355+
Response(data = result.data)
356+
}
357+
358+
is WebClientWrapperResponse.Error -> {
359+
Response(
360+
data = null,
361+
errors = result.errors,
362+
)
363+
}
364+
}
365+
}
366+
334367
private fun authenticationHeader(): Map<String, String> {
335368
val token = hmppsAuthGateway.getClientToken("NOMIS")
336369

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps
2+
3+
data class Type(
4+
val code: String,
5+
val desc: String,
6+
)
7+
8+
data class Transaction(
9+
val id: String,
10+
val type: Type,
11+
val description: String,
12+
val amount: Int,
13+
val date: String,
14+
)
15+
16+
data class Transactions(
17+
val transactions: List<Transaction> = emptyList(),
18+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
@file:Suppress("ktlint:standard:no-wildcard-imports")
2+
3+
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.services
4+
5+
import org.springframework.beans.factory.annotation.Autowired
6+
import org.springframework.stereotype.Service
7+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways.NomisGateway
8+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response
9+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Transactions
10+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApi
11+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApiError
12+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.roleconfig.ConsumerFilters
13+
14+
@Service
15+
class GetTransactionsForPersonService(
16+
@Autowired val nomisGateway: NomisGateway,
17+
@Autowired val getPersonService: GetPersonService,
18+
) {
19+
fun execute(
20+
prisonId: String,
21+
hmppsId: String,
22+
accountCode: String,
23+
startDate: String,
24+
endDate: String,
25+
filters: ConsumerFilters? = null,
26+
): Response<Transactions?> {
27+
if (
28+
filters != null && !filters.matchesPrison(prisonId)
29+
) {
30+
return Response(
31+
data = null,
32+
errors = listOf(UpstreamApiError(UpstreamApi.NOMIS, UpstreamApiError.Type.ENTITY_NOT_FOUND, "Not found")),
33+
)
34+
}
35+
36+
val personResponse = getPersonService.getNomisNumber(hmppsId = hmppsId)
37+
val nomisNumber = personResponse.data?.nomisNumber
38+
39+
if (nomisNumber == null) {
40+
return Response(
41+
data = null,
42+
errors = personResponse.errors,
43+
)
44+
}
45+
46+
val nomisTransactions =
47+
nomisGateway.getTransactionsForPerson(
48+
prisonId,
49+
nomisNumber,
50+
accountCode,
51+
startDate,
52+
endDate,
53+
)
54+
55+
if (nomisTransactions.errors.isNotEmpty()) {
56+
return Response(
57+
data = null,
58+
errors = nomisTransactions.errors,
59+
)
60+
}
61+
62+
return Response(
63+
data = nomisTransactions.data,
64+
errors = emptyList(),
65+
)
66+
}
67+
}

0 commit comments

Comments
 (0)