findTransactionByItemIdAndStatusNotInClosed(@Param("itemId") UUID itemId);
@Query(value = "SELECT * FROM transactions where item_id = :itemId AND status not in ('CLOSED', 'CANCELLED', 'ERROR')", nativeQuery = true)
diff --git a/src/main/java/org/folio/dcb/service/CirculationService.java b/src/main/java/org/folio/dcb/service/CirculationService.java
index 319c7e20..651955bb 100644
--- a/src/main/java/org/folio/dcb/service/CirculationService.java
+++ b/src/main/java/org/folio/dcb/service/CirculationService.java
@@ -17,5 +17,17 @@ public interface CirculationService {
*/
void checkOutByBarcode(TransactionEntity dcbTransaction);
- void cancelRequest(TransactionEntity dcbTransaction);
+ /**
+ * Cancels a transaction request based on the provided transaction details.
+ *
+ * If {@code isItemUnavailableCancellation} is {@code true}, the notification for this
+ * cancellation will be suppressed by setting the {@code suppressNotification} flag
+ * to {@code true}.
+ *
+ *
+ * @param dcbTransaction the transaction entity representing the request to be canceled
+ * @param isItemUnavailableCancellation a flag indicating whether the cancellation is due to item unavailability
+ * (true if the item is unavailable, false otherwise)
+ */
+ void cancelRequest(TransactionEntity dcbTransaction, boolean isItemUnavailableCancellation);
}
diff --git a/src/main/java/org/folio/dcb/service/RequestService.java b/src/main/java/org/folio/dcb/service/RequestService.java
index 49358cb8..8f73bc79 100644
--- a/src/main/java/org/folio/dcb/service/RequestService.java
+++ b/src/main/java/org/folio/dcb/service/RequestService.java
@@ -6,11 +6,11 @@
public interface RequestService {
/**
- * Create page item request
+ * Create request based on status of the item
* @param user - userEntity
* @param dcbItem - dcbItemEntity
*/
- CirculationRequest createPageItemRequest(User user, DcbItem dcbItem, String pickupServicePointId);
+ CirculationRequest createRequestBasedOnItemStatus(User user, DcbItem dcbItem, String pickupServicePointId);
CirculationRequest createHoldItemRequest(User user, DcbItem dcbItem, String pickupServicePointId);
void updateCirculationRequest(CirculationRequest circulationRequest);
}
diff --git a/src/main/java/org/folio/dcb/service/TransactionsService.java b/src/main/java/org/folio/dcb/service/TransactionsService.java
index 8d1fc190..549db7d7 100644
--- a/src/main/java/org/folio/dcb/service/TransactionsService.java
+++ b/src/main/java/org/folio/dcb/service/TransactionsService.java
@@ -1,6 +1,7 @@
package org.folio.dcb.service;
import org.folio.dcb.domain.dto.DcbTransaction;
+import org.folio.dcb.domain.dto.DcbUpdateTransaction;
import org.folio.dcb.domain.dto.TransactionStatus;
import org.folio.dcb.domain.dto.TransactionStatusResponse;
import org.folio.dcb.domain.dto.TransactionStatusResponseCollection;
@@ -17,5 +18,6 @@ public interface TransactionsService {
TransactionStatusResponse updateTransactionStatus(String dcbTransactionId, TransactionStatus transactionStatus);
TransactionStatusResponse getTransactionStatusById(String dcbTransactionId);
TransactionStatusResponseCollection getTransactionStatusList(OffsetDateTime fromDate, OffsetDateTime toDate, Integer pageNumber, Integer pageSize);
+ void updateTransactionDetails(String dcbTransactionId, DcbUpdateTransaction dcbUpdateTransaction);
}
diff --git a/src/main/java/org/folio/dcb/service/impl/BaseLibraryService.java b/src/main/java/org/folio/dcb/service/impl/BaseLibraryService.java
index dc06a555..bb0e42e9 100644
--- a/src/main/java/org/folio/dcb/service/impl/BaseLibraryService.java
+++ b/src/main/java/org/folio/dcb/service/impl/BaseLibraryService.java
@@ -5,7 +5,10 @@
import org.apache.commons.lang3.ObjectUtils;
import org.folio.dcb.domain.dto.CirculationItem;
import org.folio.dcb.domain.dto.CirculationRequest;
+import org.folio.dcb.domain.dto.DcbItem;
+import org.folio.dcb.domain.dto.DcbPatron;
import org.folio.dcb.domain.dto.DcbTransaction;
+import org.folio.dcb.domain.dto.DcbUpdateItem;
import org.folio.dcb.domain.dto.TransactionStatus;
import org.folio.dcb.domain.dto.TransactionStatusResponse;
import org.folio.dcb.domain.entity.TransactionEntity;
@@ -54,6 +57,7 @@ public TransactionStatusResponse createBorrowingLibraryTransaction(String dcbTra
}
checkItemExistsInInventoryAndThrow(itemVirtual.getBarcode());
CirculationItem item = circulationItemService.checkIfItemExistsAndCreate(itemVirtual, pickupServicePointId);
+ dcbTransaction.getItem().setId(item.getId());
checkOpenTransactionExistsAndThrow(item.getId());
CirculationRequest holdRequest = requestService.createHoldItemRequest(user, itemVirtual, pickupServicePointId);
saveDcbTransaction(dcbTransactionId, dcbTransaction, holdRequest.getId());
@@ -106,8 +110,10 @@ public void updateTransactionStatus(TransactionEntity dcbTransaction, Transactio
public void cancelTransactionRequest(TransactionEntity transactionEntity){
try {
- circulationService.cancelRequest(transactionEntity);
+ circulationService.cancelRequest(transactionEntity, false);
} catch (CirculationRequestException e) {
+ log.error("cancelTransactionRequest:: error during updating circulation request " +
+ "to cancel status", e);
updateTransactionEntity(transactionEntity, TransactionStatus.StatusEnum.ERROR);
}
}
@@ -133,4 +139,28 @@ public void updateTransactionEntity(TransactionEntity transactionEntity, Transac
transactionEntity.setStatus(transactionStatusEnum);
transactionRepository.save(transactionEntity);
}
+
+ public void updateTransactionDetails(TransactionEntity transactionEntity, DcbUpdateItem dcbUpdateItem) {
+ DcbPatron dcbPatron = transactionMapper.mapTransactionEntityToDcbPatron(transactionEntity);
+ DcbItem dcbItem = transactionMapper.convertTransactionUpdateItemToDcbItem(dcbUpdateItem, transactionEntity);
+ checkItemExistsInInventoryAndThrow(dcbItem.getBarcode());
+ CirculationItem item = circulationItemService.checkIfItemExistsAndCreate(dcbItem, transactionEntity.getServicePointId());
+ dcbItem.setId(item.getId());
+ checkOpenTransactionExistsAndThrow(item.getId());
+ circulationService.cancelRequest(transactionEntity, true);
+ CirculationRequest holdRequest = requestService.createHoldItemRequest(userService.fetchUser(dcbPatron), dcbItem,
+ transactionEntity.getServicePointId());
+ updateItemDetailsAndSaveEntity(transactionEntity, item, dcbItem.getMaterialType(), holdRequest.getId());
+ }
+
+ private void updateItemDetailsAndSaveEntity(TransactionEntity transactionEntity, CirculationItem item,
+ String materialType, String requestId) {
+ transactionEntity.setItemId(item.getId());
+ transactionEntity.setRequestId(UUID.fromString(requestId));
+ transactionEntity.setItemBarcode(item.getBarcode());
+ transactionEntity.setLendingLibraryCode(item.getLendingLibraryCode());
+ transactionEntity.setMaterialType(materialType);
+ transactionEntity.setStatus(CREATED);
+ transactionRepository.save(transactionEntity);
+ }
}
diff --git a/src/main/java/org/folio/dcb/service/impl/CirculationItemServiceImpl.java b/src/main/java/org/folio/dcb/service/impl/CirculationItemServiceImpl.java
index fe53443d..2e782a87 100644
--- a/src/main/java/org/folio/dcb/service/impl/CirculationItemServiceImpl.java
+++ b/src/main/java/org/folio/dcb/service/impl/CirculationItemServiceImpl.java
@@ -9,9 +9,11 @@
import org.folio.dcb.domain.dto.ItemStatus;
import org.folio.dcb.service.CirculationItemService;
import org.folio.dcb.service.ItemService;
+import org.folio.util.StringUtil;
import org.springframework.stereotype.Service;
import java.util.Objects;
+import java.util.UUID;
import static org.folio.dcb.domain.dto.ItemStatus.NameEnum.IN_TRANSIT;
import static org.folio.dcb.utils.DCBConstants.HOLDING_ID;
@@ -29,18 +31,18 @@ public class CirculationItemServiceImpl implements CirculationItemService {
@Override
public CirculationItem checkIfItemExistsAndCreate(DcbItem dcbItem, String pickupServicePointId) {
- var dcbItemId = dcbItem.getId();
- log.debug("checkIfItemExistsAndCreate:: generate Circulation item by DcbItem with id={} if nit doesn't exist.", dcbItemId);
- var circulationItem = fetchCirculationItemByIdAndBarcode(dcbItemId, dcbItem.getBarcode());
+ var dcbItemBarcode = dcbItem.getBarcode();
+ log.debug("checkIfItemExistsAndCreate:: generate Circulation item with barcode {} if it doesn't exist.", dcbItemBarcode);
+ var circulationItem = fetchCirculationItemByBarcode(dcbItem.getBarcode());
if(Objects.isNull(circulationItem)) {
- log.warn("Circulation item not found by id={}. Creating it.", dcbItemId);
+ log.warn("checkIfItemExistsAndCreate:: Circulation item not found by barcode={}. Creating it.", dcbItemBarcode);
circulationItem = createCirculationItem(dcbItem, pickupServicePointId);
}
return circulationItem;
}
- private CirculationItem fetchCirculationItemByIdAndBarcode(String id, String barcode) {
- return circulationItemClient.fetchItemByIdAndBarcode("id==" + id + " and barcode==" + barcode)
+ private CirculationItem fetchCirculationItemByBarcode(String barcode) {
+ return circulationItemClient.fetchItemByCqlQuery("barcode==" + StringUtil.cqlEncode(barcode))
.getItems()
.stream()
.findFirst()
@@ -56,10 +58,10 @@ private CirculationItem createCirculationItem(DcbItem item, String pickupService
//SetupDefaultMaterialTypeIfNotGiven
String materialType = StringUtils.isBlank(item.getMaterialType()) ? MATERIAL_TYPE_NAME_BOOK : item.getMaterialType();
var materialTypeId = itemService.fetchItemMaterialTypeIdByMaterialTypeName(materialType);
-
+ var itemId = UUID.randomUUID().toString();
CirculationItem circulationItem =
CirculationItem.builder()
- .id(item.getId())
+ .id(itemId)
.barcode(item.getBarcode())
.status(ItemStatus.builder()
.name(IN_TRANSIT)
@@ -72,6 +74,6 @@ private CirculationItem createCirculationItem(DcbItem item, String pickupService
.lendingLibraryCode(item.getLendingLibraryCode())
.build();
- return circulationItemClient.createCirculationItem(item.getId(), circulationItem);
+ return circulationItemClient.createCirculationItem(itemId, circulationItem);
}
}
diff --git a/src/main/java/org/folio/dcb/service/impl/CirculationServiceImpl.java b/src/main/java/org/folio/dcb/service/impl/CirculationServiceImpl.java
index 9934385f..b0159392 100644
--- a/src/main/java/org/folio/dcb/service/impl/CirculationServiceImpl.java
+++ b/src/main/java/org/folio/dcb/service/impl/CirculationServiceImpl.java
@@ -41,11 +41,14 @@ public void checkOutByBarcode(TransactionEntity dcbTransaction) {
}
@Override
- public void cancelRequest(TransactionEntity dcbTransaction) {
+ public void cancelRequest(TransactionEntity dcbTransaction, boolean isItemUnavailableCancellation) {
log.debug("cancelRequest:: cancelling request using request id {} ", dcbTransaction.getRequestId());
CirculationRequest request = circulationStorageService.getCancellationRequestIfOpenOrNull(dcbTransaction.getRequestId().toString());
if (request != null){
try {
+ if (isItemUnavailableCancellation) {
+ request.setIsDcbReRequestCancellation(true);
+ }
circulationClient.updateRequest(request.getId(), request);
} catch (FeignException e) {
log.warn("cancelRequest:: error cancelling request using request id {} ", dcbTransaction.getRequestId(), e);
diff --git a/src/main/java/org/folio/dcb/service/impl/LendingLibraryServiceImpl.java b/src/main/java/org/folio/dcb/service/impl/LendingLibraryServiceImpl.java
index de65af3e..538bf473 100644
--- a/src/main/java/org/folio/dcb/service/impl/LendingLibraryServiceImpl.java
+++ b/src/main/java/org/folio/dcb/service/impl/LendingLibraryServiceImpl.java
@@ -47,7 +47,7 @@ public TransactionStatusResponse createCirculation(String dcbTransactionId, DcbT
baseLibraryService.checkUserTypeAndThrowIfMismatch(user.getType());
ServicePointRequest pickupServicePoint = servicePointService.createServicePointIfNotExists(dcbTransaction.getPickup());
dcbTransaction.getPickup().setServicePointId(pickupServicePoint.getId());
- CirculationRequest pageRequest = requestService.createPageItemRequest(user, item, pickupServicePoint.getId());
+ CirculationRequest pageRequest = requestService.createRequestBasedOnItemStatus(user, item, pickupServicePoint.getId());
baseLibraryService.saveDcbTransaction(dcbTransactionId, dcbTransaction, pageRequest.getId());
return TransactionStatusResponse.builder()
diff --git a/src/main/java/org/folio/dcb/service/impl/PickupLibraryServiceImpl.java b/src/main/java/org/folio/dcb/service/impl/PickupLibraryServiceImpl.java
index 88a5495d..677235bb 100644
--- a/src/main/java/org/folio/dcb/service/impl/PickupLibraryServiceImpl.java
+++ b/src/main/java/org/folio/dcb/service/impl/PickupLibraryServiceImpl.java
@@ -33,6 +33,7 @@ public TransactionStatusResponse createCirculation(String dcbTransactionId, DcbT
baseLibraryService.checkUserTypeAndThrowIfMismatch(user.getType());
baseLibraryService.checkItemExistsInInventoryAndThrow(itemVirtual.getBarcode());
CirculationItem item = circulationItemService.checkIfItemExistsAndCreate(itemVirtual, dcbTransaction.getPickup().getServicePointId());
+ dcbTransaction.getItem().setId(item.getId());
baseLibraryService.checkOpenTransactionExistsAndThrow(item.getId());
CirculationRequest holdRequest = requestService.createHoldItemRequest(user, itemVirtual, dcbTransaction.getPickup().getServicePointId());
baseLibraryService.saveDcbTransaction(dcbTransactionId, dcbTransaction, holdRequest.getId());
diff --git a/src/main/java/org/folio/dcb/service/impl/RequestServiceImpl.java b/src/main/java/org/folio/dcb/service/impl/RequestServiceImpl.java
index f8fe764f..7957a403 100644
--- a/src/main/java/org/folio/dcb/service/impl/RequestServiceImpl.java
+++ b/src/main/java/org/folio/dcb/service/impl/RequestServiceImpl.java
@@ -2,8 +2,10 @@
import static org.folio.dcb.domain.dto.CirculationRequest.RequestTypeEnum.PAGE;
import static org.folio.dcb.domain.dto.CirculationRequest.RequestTypeEnum.HOLD;
+import static org.folio.dcb.domain.dto.ItemStatus.NameEnum.AVAILABLE;
import static org.folio.dcb.utils.DCBConstants.HOLDING_ID;
import static org.folio.dcb.utils.DCBConstants.INSTANCE_ID;
+import static org.folio.dcb.utils.DCBConstants.holdItemStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
@@ -13,6 +15,7 @@
import org.folio.dcb.domain.dto.Item;
import org.folio.dcb.domain.dto.Requester;
import org.folio.dcb.domain.dto.User;
+import org.folio.dcb.exception.StatusException;
import org.folio.dcb.service.HoldingsService;
import org.folio.dcb.service.ItemService;
import org.folio.dcb.service.RequestService;
@@ -31,13 +34,25 @@ public class RequestServiceImpl implements RequestService {
private final CirculationClient circulationClient;
@Override
- public CirculationRequest createPageItemRequest(User user, DcbItem item, String pickupServicePointId) {
- log.debug("createPageItemRequest:: creating a new page request for userBarcode {} , itemBarcode {}",
+ public CirculationRequest createRequestBasedOnItemStatus(User user, DcbItem item, String pickupServicePointId) {
+ log.debug("createRequestBasedOnItemStatus:: creating a new request for userBarcode {} , itemBarcode {}",
user.getBarcode(), item.getBarcode());
var inventoryItem = itemService.fetchItemByIdAndBarcode(item.getId(), item.getBarcode());
var inventoryHolding = holdingsService.fetchInventoryHoldingDetailsByHoldingId(inventoryItem.getHoldingsRecordId());
- var circulationRequest = createCirculationRequest(PAGE, user, item, inventoryItem.getHoldingsRecordId(), inventoryHolding.getInstanceId(), pickupServicePointId);
- return circulationClient.createRequest(circulationRequest);
+ var inventoryItemStatus = inventoryItem.getStatus().getName();
+ if (inventoryItemStatus.equals(AVAILABLE)) {
+ log.info("createRequestBasedOnItemStatus:: Creating page request for item with barcode {}", item.getBarcode());
+ var circulationRequest = createCirculationRequest(PAGE, user, item, inventoryItem.getHoldingsRecordId(), inventoryHolding.getInstanceId(), pickupServicePointId);
+ return circulationClient.createRequest(circulationRequest);
+ } else if (holdItemStatus.contains(inventoryItemStatus)) {
+ log.info("createRequestBasedOnItemStatus:: Creating hold request for item with barcode {}", item.getBarcode());
+ var circulationRequest = createCirculationRequest(HOLD, user, item, inventoryItem.getHoldingsRecordId(), inventoryHolding.getInstanceId(), pickupServicePointId);
+ return circulationClient.createRequest(circulationRequest);
+ } else {
+ String errorMsg = String.format("Request will not be created for item barcode %s as it is %s", item.getBarcode(), inventoryItemStatus);
+ log.error(errorMsg);
+ throw new StatusException(errorMsg);
+ }
}
@Override
diff --git a/src/main/java/org/folio/dcb/service/impl/TransactionsServiceImpl.java b/src/main/java/org/folio/dcb/service/impl/TransactionsServiceImpl.java
index 64b41d20..050c3176 100644
--- a/src/main/java/org/folio/dcb/service/impl/TransactionsServiceImpl.java
+++ b/src/main/java/org/folio/dcb/service/impl/TransactionsServiceImpl.java
@@ -3,6 +3,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.folio.dcb.domain.dto.DcbTransaction;
+import org.folio.dcb.domain.dto.DcbUpdateTransaction;
import org.folio.dcb.domain.dto.TransactionStatus;
import org.folio.dcb.domain.dto.TransactionStatusResponse;
import org.folio.dcb.domain.dto.TransactionStatusResponseCollection;
@@ -39,6 +40,7 @@ public class TransactionsServiceImpl implements TransactionsService {
private final StatusProcessorService statusProcessorService;
private final TransactionMapper transactionMapper;
private final TransactionAuditRepository transactionAuditRepository;
+ private final BaseLibraryService baseLibraryService;
@Override
public TransactionStatusResponse createCirculationRequest(String dcbTransactionId, DcbTransaction dcbTransaction) {
@@ -109,6 +111,19 @@ public TransactionStatusResponseCollection getTransactionStatusList(OffsetDateTi
.build();
}
+ @Override
+ public void updateTransactionDetails(String dcbTransactionId, DcbUpdateTransaction dcbUpdateTransaction) {
+ var transactionEntity = getTransactionEntityOrThrow(dcbTransactionId);
+ if (!TransactionStatus.StatusEnum.CREATED.equals(transactionEntity.getStatus())) {
+ throw new StatusException(String.format(
+ "Transaction details should not be updated from %s status, it can be updated only from CREATED status", transactionEntity.getStatus()));
+ }
+ if (DcbTransaction.RoleEnum.LENDER.equals(transactionEntity.getRole())) {
+ throw new IllegalArgumentException("Item details cannot be updated for lender role");
+ }
+ baseLibraryService.updateTransactionDetails(transactionEntity, dcbUpdateTransaction.getItem());
+ }
+
private TransactionStatusResponse generateTransactionStatusResponseFromTransactionEntity(TransactionEntity transactionEntity) {
TransactionStatus.StatusEnum transactionStatus = transactionEntity.getStatus();
TransactionStatusResponse.StatusEnum transactionStatusResponseStatusEnum = TransactionStatusResponse.StatusEnum.fromValue(transactionStatus.getValue());
diff --git a/src/main/java/org/folio/dcb/utils/DCBConstants.java b/src/main/java/org/folio/dcb/utils/DCBConstants.java
index 12bca85e..acf871b1 100644
--- a/src/main/java/org/folio/dcb/utils/DCBConstants.java
+++ b/src/main/java/org/folio/dcb/utils/DCBConstants.java
@@ -1,5 +1,15 @@
package org.folio.dcb.utils;
+import org.folio.dcb.domain.dto.ItemStatus;
+
+import java.util.List;
+
+import static org.folio.dcb.domain.dto.ItemStatus.NameEnum.AWAITING_DELIVERY;
+import static org.folio.dcb.domain.dto.ItemStatus.NameEnum.AWAITING_PICKUP;
+import static org.folio.dcb.domain.dto.ItemStatus.NameEnum.CHECKED_OUT;
+import static org.folio.dcb.domain.dto.ItemStatus.NameEnum.IN_TRANSIT;
+import static org.folio.dcb.domain.dto.ItemStatus.NameEnum.PAGED;
+
public class DCBConstants {
private DCBConstants() {}
@@ -26,4 +36,5 @@ private DCBConstants() {}
public static final String SHADOW_TYPE = "shadow";
public static final String HOLDING_SOURCE = "folio";
public static final String DCB_CALENDAR_NAME = "DCB Calendar";
+ public static final List holdItemStatus = List.of(IN_TRANSIT, CHECKED_OUT, PAGED, AWAITING_PICKUP, AWAITING_DELIVERY);
}
diff --git a/src/main/java/org/folio/dcb/utils/TransactionHelper.java b/src/main/java/org/folio/dcb/utils/TransactionHelper.java
index 6468ad25..02e29dfd 100644
--- a/src/main/java/org/folio/dcb/utils/TransactionHelper.java
+++ b/src/main/java/org/folio/dcb/utils/TransactionHelper.java
@@ -7,10 +7,13 @@
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import static org.folio.dcb.utils.KafkaEvent.ACTION;
import static org.folio.dcb.utils.KafkaEvent.STATUS;
+import com.fasterxml.jackson.databind.JsonNode;
+
@Log4j2
public class TransactionHelper {
private static final String LOAN_ACTION_CHECKED_OUT = "checkedout";
@@ -57,6 +60,8 @@ public static EventData parseRequestEvent(String eventPayload){
&& kafkaEvent.getNewNode().has(STATUS)){
EventData eventData = new EventData();
eventData.setRequestId(kafkaEvent.getNewNode().get("id").asText());
+ eventData.setDcbReRequestCancellation(
+ getNodeAsBooleanOrDefault(kafkaEvent, "dcbReRequestCancellation", false));
RequestStatus requestStatus = RequestStatus.from(kafkaEvent.getNewNode().get(STATUS).asText());
switch (requestStatus) {
case OPEN_IN_TRANSIT -> eventData.setType(EventData.EventType.IN_TRANSIT);
@@ -69,6 +74,16 @@ public static EventData parseRequestEvent(String eventPayload){
}
return null;
}
+
+ private static boolean getNodeAsBooleanOrDefault(KafkaEvent kafkaEvent, String name,
+ boolean defaultValue) {
+
+ JsonNode booleanNode = kafkaEvent.getNewNode().get(name);
+ return Objects.nonNull(booleanNode)
+ ? booleanNode.asBoolean()
+ : defaultValue;
+ }
+
private static boolean checkDcbRequest(KafkaEvent kafkaEvent) {
return (kafkaEvent.getNewNode().has(INSTANCE) && kafkaEvent.getNewNode().get(INSTANCE).has(TITLE)
&& kafkaEvent.getNewNode().get(INSTANCE).get(TITLE).asText().equals(DCB_INSTANCE_TITLE)) || (kafkaEvent.getNewNode().has(REQUESTER)
diff --git a/src/main/resources/swagger.api/dcb_transaction.yaml b/src/main/resources/swagger.api/dcb_transaction.yaml
index 5871cfa8..d8b0b562 100644
--- a/src/main/resources/swagger.api/dcb_transaction.yaml
+++ b/src/main/resources/swagger.api/dcb_transaction.yaml
@@ -24,6 +24,22 @@ paths:
$ref: '#/components/responses/Conflict'
'500':
$ref: '#/components/responses/InternalServerError'
+ put:
+ description: Update the details of a transaction
+ operationId: updateTransactionDetails
+ tags:
+ - circulation
+ parameters:
+ - $ref: '#/components/parameters/dcbTransactionId'
+ requestBody:
+ $ref: "#/components/requestBodies/DcbUpdateTransaction"
+ responses:
+ '204':
+ description: 'Transaction updated successfully'
+ '404':
+ $ref: '#/components/responses/NotFound'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
/transactions/{dcbTransactionId}/status:
parameters:
- $ref: '#/components/parameters/dcbTransactionId'
@@ -110,6 +126,13 @@ components:
application/json:
schema:
$ref: "schemas/dcbTransaction.yaml#/DcbTransaction"
+ DcbUpdateTransaction:
+ description: DCB transaction update object
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: "schemas/dcbUpdateTransaction.yaml#/DcbUpdateTransaction"
responses:
TransactionStatus:
description: Transaction Status object
diff --git a/src/main/resources/swagger.api/schemas/CirculationRequest.yaml b/src/main/resources/swagger.api/schemas/CirculationRequest.yaml
index c45a1fd3..a26f8555 100644
--- a/src/main/resources/swagger.api/schemas/CirculationRequest.yaml
+++ b/src/main/resources/swagger.api/schemas/CirculationRequest.yaml
@@ -78,6 +78,9 @@ CirculationRequest:
pickupServicePointId:
description: The ID of the Service Point where this request can be picked up
type: string
+ isDcbReRequestCancellation:
+ description: Indicates whether the request was cancelled during a DCB transaction update
+ type: boolean
item:
$ref: CirculationRequest.yaml#/item
requester:
diff --git a/src/main/resources/swagger.api/schemas/dcbUpdateItem.yaml b/src/main/resources/swagger.api/schemas/dcbUpdateItem.yaml
new file mode 100644
index 00000000..8c6914b1
--- /dev/null
+++ b/src/main/resources/swagger.api/schemas/dcbUpdateItem.yaml
@@ -0,0 +1,18 @@
+DcbUpdateItem:
+ description: Item metadata required for updating the existing transaction
+ type: object
+ properties:
+ barcode:
+ description: The barcode of the item as specified in the lending library
+ type: string
+ materialType:
+ description: The “hub-normalized” form of the item item type, used in the circulation rules for determining the correct loan policy.
+ type: string
+ lendingLibraryCode:
+ description: The code which identifies the lending library
+ type: string
+ additionalProperties: false
+ required:
+ - barcode
+ - materialType
+ - lendingLibraryCode
diff --git a/src/main/resources/swagger.api/schemas/dcbUpdateTransaction.yaml b/src/main/resources/swagger.api/schemas/dcbUpdateTransaction.yaml
new file mode 100644
index 00000000..c08ed5c1
--- /dev/null
+++ b/src/main/resources/swagger.api/schemas/dcbUpdateTransaction.yaml
@@ -0,0 +1,6 @@
+DcbUpdateTransaction:
+ type: object
+ properties:
+ item:
+ $ref: 'dcbUpdateItem.yaml#/DcbUpdateItem'
+
diff --git a/src/test/java/org/folio/dcb/controller/TransactionApiControllerTest.java b/src/test/java/org/folio/dcb/controller/TransactionApiControllerTest.java
index 5edbc7ff..ab8b6dd7 100644
--- a/src/test/java/org/folio/dcb/controller/TransactionApiControllerTest.java
+++ b/src/test/java/org/folio/dcb/controller/TransactionApiControllerTest.java
@@ -1,15 +1,16 @@
package org.folio.dcb.controller;
+import com.jayway.jsonpath.JsonPath;
import org.folio.dcb.domain.dto.DcbItem;
import org.folio.dcb.domain.dto.TransactionStatus;
import org.folio.dcb.domain.entity.TransactionAuditEntity;
import org.folio.dcb.repository.TransactionAuditRepository;
import org.folio.dcb.repository.TransactionRepository;
import org.folio.spring.service.SystemUserScopedExecutionService;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MvcResult;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
@@ -19,6 +20,7 @@
import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.BORROWING_PICKUP;
import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.LENDER;
import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.PICKUP;
+import static org.folio.dcb.utils.EntityUtils.DCB_NEW_BARCODE;
import static org.folio.dcb.utils.EntityUtils.DCB_TRANSACTION_ID;
import static org.folio.dcb.utils.EntityUtils.DCB_TYPE_USER_ID;
import static org.folio.dcb.utils.EntityUtils.EXISTED_INVENTORY_ITEM_BARCODE;
@@ -28,6 +30,7 @@
import static org.folio.dcb.utils.EntityUtils.PATRON_TYPE_USER_ID;
import static org.folio.dcb.utils.EntityUtils.createDcbItem;
import static org.folio.dcb.utils.EntityUtils.createDcbPatronWithExactPatronId;
+import static org.folio.dcb.utils.EntityUtils.createDcbTransactionUpdate;
import static org.folio.dcb.utils.EntityUtils.createDefaultDcbPatron;
import static org.folio.dcb.utils.EntityUtils.createDcbTransactionByRole;
import static org.folio.dcb.utils.EntityUtils.createTransactionEntity;
@@ -35,6 +38,9 @@
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInRelativeOrder;
import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
@@ -87,9 +93,9 @@ void createLendingCirculationRequestTest() throws Exception {
() -> {
TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(DCB_TRANSACTION_ID)
.orElse(null);
- Assertions.assertNotNull(auditExisting);
- Assertions.assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction());
- Assertions.assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); }
+ assertNotNull(auditExisting);
+ assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction());
+ assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); }
);
}
@@ -162,9 +168,9 @@ void createBorrowingPickupCirculationRequestTest() throws Exception {
() -> {
TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(DCB_TRANSACTION_ID)
.orElse(null);
- Assertions.assertNotNull(auditExisting);
- Assertions.assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction());
- Assertions.assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); }
+ assertNotNull(auditExisting);
+ assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction());
+ assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); }
);
}
@@ -190,8 +196,8 @@ void createLendingCirculationRequestWithInvalidItemId() throws Exception {
() -> {
TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(trnId)
.orElse(null);
- Assertions.assertNotNull(auditExisting);
- Assertions.assertEquals(TRANSACTION_AUDIT_ERROR_ACTION, auditExisting.getAction()); }
+ assertNotNull(auditExisting);
+ assertEquals(TRANSACTION_AUDIT_ERROR_ACTION, auditExisting.getAction()); }
);
}
@@ -216,8 +222,8 @@ void createBorrowingPickupCirculationRequestWithInvalidDefaultNotExistedPatronId
() -> {
TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(trnId)
.orElse(null);
- Assertions.assertNotNull(auditExisting);
- Assertions.assertEquals(TRANSACTION_AUDIT_ERROR_ACTION, auditExisting.getAction()); }
+ assertNotNull(auditExisting);
+ assertEquals(TRANSACTION_AUDIT_ERROR_ACTION, auditExisting.getAction()); }
);
}
@@ -455,9 +461,9 @@ void createTransactionForPickupLibrary() throws Exception {
() -> {
TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(DCB_TRANSACTION_ID)
.orElse(null);
- Assertions.assertNotNull(auditExisting);
- Assertions.assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction());
- Assertions.assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); }
+ assertNotNull(auditExisting);
+ assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction());
+ assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); }
);
}
@@ -513,9 +519,9 @@ void createBorrowerCirculationRequestTest() throws Exception {
() -> {
TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(DCB_TRANSACTION_ID)
.orElse(null);
- Assertions.assertNotNull(auditExisting);
- Assertions.assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction());
- Assertions.assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); }
+ assertNotNull(auditExisting);
+ assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction());
+ assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); }
);
}
@@ -537,7 +543,10 @@ void createBorrowerCirculationRequestWithoutExistingItemTest() throws Exception
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.status").value("CREATED"))
- .andExpect(jsonPath("$.item").value(dcbItem))
+ .andExpect(jsonPath("$.item.barcode").value(dcbItem.getBarcode()))
+ .andExpect(jsonPath("$.item.materialType").value(dcbItem.getMaterialType()))
+ .andExpect(jsonPath("$.item.lendingLibraryCode").value(dcbItem.getLendingLibraryCode()))
+ .andExpect(jsonPath("$.item.title").value(dcbItem.getTitle()))
.andExpect(jsonPath("$.patron").value(createDcbPatronWithExactPatronId(EXISTED_PATRON_ID)));
//Trying to create another transaction with same transaction id
@@ -983,9 +992,58 @@ void getTransactionStatusUpdateListTest() throws Exception {
.andExpect(jsonPath("$.maximumPageNumber", is(2)))
.andExpect(jsonPath("$.transactions[*].status",
containsInRelativeOrder("ITEM_CHECKED_OUT", "ITEM_CHECKED_IN")));
+ }
+ @Test
+ void createAndUpdateBorrowerTransactionTest() throws Exception {
+ removeExistedTransactionFromDbIfSoExists();
+ removeExistingTransactionsByItemId(ITEM_ID);
+
+ MvcResult result = this.mockMvc.perform(
+ post("/transactions/" + DCB_TRANSACTION_ID)
+ .content(asJsonString(createDcbTransactionByRole(BORROWER)))
+ .headers(defaultHeaders())
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isCreated())
+ .andExpect(jsonPath("$.status").value("CREATED"))
+ .andExpect(jsonPath("$.item").value(createDcbItem()))
+ .andExpect(jsonPath("$.patron").value(createDcbPatronWithExactPatronId(EXISTED_PATRON_ID)))
+ .andReturn(); // Capture the response for assertion
+
+ String responseContent = result.getResponse().getContentAsString();
+ String itemId = JsonPath.parse(responseContent).read("$.item.id", String.class);
+ String itemBarcode = JsonPath.parse(responseContent).read("$.item.barcode", String.class);
+ String lendingLibraryCode = JsonPath.parse(responseContent).read("$.item.lendingLibraryCode", String.class);
+ String materialType = JsonPath.parse(responseContent).read("$.item.materialType", String.class);
+
+ //Trying to update the transaction with same transaction id
+ this.mockMvc.perform(
+ put("/transactions/" + DCB_TRANSACTION_ID)
+ .content(asJsonString(createDcbTransactionUpdate()))
+ .headers(defaultHeaders())
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpectAll(status().isNoContent());
+
+ // check whether item related data is updated
+ systemUserScopedExecutionService.executeAsyncSystemUserScoped(
+ TENANT,
+ () -> {
+ var transactionEntity = transactionRepository.findById(DCB_TRANSACTION_ID)
+ .orElse(null);
+ assertNotNull(transactionEntity);
+ assertNotEquals(itemId, transactionEntity.getItemId());
+ assertNotEquals(itemBarcode, transactionEntity.getItemBarcode());
+ assertEquals(DCB_NEW_BARCODE, transactionEntity.getItemBarcode());
+ assertNotEquals(lendingLibraryCode, transactionEntity.getLendingLibraryCode());
+ assertEquals("LEN", transactionEntity.getLendingLibraryCode());
+ assertNotEquals(materialType, transactionEntity.getMaterialType());
+ assertEquals("DVD", transactionEntity.getMaterialType());
+ });
}
+
private void removeExistedTransactionFromDbIfSoExists() {
systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> {
if (transactionRepository.existsById(DCB_TRANSACTION_ID)){
diff --git a/src/test/java/org/folio/dcb/listener/CirculationRequestEventListenerTest.java b/src/test/java/org/folio/dcb/listener/CirculationRequestEventListenerTest.java
index ba2ac32f..500bad1f 100644
--- a/src/test/java/org/folio/dcb/listener/CirculationRequestEventListenerTest.java
+++ b/src/test/java/org/folio/dcb/listener/CirculationRequestEventListenerTest.java
@@ -8,6 +8,9 @@
import org.folio.dcb.service.CirculationItemService;
import org.folio.spring.integration.XOkapiHeaders;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -17,6 +20,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
+import java.util.stream.Stream;
import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.*;
import static org.folio.dcb.utils.EntityUtils.createCirculationItem;
@@ -36,8 +40,13 @@ class CirculationRequestEventListenerTest extends BaseIT {
private static final String CHECK_IN_TRANSIT_EVENT_FOR_DCB_SAMPLE = getMockDataAsString("mockdata/kafka/check_in_transit_dcb.json");
private static final String CHECK_IN_UNDEFINED_EVENT_SAMPLE = getMockDataAsString("mockdata/kafka/request_undefined.json");
private static final String REQUEST_CANCEL_EVENT_SAMPLE = getMockDataAsString("mockdata/kafka/cancel_request.json");
-
private static final String REQUEST_CANCEL_EVENT_FOR_DCB_SAMPLE = getMockDataAsString("mockdata/kafka/cancel_request_dcb.json");
+ private static final String CANCELLATION_DCB_REREQUEST_TRUE_SAMPLE = getMockDataAsString(
+ "mockdata/kafka/cancellation_dcb_rerequest_true.json");
+ private static final String CANCELLATION_DCB_REREQUEST_FALSE_SAMPLE = getMockDataAsString(
+ "mockdata/kafka/cancellation_dcb_rerequest_false.json");
+ private static final String CANCELLATION_DCB_REREQUEST_WITHOUT_SAMPLE = getMockDataAsString(
+ "mockdata/kafka/cancellation_dcb_rerequest_without_dcb_rerequest_property.json");
@Autowired
private CirculationEventListener eventListener ;
@@ -54,6 +63,26 @@ void handleNonDcbRequestTest() {
eventListener.handleRequestEvent(REQUEST_EVENT_SAMPLE_NON_DCB, messageHeaders);
Mockito.verify(transactionRepository, times(0)).save(any());
}
+
+ @ParameterizedTest
+ @MethodSource("pathToExecutionTimes")
+ void handleCancelRequestEventWhenTransactionDcbUpdates(String path, int executionTimes) {
+ var transactionEntity = createTransactionEntity();
+ MessageHeaders messageHeaders = getMessageHeaders();
+ when(transactionRepository.findTransactionByRequestIdAndStatusNotInClosed(any()))
+ .thenReturn(Optional.of(transactionEntity));
+ eventListener.handleRequestEvent(path, messageHeaders);
+ Mockito.verify(transactionRepository, times(executionTimes)).save(any());
+ }
+
+ private static Stream pathToExecutionTimes() {
+ return Stream.of(
+ Arguments.of(CANCELLATION_DCB_REREQUEST_FALSE_SAMPLE, 1),
+ Arguments.of(CANCELLATION_DCB_REREQUEST_TRUE_SAMPLE, 0),
+ Arguments.of(CANCELLATION_DCB_REREQUEST_WITHOUT_SAMPLE, 1)
+ );
+ }
+
@Test
void handleCheckInEventInPickupForDcbFromOpenToAwaitingPickupTest() {
var transactionEntity = createTransactionEntity();
@@ -104,7 +133,6 @@ void handleCancelRequestForDcbTest() {
Mockito.verify(transactionRepository).save(any());
}
-
@Test
void handleOpenRequestTest() {
var transactionEntity = createTransactionEntity();
diff --git a/src/test/java/org/folio/dcb/service/BaseLibraryServiceTest.java b/src/test/java/org/folio/dcb/service/BaseLibraryServiceTest.java
index 82853ca4..32241206 100644
--- a/src/test/java/org/folio/dcb/service/BaseLibraryServiceTest.java
+++ b/src/test/java/org/folio/dcb/service/BaseLibraryServiceTest.java
@@ -39,6 +39,7 @@
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.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.times;
@@ -110,15 +111,18 @@ void createBorrowingTransactionTest() {
var patron = createDcbPatronWithExactPatronId(EXISTED_PATRON_ID);
var user = createUser();
user.setType("shadow");
+ var circulationItem = createCirculationItem();
when(userService.fetchUser(any()))
.thenReturn(user);
when(requestService.createHoldItemRequest(any(), any(), anyString())).thenReturn(createCirculationRequest());
when(transactionMapper.mapToEntity(any(), any())).thenReturn(createTransactionEntity());
when(itemService.fetchItemByBarcode(item.getBarcode())).thenReturn(new ResultList<>());
- when(circulationItemService.checkIfItemExistsAndCreate(any(), any())).thenReturn(createCirculationItem());
+ when(circulationItemService.checkIfItemExistsAndCreate(any(), any())).thenReturn(circulationItem);
var response = baseLibraryService.createBorrowingLibraryTransaction(DCB_TRANSACTION_ID, createDcbTransactionByRole(BORROWER), PICKUP_SERVICE_POINT_ID);
verify(userService).fetchUser(patron);
+ // Circulation item id will be set as dcb item id in the code, hence setting it for assertion
+ item.setId(circulationItem.getId());
verify(requestService).createHoldItemRequest(user, item, PICKUP_SERVICE_POINT_ID);
verify(transactionRepository).save(any());
Assertions.assertEquals(TransactionStatusResponse.StatusEnum.CREATED, response.getStatus());
@@ -185,7 +189,7 @@ void testTransactionCancelTest(){
transactionEntity.setStatus(OPEN);
TransactionStatus transactionStatus = TransactionStatus.builder().status(CANCELLED).build();
baseLibraryService.updateTransactionStatus(transactionEntity, transactionStatus);
- verify(circulationService).cancelRequest(any());
+ verify(circulationService).cancelRequest(any(), eq(false));
}
@Test
diff --git a/src/test/java/org/folio/dcb/service/CirculationServiceTest.java b/src/test/java/org/folio/dcb/service/CirculationServiceTest.java
index eb461539..8ec991ae 100644
--- a/src/test/java/org/folio/dcb/service/CirculationServiceTest.java
+++ b/src/test/java/org/folio/dcb/service/CirculationServiceTest.java
@@ -1,7 +1,18 @@
package org.folio.dcb.service;
-import feign.FeignException;
+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.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.UUID;
+
import org.folio.dcb.client.feign.CirculationClient;
+import org.folio.dcb.domain.dto.CirculationRequest;
+import org.folio.dcb.domain.entity.TransactionEntity;
import org.folio.dcb.exception.CirculationRequestException;
import org.folio.dcb.service.impl.CirculationServiceImpl;
import org.junit.jupiter.api.Test;
@@ -10,15 +21,7 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
-import java.util.UUID;
-
-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.Mockito.verify;
-import static org.mockito.Mockito.when;
+import feign.FeignException;
@ExtendWith(MockitoExtension.class)
class CirculationServiceTest {
@@ -44,18 +47,32 @@ void checkInByBarcodeWithServicePointTest(){
verify(circulationClient).checkInByBarcode(any());
}
+ @Test
+ void shouldUpdateRequestApiWithIsDcbRerequestCancellationTrue() {
+ CirculationRequest fetchedRequest = createCirculationRequest();
+ fetchedRequest.setIsDcbReRequestCancellation(null);
+ CirculationRequest requestToBeCancelled = createCirculationRequest();
+ requestToBeCancelled.setIsDcbReRequestCancellation(true);
+ when(circulationRequestService.getCancellationRequestIfOpenOrNull(anyString())).thenReturn(fetchedRequest);
+ circulationService.cancelRequest(createTransactionEntity(), true);
+ verify(circulationClient).updateRequest(requestToBeCancelled.getId(), requestToBeCancelled);
+ }
+
@Test
void cancelRequestTest() {
when(circulationRequestService.getCancellationRequestIfOpenOrNull(anyString())).thenReturn(createCirculationRequest());
- circulationService.cancelRequest(createTransactionEntity());
+ circulationService.cancelRequest(createTransactionEntity(), false);
verify(circulationClient).updateRequest(anyString(), any());
}
@Test
void shouldThrowExceptionWhenRequestIsNotUpdated() {
+ TransactionEntity transactionEntity = createTransactionEntity();
when(circulationRequestService.getCancellationRequestIfOpenOrNull(anyString())).thenReturn(createCirculationRequest());
when(circulationClient.updateRequest(anyString(), any())).thenThrow(FeignException.BadRequest.class);
- assertThrows(CirculationRequestException.class, () -> circulationService.cancelRequest(createTransactionEntity()));
+ assertThrows(CirculationRequestException.class, () -> {
+ circulationService.cancelRequest(transactionEntity, false);
+ });
}
}
diff --git a/src/test/java/org/folio/dcb/service/LendingLibraryServiceTest.java b/src/test/java/org/folio/dcb/service/LendingLibraryServiceTest.java
index 2b55800f..cf2990a7 100644
--- a/src/test/java/org/folio/dcb/service/LendingLibraryServiceTest.java
+++ b/src/test/java/org/folio/dcb/service/LendingLibraryServiceTest.java
@@ -69,12 +69,12 @@ void createTransactionTest() {
when(userService.fetchOrCreateUser(any()))
.thenReturn(user);
when(servicePointService.createServicePointIfNotExists(dcbPickup)).thenReturn(servicePoint);
- when(requestService.createPageItemRequest(any(), any(), anyString())).thenReturn(createCirculationRequest());
+ when(requestService.createRequestBasedOnItemStatus(any(), any(), anyString())).thenReturn(createCirculationRequest());
doNothing().when(baseLibraryService).saveDcbTransaction(any(), any(), any());
var response = lendingLibraryService.createCirculation(DCB_TRANSACTION_ID, dcbTransaction);
verify(userService).fetchOrCreateUser(patron);
- verify(requestService).createPageItemRequest(user, item, dcbTransaction.getPickup().getServicePointId());
+ verify(requestService).createRequestBasedOnItemStatus(user, item, dcbTransaction.getPickup().getServicePointId());
verify(baseLibraryService).saveDcbTransaction(DCB_TRANSACTION_ID, dcbTransaction, CIRCULATION_REQUEST_ID);
Assertions.assertEquals(TransactionStatusResponse.StatusEnum.CREATED, response.getStatus());
assertEquals(servicePoint.getId(), dcbTransaction.getPickup().getServicePointId());
diff --git a/src/test/java/org/folio/dcb/service/RequestServiceTest.java b/src/test/java/org/folio/dcb/service/RequestServiceTest.java
index dc6511a8..df9e1bae 100644
--- a/src/test/java/org/folio/dcb/service/RequestServiceTest.java
+++ b/src/test/java/org/folio/dcb/service/RequestServiceTest.java
@@ -1,11 +1,17 @@
package org.folio.dcb.service;
import org.folio.dcb.client.feign.CirculationClient;
+import org.folio.dcb.domain.dto.CirculationRequest;
+import org.folio.dcb.domain.dto.ItemStatus;
+import org.folio.dcb.exception.StatusException;
import org.folio.dcb.service.impl.HoldingsServiceImpl;
import org.folio.dcb.service.impl.ItemServiceImpl;
import org.folio.dcb.service.impl.RequestServiceImpl;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@@ -15,6 +21,9 @@
import static org.folio.dcb.utils.EntityUtils.createInventoryHolding;
import static org.folio.dcb.utils.EntityUtils.createInventoryItem;
import static org.folio.dcb.utils.EntityUtils.createUser;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentCaptor.forClass;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,13 +41,47 @@ class RequestServiceTest {
private CirculationClient circulationClient;
@Test
- void createPageItemRequestTest() {
+ void createItemRequestWithPageStatusTest() {
when(itemService.fetchItemByIdAndBarcode(any(), any())).thenReturn(createInventoryItem());
when(holdingsService.fetchInventoryHoldingDetailsByHoldingId(any())).thenReturn(createInventoryHolding());
- requestService.createPageItemRequest(createUser(), createDcbItem(), createDcbPickup().getServicePointId());
+ requestService.createRequestBasedOnItemStatus(createUser(), createDcbItem(), createDcbPickup().getServicePointId());
verify(itemService).fetchItemByIdAndBarcode(any(), any());
verify(holdingsService).fetchInventoryHoldingDetailsByHoldingId(any());
- verify(circulationClient).createRequest(any());
+ ArgumentCaptor requestTypeCaptor = forClass(CirculationRequest.class);
+ verify(circulationClient).createRequest(requestTypeCaptor.capture());
+ var capturedRequest = requestTypeCaptor.getValue();
+ assertEquals(CirculationRequest.RequestTypeEnum.PAGE, capturedRequest.getRequestType());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"Awaiting delivery", "Paged", "In transit", "Checked out", "Awaiting pickup"})
+ void createItemRequestWithHoldStatusTest(String holdStatus) {
+ var inventoryItem = createInventoryItem();
+ inventoryItem.setStatus(inventoryItem.getStatus().name(ItemStatus.NameEnum.fromValue(holdStatus)));
+ when(itemService.fetchItemByIdAndBarcode(any(), any())).thenReturn(inventoryItem);
+ when(holdingsService.fetchInventoryHoldingDetailsByHoldingId(any())).thenReturn(createInventoryHolding());
+ requestService.createRequestBasedOnItemStatus(createUser(), createDcbItem(), createDcbPickup().getServicePointId());
+ verify(itemService).fetchItemByIdAndBarcode(any(), any());
+ verify(holdingsService).fetchInventoryHoldingDetailsByHoldingId(any());
+ ArgumentCaptor requestTypeCaptor = forClass(CirculationRequest.class);
+ verify(circulationClient).createRequest(requestTypeCaptor.capture());
+ var capturedRequest = requestTypeCaptor.getValue();
+ assertEquals(CirculationRequest.RequestTypeEnum.HOLD, capturedRequest.getRequestType());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"Aged to lost", "Claimed returned", "Declared lost", "Lost and paid",
+ "Long missing", "Missing", "In process (non-requestable)", "Intellectual item", "On order",
+ "Order closed", "Restricted", "Unavailable", "Unknown", "Withdrawn"})
+ void createItemRequestWithInvalidStatusTest(String invalidStatus) {
+ var inventoryItem = createInventoryItem();
+ inventoryItem.setStatus(inventoryItem.getStatus().name(ItemStatus.NameEnum.fromValue(invalidStatus)));
+ var user = createUser();
+ var item = createDcbItem();
+ var pickupServicePointId = createDcbPickup().getServicePointId();
+ when(itemService.fetchItemByIdAndBarcode(any(), any())).thenReturn(inventoryItem);
+ when(holdingsService.fetchInventoryHoldingDetailsByHoldingId(any())).thenReturn(createInventoryHolding());
+ assertThrows(StatusException.class, () -> requestService.createRequestBasedOnItemStatus(user, item, pickupServicePointId));
}
}
diff --git a/src/test/java/org/folio/dcb/utils/EntityUtils.java b/src/test/java/org/folio/dcb/utils/EntityUtils.java
index 9e469962..b867f75f 100644
--- a/src/test/java/org/folio/dcb/utils/EntityUtils.java
+++ b/src/test/java/org/folio/dcb/utils/EntityUtils.java
@@ -11,6 +11,9 @@
import org.folio.dcb.domain.dto.DcbItem;
import org.folio.dcb.domain.dto.DcbPatron;
import org.folio.dcb.domain.dto.DcbPickup;
+import org.folio.dcb.domain.dto.DcbUpdateTransaction;
+import org.folio.dcb.domain.dto.DcbUpdateItem;
+import org.folio.dcb.domain.dto.ItemStatus;
import org.folio.dcb.domain.dto.TransactionStatusResponse;
import org.folio.dcb.domain.dto.User;
import org.folio.dcb.domain.dto.TransactionStatus;
@@ -61,6 +64,7 @@ public class EntityUtils {
public static String EXISTED_INVENTORY_ITEM_BARCODE = "INVENTORY_ITEM";
public static String PATRON_TYPE_USER_ID = "18c1741d-e678-4c8e-9fe7-cfaeefab5eea";
public static String REQUEST_ID = "398501a2-5c97-4ba6-9ee7-d1cd6433cb98";
+ public static final String DCB_NEW_BARCODE = "398501a2-5c97-4ba6-9ee7-d1cd6433cb91";
public static DcbTransaction createDcbTransactionByRole(DcbTransaction.RoleEnum role) {
return DcbTransaction.builder()
@@ -136,6 +140,18 @@ public static DcbItem createDcbItem() {
.build();
}
+ public static DcbUpdateTransaction createDcbTransactionUpdate() {
+ return DcbUpdateTransaction
+ .builder()
+ .item(DcbUpdateItem
+ .builder()
+ .barcode(DCB_NEW_BARCODE)
+ .lendingLibraryCode("LEN")
+ .materialType("DVD")
+ .build())
+ .build();
+ }
+
public static CirculationRequest createCirculationRequest() {
return CirculationRequest.builder()
.id(CIRCULATION_REQUEST_ID)
@@ -237,6 +253,10 @@ public static InventoryItem createInventoryItem() {
.id(UUID.randomUUID().toString())
.holdingsRecordId(UUID.randomUUID().toString())
.barcode("DCB_ITEM")
+ .status(ItemStatus
+ .builder()
+ .name(ItemStatus.NameEnum.AVAILABLE)
+ .build())
.build();
}
diff --git a/src/test/resources/mappings/circulation-item.json b/src/test/resources/mappings/circulation-item.json
index d8a08687..860e677e 100644
--- a/src/test/resources/mappings/circulation-item.json
+++ b/src/test/resources/mappings/circulation-item.json
@@ -3,11 +3,12 @@
{
"request": {
"method": "POST",
- "url": "/circulation-item/5b95877d-86c0-4cb7-a0cd-7660b348ae5a"
+ "urlPathPattern": "/circulation-item/.*"
},
"response": {
"status": 201,
- "body": "{\"id\": \"5b95877d-86c0-4cb7-a0cd-7660b348ae5a\", \"holdingsRecordId\": \"10cd3a5a-d36f-4c7a-bc4f-e1ae3cf820c9\", \"status\": {\"name\":\"In transit\"}, \"materialTypeId\": \"1a54b431-2e4f-452d-9cae-9cee66c9a892\", \"permanentLoanTypeId\": \"2b94c631-fca9-4892-a730-03ee529ffe27\", \"instanceTitle\": \"ITEM\", \"itemBarcode\": \"DCB_ITEM\", \"pickupLocation\": \"3a40852d-49fd-4df2-a1f9-6e2641a6e91f\"}",
+ "body": "{{jsonPath request.body '$'}}",
+ "transformers": ["response-template"],
"headers": {
"Content-Type": "application/json"
}
@@ -55,7 +56,7 @@
{
"request": {
"method": "GET",
- "url": "/circulation-item?query=id%3D%3D5b95877d-86c0-4cb7-a0cd-7660b348ae5a%20and%20barcode%3D%3DDCB_ITEM"
+ "url": "/circulation-item?query=barcode%3D%3D%22DCB_ITEM%22"
},
"response": {
"status": 200,
@@ -68,7 +69,7 @@
{
"request": {
"method": "GET",
- "url": "/circulation-item?query=id%3D%3D5b95877d-86c0-4cb7-a0cd-7660b348ae5a%20and%20barcode%3D%3DnewItem"
+ "url": "/circulation-item?query=barcode%3D%3D%22newItem%22"
},
"response": {
"status": 200,
@@ -90,6 +91,19 @@
"Content-Type": "application/json"
}
}
+ },
+ {
+ "request": {
+ "method": "GET",
+ "url": "/circulation-item?query=barcode%3D%3D%22398501a2-5c97-4ba6-9ee7-d1cd6433cb91%22"
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n \"totalRecords\": 0,\n \"items\": []\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ }
}
]
}
diff --git a/src/test/resources/mappings/circulation.json b/src/test/resources/mappings/circulation.json
index 00ec8fc2..72e25d33 100644
--- a/src/test/resources/mappings/circulation.json
+++ b/src/test/resources/mappings/circulation.json
@@ -6,8 +6,9 @@
"url": "/circulation/requests"
},
"response": {
- "status": 200,
- "body": "{\"id\": \"571b0a2c-8883-40b5-a449-d41fe6017083\"}",
+ "status": 201,
+ "body": "{{jsonPath request.body '$'}}",
+ "transformers": ["response-template"],
"headers": {
"Content-Type": "application/json"
}
diff --git a/src/test/resources/mappings/inventory.json b/src/test/resources/mappings/inventory.json
index 512502ee..6573a5c6 100644
--- a/src/test/resources/mappings/inventory.json
+++ b/src/test/resources/mappings/inventory.json
@@ -220,6 +220,19 @@
"Content-Type": "application/json"
}
}
+ },
+ {
+ "request": {
+ "method": "GET",
+ "url": "/item-storage/items?query=barcode%3D%3D398501a2-5c97-4ba6-9ee7-d1cd6433cb91"
+ },
+ "response": {
+ "status": 200,
+ "body": "{\n\"items\": [],\n\"totalRecords\": 0,\n \"resultInfo\": {\n \"totalRecords\": 0,\n \"facets\": [],\n \"diagnostics\": []\n }\n}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ }
}
]
}
diff --git a/src/test/resources/mappings/material-types.json b/src/test/resources/mappings/material-types.json
index 366e39c5..51d43f7e 100644
--- a/src/test/resources/mappings/material-types.json
+++ b/src/test/resources/mappings/material-types.json
@@ -12,6 +12,19 @@
"Content-Type": "application/json"
}
}
+ },
+ {
+ "request": {
+ "method": "GET",
+ "url": "/material-types?query=name%3D%3D%22DVD%22"
+ },
+ "response": {
+ "status": 200,
+ "body": "{\"mtypes\": [{\"name\": \"DVD\",\n \"id\": \"1b54b431-2e4f-452d-9cae-9cee66c9a892\",\n \"source\": \"folio\"}]}",
+ "headers": {
+ "Content-Type": "application/json"
+ }
+ }
}
]
}
diff --git a/src/test/resources/mockdata/kafka/cancel_request.json b/src/test/resources/mockdata/kafka/cancel_request.json
index f45127d8..3e9be1ce 100644
--- a/src/test/resources/mockdata/kafka/cancel_request.json
+++ b/src/test/resources/mockdata/kafka/cancel_request.json
@@ -17,7 +17,8 @@
"cancellationReasonId": "50ed35b2-1397-4e83-a76b-642adf91ca2a",
"cancelledByUserId": "7187c6f3-41ec-5731-b551-7f0092abb4c6",
"cancellationAdditionalInformation": "test",
- "cancelledDate": "2023-11-06T12:18:36.938+00:00"
+ "cancelledDate": "2023-11-06T12:18:36.938+00:00",
+ "dcbReRequestCancellation": false
}
}
}
diff --git a/src/test/resources/mockdata/kafka/cancel_request_dcb.json b/src/test/resources/mockdata/kafka/cancel_request_dcb.json
index 4924e559..0950f43d 100644
--- a/src/test/resources/mockdata/kafka/cancel_request_dcb.json
+++ b/src/test/resources/mockdata/kafka/cancel_request_dcb.json
@@ -24,7 +24,8 @@
"requester": {
"lastName": "DcbSystem",
"barcode": "dcbDemo21"
- }
+ },
+ "dcbReRequestCancellation": false
}
}
}
diff --git a/src/test/resources/mockdata/kafka/cancellation_dcb_rerequest_false.json b/src/test/resources/mockdata/kafka/cancellation_dcb_rerequest_false.json
new file mode 100644
index 00000000..a680b137
--- /dev/null
+++ b/src/test/resources/mockdata/kafka/cancellation_dcb_rerequest_false.json
@@ -0,0 +1,24 @@
+{
+ "id": "4a8123fe-a0aa-47ae-9836-71a6cc3cb119",
+ "type": "UPDATED",
+ "tenant": "diku",
+ "timestamp": 1695390940007,
+ "data": {
+ "new": {
+ "id": "299b5ad1-baa6-44fe-a855-0236f852c943",
+ "requestLevel": "Item",
+ "requestType": "Page",
+ "requestDate": "2023-11-06T12:17:52.573+00:00",
+ "requesterId": "2205005b-ca51-4a04-87fd-938eefa8f6de",
+ "instanceId": "9d1b77e4-f02e-4b7f-b296-3f2042ddac54",
+ "holdingsRecordId": "10cd3a5a-d36f-4c7a-bc4f-e1ae3cf820c9",
+ "itemId": "5b95877d-86c0-4cb7-a0cd-7660b348ae5a",
+ "status": "Closed - Cancelled",
+ "cancellationReasonId": "50ed35b2-1397-4e83-a76b-642adf91ca2a",
+ "cancelledByUserId": "7187c6f3-41ec-5731-b551-7f0092abb4c6",
+ "cancellationAdditionalInformation": "test",
+ "cancelledDate": "2023-11-06T12:18:36.938+00:00",
+ "dcbReRequestCancellation": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/mockdata/kafka/cancellation_dcb_rerequest_true.json b/src/test/resources/mockdata/kafka/cancellation_dcb_rerequest_true.json
new file mode 100644
index 00000000..d16838b2
--- /dev/null
+++ b/src/test/resources/mockdata/kafka/cancellation_dcb_rerequest_true.json
@@ -0,0 +1,24 @@
+{
+ "id": "4a8123fe-a0aa-47ae-9836-71a6cc3cb119",
+ "type": "UPDATED",
+ "tenant": "diku",
+ "timestamp": 1695390940007,
+ "data": {
+ "new": {
+ "id": "299b5ad1-baa6-44fe-a855-0236f852c943",
+ "requestLevel": "Item",
+ "requestType": "Page",
+ "requestDate": "2023-11-06T12:17:52.573+00:00",
+ "requesterId": "2205005b-ca51-4a04-87fd-938eefa8f6de",
+ "instanceId": "9d1b77e4-f02e-4b7f-b296-3f2042ddac54",
+ "holdingsRecordId": "10cd3a5a-d36f-4c7a-bc4f-e1ae3cf820c9",
+ "itemId": "5b95877d-86c0-4cb7-a0cd-7660b348ae5a",
+ "status": "Closed - Cancelled",
+ "cancellationReasonId": "50ed35b2-1397-4e83-a76b-642adf91ca2a",
+ "cancelledByUserId": "7187c6f3-41ec-5731-b551-7f0092abb4c6",
+ "cancellationAdditionalInformation": "test",
+ "cancelledDate": "2023-11-06T12:18:36.938+00:00",
+ "dcbReRequestCancellation": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/mockdata/kafka/cancellation_dcb_rerequest_without_dcb_rerequest_property.json b/src/test/resources/mockdata/kafka/cancellation_dcb_rerequest_without_dcb_rerequest_property.json
new file mode 100644
index 00000000..cea96935
--- /dev/null
+++ b/src/test/resources/mockdata/kafka/cancellation_dcb_rerequest_without_dcb_rerequest_property.json
@@ -0,0 +1,23 @@
+{
+ "id": "4a8123fe-a0aa-47ae-9836-71a6cc3cb119",
+ "type": "UPDATED",
+ "tenant": "diku",
+ "timestamp": 1695390940007,
+ "data": {
+ "new": {
+ "id": "299b5ad1-baa6-44fe-a855-0236f852c943",
+ "requestLevel": "Item",
+ "requestType": "Page",
+ "requestDate": "2023-11-06T12:17:52.573+00:00",
+ "requesterId": "2205005b-ca51-4a04-87fd-938eefa8f6de",
+ "instanceId": "9d1b77e4-f02e-4b7f-b296-3f2042ddac54",
+ "holdingsRecordId": "10cd3a5a-d36f-4c7a-bc4f-e1ae3cf820c9",
+ "itemId": "5b95877d-86c0-4cb7-a0cd-7660b348ae5a",
+ "status": "Closed - Cancelled",
+ "cancellationReasonId": "50ed35b2-1397-4e83-a76b-642adf91ca2a",
+ "cancelledByUserId": "7187c6f3-41ec-5731-b551-7f0092abb4c6",
+ "cancellationAdditionalInformation": "test",
+ "cancelledDate": "2023-11-06T12:18:36.938+00:00"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/mockdata/kafka/check_in_dcb.json b/src/test/resources/mockdata/kafka/check_in_dcb.json
index 014cb2fd..28446d6f 100644
--- a/src/test/resources/mockdata/kafka/check_in_dcb.json
+++ b/src/test/resources/mockdata/kafka/check_in_dcb.json
@@ -25,7 +25,8 @@
"requester": {
"lastName": "DcbSystem",
"barcode": "user2"
- }
+ },
+ "dcbReRequestCancellation": false
}
}
}
diff --git a/src/test/resources/mockdata/kafka/check_in_transit.json b/src/test/resources/mockdata/kafka/check_in_transit.json
index 7f48d141..9af72b41 100644
--- a/src/test/resources/mockdata/kafka/check_in_transit.json
+++ b/src/test/resources/mockdata/kafka/check_in_transit.json
@@ -13,7 +13,8 @@
"instanceId": "9d1b77e4-f02e-4b7f-b296-3f2042ddac54",
"holdingsRecordId": "10cd3a5a-d36f-4c7a-bc4f-e1ae3cf820c9",
"itemId": "5d2625ef-81eb-4e61-a8a9-87c94ba3764e",
- "status": "Open - In transit"
+ "status": "Open - In transit",
+ "dcbReRequestCancellation": false
}
}
}
diff --git a/src/test/resources/mockdata/kafka/check_in_transit_dcb.json b/src/test/resources/mockdata/kafka/check_in_transit_dcb.json
index 8ea68aba..48377923 100644
--- a/src/test/resources/mockdata/kafka/check_in_transit_dcb.json
+++ b/src/test/resources/mockdata/kafka/check_in_transit_dcb.json
@@ -24,7 +24,8 @@
"requester": {
"lastName": "DcbSystem",
"barcode": "systemDemoUser1"
- }
+ },
+ "dcbReRequestCancellation": false
}
}
}
diff --git a/src/test/resources/mockdata/kafka/request_sample.json b/src/test/resources/mockdata/kafka/request_sample.json
index 7c9f3f9f..9002dc06 100644
--- a/src/test/resources/mockdata/kafka/request_sample.json
+++ b/src/test/resources/mockdata/kafka/request_sample.json
@@ -97,7 +97,8 @@
},
"shelvingOrder": "K1 .M44 v.72:no.6-7,10-12 1986:July-Aug.,Oct.-Dec.",
"pickupServicePointName": "Circ Desk 1"
- }
+ },
+ "dcbReRequestCancellation": false
}
}
}
diff --git a/src/test/resources/mockdata/kafka/request_undefined.json b/src/test/resources/mockdata/kafka/request_undefined.json
index 1854bdb2..a46099d9 100644
--- a/src/test/resources/mockdata/kafka/request_undefined.json
+++ b/src/test/resources/mockdata/kafka/request_undefined.json
@@ -13,7 +13,8 @@
"instanceId": "9d1b77e4-f02e-4b7f-b296-3f2042ddac54",
"holdingsRecordId": "10cd3a5a-d36f-4c7a-bc4f-e1ae3cf820c9",
"itemId": "5d2625ef-81eb-4e61-a8a9-87c94ba3764e",
- "status": "aaa"
+ "status": "aaa",
+ "dcbReRequestCancellation": false
}
}
}