Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,6 @@ public ResponseEntity<Errors> handleDcbHubLocationException(DcbHubLocationExcept
}

private void logExceptionMessage(Exception ex) {
log.warn("Exception occurred [{}]", ex.getClass().getSimpleName());
log.warn("Exception occurred [{}]", ex.getClass().getSimpleName(), ex);
}
}
11 changes: 11 additions & 0 deletions src/main/java/org/folio/dcb/service/CirculationService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.folio.dcb.service;

import org.folio.dcb.domain.dto.ClaimedReturnedResolution;
import org.folio.dcb.domain.entity.TransactionEntity;

public interface CirculationService {
Expand All @@ -13,6 +14,16 @@ public interface CirculationService {

void checkInByBarcode(TransactionEntity dcbTransaction, String servicePointId);

/**
* Check in item by barcode with an optional claim returned resolution.
*
* @param dcbTransaction dcbTransactionEntity
* @param servicePointId service point id
* @param claimedReturnedResolution optional claim returned resolution
*/
void checkInByBarcode(TransactionEntity dcbTransaction, String servicePointId,
ClaimedReturnedResolution claimedReturnedResolution);

/**
* Check out an item by barcode.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
import static org.folio.dcb.domain.dto.TransactionStatus.StatusEnum.ITEM_CHECKED_OUT;
import static org.folio.dcb.domain.dto.TransactionStatus.StatusEnum.OPEN;

import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.folio.dcb.domain.dto.DcbTransaction;
import org.folio.dcb.domain.dto.ServicePointRequest;
import org.folio.dcb.domain.dto.TransactionStatus;
import org.folio.dcb.domain.dto.TransactionStatusContext;
import org.folio.dcb.domain.dto.TransactionStatusResponse;
import org.folio.dcb.domain.entity.TransactionEntity;
import org.folio.dcb.repository.TransactionRepository;
Expand Down Expand Up @@ -47,11 +49,20 @@ public void updateTransactionStatus(TransactionEntity dcbTransaction, Transactio
dcbTransaction.getId(), dcbTransaction.getStatus(), transactionStatus.getStatus());
var currStatus = dcbTransaction.getStatus();
var newStatus = transactionStatus.getStatus();
String randomServicePointId = UUID.randomUUID().toString();

if (CREATED == currStatus && OPEN == newStatus || ITEM_CHECKED_OUT == currStatus && ITEM_CHECKED_IN == newStatus) {
if (CREATED == currStatus && OPEN == newStatus) {
log.info("updateTransactionStatus:: Checking in item for transaction {}.", dcbTransaction.getId());
// Random UUID for servicePointId.
circulationService.checkInByBarcode(dcbTransaction, UUID.randomUUID().toString());
circulationService.checkInByBarcode(dcbTransaction, randomServicePointId);
updateTransactionEntity(dcbTransaction, newStatus);
} else if (ITEM_CHECKED_OUT == currStatus && ITEM_CHECKED_IN == newStatus) {
log.info("updateTransactionStatus:: Checking in item for transaction {}.", dcbTransaction.getId());
Optional.ofNullable(transactionStatus.getContext())
.map(TransactionStatusContext::getClaimedReturnedResolution)
.ifPresentOrElse(
resolution -> circulationService.checkInByBarcode(dcbTransaction, randomServicePointId, resolution),
() -> circulationService.checkInByBarcode(dcbTransaction, randomServicePointId));

updateTransactionEntity(dcbTransaction, newStatus);
} else if (OPEN == currStatus && AWAITING_PICKUP == newStatus) {
circulationService.checkInByBarcode(dcbTransaction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lombok.extern.log4j.Log4j2;
import org.folio.dcb.domain.dto.CheckInRequest;
import org.folio.dcb.domain.dto.CheckOutRequest;
import org.folio.dcb.domain.dto.ClaimedReturnedResolution;
import org.folio.dcb.domain.entity.TransactionEntity;
import org.folio.dcb.exception.CirculationRequestException;
import org.folio.dcb.integration.circulation.CirculationClient;
Expand Down Expand Up @@ -34,6 +35,16 @@ public void checkInByBarcode(TransactionEntity dcbTransaction, String servicePoi
circulationClient.checkInByBarcode(createCheckInRequest(dcbTransaction.getItemBarcode(), servicePointId));
}

@Override
public void checkInByBarcode(TransactionEntity dcbTransaction, String servicePointId,
ClaimedReturnedResolution claimedReturnedResolution) {

log.info("checkInByBarcode:: checking in item for transaction {} with claimedReturnedResolution '{}'.",
dcbTransaction.getId(), claimedReturnedResolution);
circulationClient.checkInByBarcode(createCheckInRequest(dcbTransaction.getItemBarcode(), servicePointId,
claimedReturnedResolution));
}

@Override
public void checkOutByBarcode(TransactionEntity dcbTransaction) {
log.debug("checkOutByBarcode:: checking out item for transaction {}.", dcbTransaction.getId());
Expand All @@ -60,10 +71,17 @@ public void cancelRequest(TransactionEntity dcbTransaction, boolean isItemUnavai
}

private CheckInRequest createCheckInRequest(String itemBarcode, String servicePointId) {
return createCheckInRequest(itemBarcode, servicePointId, null);
}

private CheckInRequest createCheckInRequest(String itemBarcode, String servicePointId,
ClaimedReturnedResolution claimedReturnedResolution) {

return CheckInRequest.builder()
.itemBarcode(itemBarcode)
.servicePointId(servicePointId)
.checkInDate(OffsetDateTime.now().toString())
.claimedReturnedResolution(claimedReturnedResolution)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ private void processLenderRoleTransaction(TransactionStatus status, TransactionE

private void processBorrowerTransaction(TransactionStatus status, TransactionEntity transaction) {
statusProcessorService.borrowingChainProcessor(transaction.getStatus(), status.getStatus())
.forEach(statusEnum -> borrowingLibraryService.updateTransactionStatus(
transaction, TransactionStatus.builder().status(statusEnum).build()));
.forEach(statusEnum -> borrowingLibraryService.updateTransactionStatus(transaction,
TransactionStatus.builder().status(statusEnum).context(status.getContext()).build()));
}

private void validateLoanPolicy(String loanPolicyId, LoanPolicy loanPolicy) {
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/swagger.api/dcb_transaction.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,8 @@ components:
$ref: 'schemas/CirculationRequest.yaml#/CirculationRequest'
CheckInRequest:
$ref: 'schemas/CheckInRequest.yaml#/CheckInRequest'
ClaimedReturnedResolution:
$ref: 'schemas/claimedReturnedResolution.yaml#/ClaimedReturnedResolution'
CheckOutRequest:
$ref: 'schemas/checkOutRequest.yaml#/CheckOutRequest'
ServicePointRequest:
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/swagger.api/schemas/CheckInRequest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ CheckInRequest:
description: When the item was checked in
type: string
format: date-type
claimedReturnedResolution:
$ref: 'claimedReturnedResolution.yaml#/ClaimedReturnedResolution'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ClaimedReturnedResolution:
type: string
description: Claimed returned resolution
enum:
- Found by library
- Returned by patron
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ TransactionStatus:
- ERROR #Error occurred
message:
type: string
context:
type: object
description: Optional context for the transaction status update
properties:
claimedReturnedResolution:
$ref: 'claimedReturnedResolution.yaml#/ClaimedReturnedResolution'
51 changes: 51 additions & 0 deletions src/test/java/org/folio/dcb/it/BorrowerTransactionIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Map;
import org.folio.dcb.domain.dto.ClaimedReturnedResolution;
import org.folio.dcb.domain.dto.TransactionStatus;
import org.folio.dcb.domain.dto.TransactionStatus.StatusEnum;
import org.folio.dcb.domain.dto.TransactionStatusContext;
import org.folio.dcb.it.base.BaseTenantIntegrationTest;
import org.folio.dcb.support.types.IntegrationTest;
import org.folio.dcb.support.wiremock.WireMockStub;
Expand Down Expand Up @@ -221,6 +224,54 @@ void updateTransactionStatus_positive_fromItemCheckedOutToItemCheckedIn() throws
.andExpect(jsonPath("$.status").value("ITEM_CHECKED_IN"));
}

@Test
@WireMockStub("/stubs/mod-circulation/check-in-by-barcode/201-post(with claim returned - found by library).json")
void updateTransactionStatus_positive_fromItemCheckedOutToItemCheckedInWithFoundByLibrary() throws Exception {
testJdbcHelper.saveDcbTransaction(DCB_TRANSACTION_ID, ITEM_CHECKED_OUT, borrowerDcbTransaction());
var context = TransactionStatusContext.builder()
.claimedReturnedResolution(ClaimedReturnedResolution.FOUND_BY_LIBRARY)
.build();
var transactionStatus = TransactionStatus.builder()
.status(ITEM_CHECKED_IN)
.context(context)
.build();
putDcbTransactionStatus(DCB_TRANSACTION_ID, transactionStatus)
.andExpect(jsonPath("$.status").value("ITEM_CHECKED_IN"));

wiremock.verifyThat(1, postRequestedFor(urlPathEqualTo("/circulation/check-in-by-barcode"))
.withRequestBody(matchingJsonPath("$.claimedReturnedResolution", equalTo("Found by library"))));
}

@Test
@WireMockStub("/stubs/mod-circulation/check-in-by-barcode/201-post(with claim returned - returned by patron).json")
void updateTransactionStatus_positive_fromItemCheckedOutToItemCheckedInWithReturnedByPatron() throws Exception {

testJdbcHelper.saveDcbTransaction(DCB_TRANSACTION_ID, ITEM_CHECKED_OUT, borrowerDcbTransaction());
var context = TransactionStatusContext.builder()
.claimedReturnedResolution(ClaimedReturnedResolution.RETURNED_BY_PATRON)
.build();
var transactionStatus = TransactionStatus.builder()
.status(ITEM_CHECKED_IN)
.context(context)
.build();
putDcbTransactionStatus(DCB_TRANSACTION_ID, transactionStatus)
.andExpect(jsonPath("$.status").value("ITEM_CHECKED_IN"));

wiremock.verifyThat(1, postRequestedFor(urlPathEqualTo("/circulation/check-in-by-barcode"))
.withRequestBody(matchingJsonPath("$.claimedReturnedResolution", equalTo("Returned by patron"))));
}

@Test
void updateTransactionStatus_negative_invalidClaimedReturnedResolution() throws Exception {
testJdbcHelper.saveDcbTransaction(DCB_TRANSACTION_ID, ITEM_CHECKED_OUT, borrowerDcbTransaction());
mockMvc.perform(put("/transactions/{id}/status", DCB_TRANSACTION_ID)
.content("{\"status\":\"ITEM_CHECKED_IN\",\"context\":{\"claimedReturnedResolution\":\"INVALID_VALUE\"}}")
.headers(defaultHeaders())
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest());
}

@Test
@WireMockStub({
"/stubs/mod-circulation/check-in-by-barcode/201-post(random SP).json",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.UUID;
import org.folio.dcb.domain.dto.ClaimedReturnedResolution;
import org.folio.dcb.domain.dto.TransactionStatus;
import org.folio.dcb.domain.dto.TransactionStatusContext;
import org.folio.dcb.repository.TransactionRepository;
import org.folio.dcb.service.impl.BaseLibraryService;
import org.folio.dcb.service.impl.BorrowingLibraryServiceImpl;
Expand Down Expand Up @@ -116,4 +119,40 @@ void testTransactionStatusUpdateFromItemCheckedOutToItemCheckedIn() {
verify(circulationService).checkInByBarcode(any(), any());
assertEquals(ITEM_CHECKED_IN, transactionEntity.getStatus());
}

@Test
void testTransactionStatusUpdateFromItemCheckedOutToItemCheckedInWithFoundByLibrary() {
var transactionEntity = createTransactionEntity();
transactionEntity.setStatus(ITEM_CHECKED_OUT);
doNothing().when(circulationService).checkInByBarcode(any(), any(), any());
var context = TransactionStatusContext.builder()
.claimedReturnedResolution(ClaimedReturnedResolution.FOUND_BY_LIBRARY)
.build();
TransactionStatus transactionStatus = TransactionStatus.builder()
.status(ITEM_CHECKED_IN)
.context(context)
.build();
borrowingLibraryService.updateTransactionStatus(transactionEntity, transactionStatus);

verify(circulationService).checkInByBarcode(any(), any(), eq(ClaimedReturnedResolution.FOUND_BY_LIBRARY));
assertEquals(ITEM_CHECKED_IN, transactionEntity.getStatus());
}

@Test
void testTransactionStatusUpdateFromItemCheckedOutToItemCheckedInWithReturnedByPatron() {
var transactionEntity = createTransactionEntity();
transactionEntity.setStatus(ITEM_CHECKED_OUT);
doNothing().when(circulationService).checkInByBarcode(any(), any(), any());
var context = TransactionStatusContext.builder()
.claimedReturnedResolution(ClaimedReturnedResolution.RETURNED_BY_PATRON)
.build();
TransactionStatus transactionStatus = TransactionStatus.builder()
.status(ITEM_CHECKED_IN)
.context(context)
.build();
borrowingLibraryService.updateTransactionStatus(transactionEntity, transactionStatus);

verify(circulationService).checkInByBarcode(any(), any(), eq(ClaimedReturnedResolution.RETURNED_BY_PATRON));
assertEquals(ITEM_CHECKED_IN, transactionEntity.getStatus());
}
}
17 changes: 17 additions & 0 deletions src/test/java/org/folio/dcb/service/CirculationServiceTest.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package org.folio.dcb.service;

import static java.util.UUID.randomUUID;
import static org.folio.dcb.utils.EntityUtils.createCirculationRequest;
import static org.folio.dcb.utils.EntityUtils.createTransactionEntity;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.UUID;
import org.folio.dcb.domain.dto.CirculationRequest;
import org.folio.dcb.domain.dto.ClaimedReturnedResolution;
import org.folio.dcb.domain.entity.TransactionEntity;
import org.folio.dcb.exception.CirculationRequestException;
import org.folio.dcb.integration.circulation.CirculationClient;
Expand Down Expand Up @@ -42,6 +45,20 @@ void checkInByBarcodeWithServicePointTest() {
verify(circulationClient).checkInByBarcode(any());
}

@Test
void checkInByBarcodeWithServicePointAndClaimedReturnedResolutionTest() {
circulationService.checkInByBarcode(createTransactionEntity(), randomUUID().toString(),
ClaimedReturnedResolution.FOUND_BY_LIBRARY);
verify(circulationClient).checkInByBarcode(argThat(req ->
ClaimedReturnedResolution.FOUND_BY_LIBRARY.equals(req.getClaimedReturnedResolution())));
}

@Test
void checkInByBarcodeWithServicePointAndNullClaimedReturnedResolutionTest() {
circulationService.checkInByBarcode(createTransactionEntity(), randomUUID().toString(), null);
verify(circulationClient).checkInByBarcode(argThat(req -> req.getClaimedReturnedResolution() == null));
}

@Test
void shouldUpdateRequestApiWithIsDcbRerequestCancellationTrue() {
CirculationRequest fetchedRequest = createCirculationRequest();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"request": {
"method": "POST",
"urlPath": "/circulation/check-in-by-barcode",
"bodyPatterns": [
{
"equalToJson": {
"itemBarcode": "DCB_ITEM",
"claimedReturnedResolution": "Found by library"
},
"ignoreExtraElements": true
},
{ "matchesJsonPath": "$.checkInDate" },
{
"matchesJsonPath": {
"expression": "$.servicePointId",
"matches": "[0-9a-f-]{36}"
}
}
],
"headers": {
"x-okapi-tenant": {
"equalTo": "test_tenant"
}
}
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": {
"loan": {
"id": "cf23adf0-61ba-4887-bf82-956c4aae2260",
"userId": "df7f4993-8c14-4a0f-ab63-93975ab01c76",
"itemId": "5b95877d-86c0-4cb7-a0cd-7660b348ae5a",
"item": {
"title": "The Long Way to a Small, Angry Planet",
"barcode": "DCB_ITEM",
"status": {
"name": "Checked out"
}
},
"loanDate": "2017-03-01T23:11:00.000Z",
"dueDate": "2017-04-01T23:11:00.000Z",
"checkinServicePointId": "9d1b77e8-f02e-4b7f-b296-3f2042ddac55",
"status": {
"name": "Closed"
},
"action": "checkedin"
}
}
}
}
Loading
Loading