diff --git a/src/main/java/org/folio/circulation/domain/Loan.java b/src/main/java/org/folio/circulation/domain/Loan.java index dc11924531..fc2c6f412a 100644 --- a/src/main/java/org/folio/circulation/domain/Loan.java +++ b/src/main/java/org/folio/circulation/domain/Loan.java @@ -21,6 +21,7 @@ import static org.folio.circulation.domain.representations.LoanProperties.ACTION_COMMENT; import static org.folio.circulation.domain.representations.LoanProperties.AGED_TO_LOST_DATE; import static org.folio.circulation.domain.representations.LoanProperties.AGED_TO_LOST_DELAYED_BILLING; +import static org.folio.circulation.domain.representations.LoanProperties.AT_LOCATION_USE_EXPIRY_DATE; import static org.folio.circulation.domain.representations.LoanProperties.AT_LOCATION_USE_STATUS; import static org.folio.circulation.domain.representations.LoanProperties.AT_LOCATION_USE_STATUS_DATE; import static org.folio.circulation.domain.representations.LoanProperties.BILL_DATE; @@ -46,6 +47,7 @@ import static org.folio.circulation.domain.representations.LoanProperties.STATUS; import static org.folio.circulation.domain.representations.LoanProperties.SYSTEM_RETURN_DATE; import static org.folio.circulation.domain.representations.LoanProperties.UPDATED_BY_USER_ID; +import static org.folio.circulation.domain.representations.LoanProperties.USAGE_STATUS_HELD; import static org.folio.circulation.domain.representations.LoanProperties.USER_ID; import static org.folio.circulation.support.ValidationErrorFailure.failedValidation; import static org.folio.circulation.support.json.JsonPropertyFetcher.getBooleanProperty; @@ -79,6 +81,7 @@ import org.apache.logging.log4j.Logger; import org.folio.circulation.domain.policy.LoanPolicy; import org.folio.circulation.domain.policy.OverdueFinePolicy; +import org.folio.circulation.domain.policy.Period; import org.folio.circulation.domain.policy.RemindersPolicy; import org.folio.circulation.domain.policy.lostitem.LostItemPolicy; import org.folio.circulation.domain.representations.LoanProperties; @@ -211,9 +214,19 @@ public String getAction() { } public Loan changeStatusOfUsageAtLocation(String usageStatus) { - log.debug("changeStatusOfUsageAtLocation:: parameters usageStatus: {}", usageStatus); + log.info("changeStatusOfUsageAtLocation:: parameters usageStatus: {}", usageStatus); writeByPath(representation, usageStatus, FOR_USE_AT_LOCATION, AT_LOCATION_USE_STATUS); writeByPath(representation, ClockUtil.getZonedDateTime().toString(), FOR_USE_AT_LOCATION, AT_LOCATION_USE_STATUS_DATE); + if (usageStatus.equals(USAGE_STATUS_HELD)) { + Period expiry = getLoanPolicy().getHoldShelfExpiryPeriodForUseAtLocation(); + if (expiry == null) { + log.warn("No hold shelf expiry period for use at location defined in loan policy {}", getLoanPolicy().getName()); + } else { + writeByPath(representation, expiry.plusDate(ClockUtil.getZonedDateTime()), FOR_USE_AT_LOCATION, AT_LOCATION_USE_EXPIRY_DATE); + return this; + } + } + remove(representation.getJsonObject(FOR_USE_AT_LOCATION), AT_LOCATION_USE_EXPIRY_DATE); return this; } diff --git a/src/main/java/org/folio/circulation/domain/policy/LoanPolicy.java b/src/main/java/org/folio/circulation/domain/policy/LoanPolicy.java index 26fd8f96cb..9b82a920c2 100644 --- a/src/main/java/org/folio/circulation/domain/policy/LoanPolicy.java +++ b/src/main/java/org/folio/circulation/domain/policy/LoanPolicy.java @@ -216,7 +216,7 @@ private boolean isAlternatePeriod(RequestQueue requestQueue, String itemId) { (!request.hasItem() || itemId.equals(request.getItemId()))); } - private JsonObject getLoansPolicy() { + public JsonObject getLoansPolicy() { return representation.getJsonObject(LOANS_POLICY_KEY); } @@ -350,6 +350,16 @@ public boolean isForUseAtLocation() { return getBooleanProperty(getLoansPolicy(), FOR_USE_AT_LOCATION); } + public Period getHoldShelfExpiryPeriodForUseAtLocation() { + if (isForUseAtLocation()) { + JsonObject holdShelfExpiryPeriod = getObjectProperty(getLoansPolicy(), "holdShelfExpiryPeriodForUseAtLocation"); + if (holdShelfExpiryPeriod != null) { + return Period.from(holdShelfExpiryPeriod); + } + } + return null; + } + public DueDateManagement getDueDateManagement() { JsonObject loansPolicyObj = getLoansPolicy(); if (Objects.isNull(loansPolicyObj)) { diff --git a/src/main/java/org/folio/circulation/domain/representations/LoanProperties.java b/src/main/java/org/folio/circulation/domain/representations/LoanProperties.java index 784ed9ffcb..ecf62f2fc5 100644 --- a/src/main/java/org/folio/circulation/domain/representations/LoanProperties.java +++ b/src/main/java/org/folio/circulation/domain/representations/LoanProperties.java @@ -37,6 +37,7 @@ private LoanProperties() { } public static final String FOR_USE_AT_LOCATION = "forUseAtLocation"; public static final String AT_LOCATION_USE_STATUS = "status"; public static final String AT_LOCATION_USE_STATUS_DATE = "statusDate"; + public static final String AT_LOCATION_USE_EXPIRY_DATE = "holdShelfExpirationDate"; public static final String USAGE_STATUS_IN_USE = "In use"; public static final String USAGE_STATUS_HELD = "Held"; public static final String USAGE_STATUS_RETURNED = "Returned"; diff --git a/src/main/java/org/folio/circulation/resources/foruseatlocation/HoldByBarcodeResource.java b/src/main/java/org/folio/circulation/resources/foruseatlocation/HoldByBarcodeResource.java index 9d2bb9a4ec..8d8b5fe3e6 100644 --- a/src/main/java/org/folio/circulation/resources/foruseatlocation/HoldByBarcodeResource.java +++ b/src/main/java/org/folio/circulation/resources/foruseatlocation/HoldByBarcodeResource.java @@ -10,6 +10,7 @@ import org.folio.circulation.domain.LoanAction; import org.folio.circulation.domain.representations.logs.LogEventType; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; +import org.folio.circulation.infrastructure.storage.loans.LoanPolicyRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; import org.folio.circulation.infrastructure.storage.users.UserRepository; import org.folio.circulation.resources.Resource; @@ -58,6 +59,7 @@ private void markHeld(RoutingContext routingContext) { final var itemRepository = new ItemRepository(clients); final var userRepository = new UserRepository(clients); final var loanRepository = new LoanRepository(clients, itemRepository, userRepository); + final var loanPolicyRepository = new LoanPolicyRepository(clients); final EventPublisher eventPublisher = new EventPublisher(webContext,clients); JsonObject requestBodyAsJson = routingContext.body().asJsonObject(); @@ -67,6 +69,7 @@ private void markHeld(RoutingContext routingContext) { .after(request -> findLoan(request, loanRepository, itemRepository, userRepository, errorHandler)) .thenApply(loan -> failWhenOpenLoanNotFoundForItem(loan, requestResult.value())) .thenApply(loan -> failWhenOpenLoanIsNotForUseAtLocation(loan, requestResult.value())) + .thenCompose(loanPolicyRepository::findPolicyForLoan) .thenApply(loanResult -> loanResult.map(loan -> loan.changeStatusOfUsageAtLocation(USAGE_STATUS_HELD))) .thenApply(loanResult -> loanResult.map(loan -> loan.withAction(LoanAction.HELD_FOR_USE_AT_LOCATION))) .thenCompose(loanResult -> loanResult.after( diff --git a/src/test/java/api/loans/LoansForUseAtLocationTests.java b/src/test/java/api/loans/LoansForUseAtLocationTests.java index 688828615c..8b32640d37 100644 --- a/src/test/java/api/loans/LoansForUseAtLocationTests.java +++ b/src/test/java/api/loans/LoansForUseAtLocationTests.java @@ -18,6 +18,7 @@ import static api.support.fixtures.ItemExamples.basedUponSmallAngryPlanet; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.nullValue; public class LoansForUseAtLocationTests extends APITests { private ItemResource item; @@ -77,7 +78,8 @@ void willMarkItemHeldByBarcode() { .withName("Reading room loans") .withDescription("Policy for items to be used at location") .rolling(Period.days(30)) - .withForUseAtLocation(true); + .withForUseAtLocation(true) + .withHoldShelfExpiryPeriodForUseAtLocation(Period.from(5, "DAYS")); use(forUseAtLocationPolicyBuilder); @@ -91,10 +93,13 @@ void willMarkItemHeldByBarcode() { new HoldByBarcodeRequestBuilder(item.getBarcode())); JsonObject forUseAtLocation = holdResponse.getJson().getJsonObject("forUseAtLocation"); + assertThat("loan.forUseAtLocation", forUseAtLocation, notNullValue()); assertThat("loan.forUseAtLocation.status", forUseAtLocation.getString("status"), Is.is("Held")); + assertThat("loan.forUseAtLocation.holdShelfExpirationDate", + forUseAtLocation.getString("holdShelfExpirationDate"), notNullValue()); } @Test @@ -103,7 +108,8 @@ void holdWillFailWithDifferentItem() { .withName("Reading room loans") .withDescription("Policy for items to be used at location") .rolling(Period.days(30)) - .withForUseAtLocation(true); + .withForUseAtLocation(true) + .withHoldShelfExpiryPeriodForUseAtLocation(Period.from(5, "DAYS")); use(forUseAtLocationPolicyBuilder); @@ -142,7 +148,8 @@ void holdWillFailWithIncompleteRequest() { .withName("Reading room loans") .withDescription("Policy for items to be used at location") .rolling(Period.days(30)) - .withForUseAtLocation(true); + .withForUseAtLocation(true) + .withHoldShelfExpiryPeriodForUseAtLocation(Period.from(5, "DAYS")); use(forUseAtLocationPolicyBuilder); @@ -163,7 +170,8 @@ void willMarkItemInUseByBarcode() { .withName("Reading room loans") .withDescription("Policy for items to be used at location") .rolling(Period.days(30)) - .withForUseAtLocation(true); + .withForUseAtLocation(true) + .withHoldShelfExpiryPeriodForUseAtLocation(Period.from(5, "DAYS")); use(forUseAtLocationPolicyBuilder); @@ -213,7 +221,8 @@ void pickupWillFailWithIncompleteRequestObject() { .withName("Reading room loans") .withDescription("Policy for items to be used at location") .rolling(Period.days(30)) - .withForUseAtLocation(true); + .withForUseAtLocation(true) + .withHoldShelfExpiryPeriodForUseAtLocation(Period.from(5, "DAYS")); use(forUseAtLocationPolicyBuilder); @@ -255,7 +264,8 @@ void willSetAtLocationUsageStatusToReturnedOnCheckIn() { .withName("Reading room loans") .withDescription("Policy for items to be used at location") .rolling(Period.days(30)) - .withForUseAtLocation(true); + .withForUseAtLocation(true) + .withHoldShelfExpiryPeriodForUseAtLocation(Period.from(5, "DAYS")); use(forUseAtLocationPolicyBuilder); @@ -275,6 +285,8 @@ void willSetAtLocationUsageStatusToReturnedOnCheckIn() { forUseAtLocation, notNullValue()); assertThat("loan.forUseAtLocation.status", forUseAtLocation.getString("status"), Is.is("Returned")); + assertThat("loan.forUseAtLocation.holdShelfExpirationDate", + forUseAtLocation.getString("holdShelfExpirationDate"), nullValue()); } } diff --git a/src/test/java/api/support/builders/LoanPolicyBuilder.java b/src/test/java/api/support/builders/LoanPolicyBuilder.java index 0eb352ad87..3f0451887a 100644 --- a/src/test/java/api/support/builders/LoanPolicyBuilder.java +++ b/src/test/java/api/support/builders/LoanPolicyBuilder.java @@ -41,6 +41,7 @@ public class LoanPolicyBuilder extends JsonBuilder implements Builder { private final Integer itemLimit; private final Period gracePeriod; private final boolean forUseAtLocation; + private final Period holdShelfExpiryPeriodForUseAtLocation; public LoanPolicyBuilder() { this(UUID.randomUUID(), @@ -67,7 +68,8 @@ public LoanPolicyBuilder() { null, null, null, - false + false, + null ); } @@ -91,6 +93,7 @@ public JsonObject create() { put(loansPolicy, "itemLimit", itemLimit); putIfNotNull(loansPolicy, "gracePeriod", gracePeriod, Period::asJson); put(loansPolicy, "forUseAtLocation", forUseAtLocation); + putIfNotNull(loansPolicy, "holdShelfExpiryPeriodForUseAtLocation", holdShelfExpiryPeriodForUseAtLocation, Period::asJson); //TODO: Replace with sub-builders if(Objects.equals(loansProfile, "Rolling")) {