Skip to content

Commit

Permalink
MODDCB-174 Handle PUT /transaction/{dcbTransactionId}/renew API to al…
Browse files Browse the repository at this point in the history
…low the DCB hub to perform the renew operation (#146)

MODDCB-174 Handle PUT /transaction/{dcbTransactionId}/renew API to allow the DCB hub to perform the renew operation
  • Loading branch information
AntonAntonich authored Feb 19, 2025
1 parent c51c984 commit 455ca84
Show file tree
Hide file tree
Showing 12 changed files with 878 additions and 287 deletions.
21 changes: 20 additions & 1 deletion descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,19 @@
"calendar.endpoint.calendars.calendarId.put"
]
},
{
"methods": [
"PUT"
],
"pathPattern": "/transactions/{dcbTransactionId}/renew",
"permissionsRequired": [
"dcb.transactions.renew.put"
],
"modulePermissions": [
"circulation.renew-by-id.post",
"circulation-storage.loan-policies.item.get"
]
},
{
"methods": [
"GET"
Expand Down Expand Up @@ -267,14 +280,20 @@
"dcb.transactions.status.get",
"dcb.transactions.collection.get",
"dcb.ecs-request.transactions.post",
"dcb.transactions.item.put"
"dcb.transactions.item.put",
"dcb.transactions.renew.put"
]
},
{
"permissionName": "dcb.transactions.post",
"displayName": "creates new transaction",
"description": "creates new transaction"
},
{
"permissionName": "dcb.transactions.renew.put",
"displayName": "renew loan by transaction id",
"description": "renew loan by transaction id"
},
{
"permissionName": "dcb.transactions.status.get",
"displayName": "get transaction details",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import org.folio.dcb.domain.dto.CheckOutRequest;
import org.folio.dcb.domain.dto.CirculationRequest;
import org.folio.dcb.domain.dto.LoanCollection;
import org.folio.dcb.domain.dto.RenewByIdRequest;
import org.folio.dcb.domain.dto.RenewByIdResponse;
import org.folio.spring.config.FeignClientConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -30,4 +32,7 @@ CirculationRequest updateRequest(@PathVariable("requestId") String requestId,

@GetMapping("/loans")
LoanCollection fetchLoanByQuery(@RequestParam("query") String query);

@PostMapping("/renew-by-id")
RenewByIdResponse renewById(@RequestBody RenewByIdRequest renewByIdRequest);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.folio.dcb.client.feign;

import org.folio.dcb.domain.dto.LoanPolicy;
import org.folio.dcb.domain.dto.LoanPolicyCollection;
import org.folio.spring.config.FeignClientConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
Expand All @@ -10,4 +11,6 @@
public interface CirculationLoanPolicyStorageClient {
@GetMapping("/loan-policies")
LoanPolicyCollection fetchLoanPolicyByQuery(@RequestParam("query") String query);
@GetMapping("/loan-policies/{id}")
LoanPolicy fetchLoanPolicyById(@RequestParam("id") String id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ public class TransactionApiController implements TransactionsApi {

private final TransactionsService transactionsService;
private final TransactionAuditService transactionAuditService;

@Override
public ResponseEntity<TransactionStatusResponse> renewItemLoanByTransactionId(String dcbTransactionId) {
return ResponseEntity.status(HttpStatus.OK)
.body(transactionsService.renewLoanByTransactionId(dcbTransactionId));
}

@Override
public ResponseEntity<TransactionStatusResponse> getTransactionStatusById(String dcbTransactionId) {
log.info("getTransactionStatus:: by id {} ", dcbTransactionId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ public interface TransactionsService {
TransactionStatusResponse getTransactionStatusById(String dcbTransactionId);
TransactionStatusResponseCollection getTransactionStatusList(OffsetDateTime fromDate, OffsetDateTime toDate, Integer pageNumber, Integer pageSize);
void updateTransactionDetails(String dcbTransactionId, DcbUpdateTransaction dcbUpdateTransaction);

TransactionStatusResponse renewLoanByTransactionId(String dcbTransactionId);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package org.folio.dcb.service.impl;

import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.LENDER;
import static org.folio.dcb.domain.dto.TransactionStatus.StatusEnum.ITEM_CHECKED_OUT;
import static org.folio.dcb.utils.TransactionDetailsUtil.rolesNotEqual;
import static org.folio.dcb.utils.TransactionDetailsUtil.statusesNotEqual;

import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.folio.dcb.client.feign.CirculationClient;
Expand All @@ -11,6 +16,8 @@
import org.folio.dcb.domain.dto.LoanCollection;
import org.folio.dcb.domain.dto.LoanPolicy;
import org.folio.dcb.domain.dto.LoanPolicyCollection;
import org.folio.dcb.domain.dto.RenewByIdRequest;
import org.folio.dcb.domain.dto.RenewByIdResponse;
import org.folio.dcb.domain.dto.RenewalInfo;
import org.folio.dcb.domain.dto.TransactionStatus;
import org.folio.dcb.domain.dto.TransactionStatusResponse;
Expand All @@ -34,6 +41,7 @@
import org.springframework.stereotype.Service;

import java.time.OffsetDateTime;
import java.util.Objects;
import java.util.Optional;

@Service
Expand Down Expand Up @@ -143,7 +151,7 @@ private Optional<LoanRenewalDetails> getLoanRenewalDetails(TransactionEntity tra
}

private static boolean isTxnItemCheckoutAndRoleIsBorrowerOrBorrowingPickup(TransactionEntity transactionEntity) {
return transactionEntity.getStatus() == TransactionStatus.StatusEnum.ITEM_CHECKED_OUT
return transactionEntity.getStatus() == ITEM_CHECKED_OUT
&& (transactionEntity.getRole() == DcbTransaction.RoleEnum.BORROWING_PICKUP
|| transactionEntity.getRole() == DcbTransaction.RoleEnum.BORROWER);
}
Expand Down Expand Up @@ -177,14 +185,73 @@ public TransactionStatusResponseCollection getTransactionStatusList(OffsetDateTi
.build();
}

@Override
public TransactionStatusResponse renewLoanByTransactionId(String dcbTransactionId) {
log.info("renewLoanByTransactionId:: getting transaction by id {} ", dcbTransactionId);
var transaction = getTransactionEntityOrThrow(dcbTransactionId);
validateTransactionForRenewal(transaction);
var itemId = transaction.getItemId();
var patronId = transaction.getPatronId();
var renewalResponse = circulationClient.renewById(buildRenewRequest(itemId, patronId));
log.debug("renewLoanByTransactionId:: Renew response {}", renewalResponse);
validateRenewalResponse(dcbTransactionId, renewalResponse, itemId);
var loanPolicy = circulationLoanPolicyStorageClient.fetchLoanPolicyById(
renewalResponse.getLoanPolicyId());
log.info("renewLoanByTransactionId:: Loan policy response {}", loanPolicy);
validateLoanPolicy(renewalResponse.getLoanPolicyId(), loanPolicy);
var loanRenewalDetails = Optional.of(new LoanRenewalDetails(renewalResponse.getRenewalCount(),
loanPolicy.getRenewalsPolicy().getNumberAllowed(), loanPolicy.getRenewable()));
return generateTransactionStatusResponseFromTransactionEntity(transaction, loanRenewalDetails);
}

private void validateLoanPolicy(String loanPolicyId, LoanPolicy loanPolicy) {
if (Objects.isNull(loanPolicy)) {
log.debug("validateLoanPolicy:: Loan policy is null");
throw new NotFoundException(String.format("Loan policy not found for loan id: %s", loanPolicyId));
}
}

private static void validateRenewalResponse(String dcbTransactionId, RenewByIdResponse response,
String itemId) {
if(Objects.isNull(response)) {
log.debug("validateRenewalResponse:: renewal response is null");
throw new NotFoundException(String.format("Renew failed. Transaction id:%s, Item id: %s",
dcbTransactionId, itemId));
}
}

private static RenewByIdRequest buildRenewRequest(String itemId, String patronId) {
return RenewByIdRequest.builder()
.itemId(itemId)
.userId(patronId)
.build();
}

private static void validateTransactionForRenewal(TransactionEntity transaction) {
TransactionStatus.StatusEnum status = transaction.getStatus();
DcbTransaction.RoleEnum role = transaction.getRole();
if (statusesNotEqual(ITEM_CHECKED_OUT, status)) {
log.debug("validateTransactionForRenewal:: Transaction status is {}", status);
throw new StatusException(String.format(
"Loan couldn't be renewed with transaction status %s, it could be renewed only with " +
"ITEM_CHECKED_OUT status", status));
}
if (rolesNotEqual(LENDER, role)) {
log.debug("validateTransactionForRenewal:: Transaction role is {}", role);
throw new IllegalArgumentException(
String.format("Loan couldn't be renewed with role %s, it could be renewed only with role" +
" LENDER", role));
}
}

@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())) {
if (LENDER.equals(transactionEntity.getRole())) {
throw new IllegalArgumentException("Item details cannot be updated for lender role");
}
baseLibraryService.updateTransactionDetails(transactionEntity, dcbUpdateTransaction.getItem());
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/org/folio/dcb/utils/TransactionDetailsUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.folio.dcb.utils;

import java.util.Objects;

import org.folio.dcb.domain.dto.DcbTransaction;
import org.folio.dcb.domain.dto.TransactionStatus;

import lombok.experimental.UtilityClass;

@UtilityClass
public class TransactionDetailsUtil {

public static boolean statusesNotEqual(TransactionStatus.StatusEnum statusOne, TransactionStatus.StatusEnum statusTwo) {
return !Objects.equals(statusOne, statusTwo);
}

public static boolean rolesNotEqual(DcbTransaction.RoleEnum roleOne, DcbTransaction.RoleEnum roleTwo) {
return !Objects.equals(roleOne, roleTwo);
}
}
21 changes: 21 additions & 0 deletions src/main/resources/swagger.api/dcb_transaction.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,23 @@ paths:
$ref: '#/components/responses/Conflict'
'500':
$ref: '#/components/responses/InternalServerError'
/transactions/{dcbTransactionId}/renew:
parameters:
- $ref: '#/components/parameters/dcbTransactionId'
put:
description: Increment the renew loan count and returned transaction status with renewal details
operationId: renewItemLoanByTransactionId
tags:
- circulation
responses:
'200':
$ref: '#/components/responses/TransactionStatusResponse'
'404':
$ref: '#/components/responses/NotFound'
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'
components:
requestBodies:
TransactionStatusBody:
Expand Down Expand Up @@ -237,6 +254,10 @@ components:
maximum: 2147483647
required: false
schemas:
RenewByIdRequest:
$ref: 'schemas/RenewByIdRequest.yaml#/RenewByIdRequest'
RenewByIdResponse:
$ref: 'schemas/RenewByIdResponse.yaml#/RenewByIdResponse'
InventoryItem:
$ref: 'schemas/InventoryItem.yaml#/InventoryItem'
InventoryHolding:
Expand Down
16 changes: 16 additions & 0 deletions src/main/resources/swagger.api/schemas/RenewByIdRequest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
RenewByIdRequest:
type: object
description: Request to renew an existing loan, found by the IDs of item and user
properties:
itemId:
description: ID of the item to be renewed
type: string
$ref: "./uuid.yaml"
userId:
description: ID of the user (representing the patron) the item has been loaned to
type: string
$ref: "./uuid.yaml"
required:
- itemId
- userId
additionalProperties: false
Loading

0 comments on commit 455ca84

Please sign in to comment.