Skip to content

Commit

Permalink
CIRC-2019 Support renewal by overriding reminders block (#1462)
Browse files Browse the repository at this point in the history
* CIRC-2019 Support renewal by overriding reminders block
  • Loading branch information
nielserik authored Apr 23, 2024
1 parent 8172b17 commit 0542fbe
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/main/java/org/folio/circulation/domain/Loan.java
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ public Loan overrideRenewal(ZonedDateTime dueDate, String basedUponLoanPolicyId,
changeDueDate(dueDate);
incrementRenewalCount();
changeActionComment(actionComment);
resetReminders();

return this;
}
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/folio/circulation/domain/LoanRepresentation.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import static org.folio.circulation.domain.representations.LoanProperties.LOST_ITEM_POLICY;
import static org.folio.circulation.domain.representations.LoanProperties.OVERDUE_FINE_POLICY;
import static org.folio.circulation.domain.representations.LoanProperties.PATRON_GROUP_ID_AT_CHECKOUT;
import static org.folio.circulation.domain.representations.LoanProperties.REMINDERS;

import static org.folio.circulation.support.json.JsonPropertyWriter.write;

import java.lang.invoke.MethodHandles;
Expand Down Expand Up @@ -59,6 +61,15 @@ public JsonObject extendedLoan(Loan loan) {
extendedRepresentation.remove(BORROWER);
}

if (loan.getOverdueFinePolicy().isReminderFeesPolicy()
&& loan.getLastReminderFeeBilledNumber() != null) {
extendedRepresentation.getJsonObject(REMINDERS)
.put("renewalBlocked",
!loan.getOverdueFinePolicy()
.getRemindersPolicy().getAllowRenewalOfItemsWithReminderFees());
}


addPolicy(extendedRepresentation, loan.getLoanPolicy(), LOAN_POLICY);
addPolicy(extendedRepresentation, loan.getOverdueFinePolicy(), OVERDUE_FINE_POLICY);
addPolicy(extendedRepresentation, loan.getLostItemPolicy(), LOST_ITEM_POLICY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.circulation.domain.Loan;
import org.folio.circulation.domain.override.BlockOverrides;
import org.folio.circulation.domain.policy.OverdueFinePolicy;
import org.folio.circulation.resources.context.RenewalContext;
import org.folio.circulation.support.HttpFailure;
Expand All @@ -14,6 +15,7 @@

import static java.util.concurrent.CompletableFuture.completedFuture;
import static org.folio.circulation.support.ValidationErrorFailure.singleValidationError;
import static org.folio.circulation.support.json.JsonPropertyFetcher.getObjectProperty;
import static org.folio.circulation.support.results.Result.failed;
import static org.folio.circulation.support.results.Result.succeeded;

Expand All @@ -27,25 +29,34 @@ public CompletableFuture<Result<RenewalContext>> blockRenewalIfReminderFeesExist

Result<RenewalContext> blockRenewalIfRuledByRemindersFeePolicy(RenewalContext renewalContext) {
log.debug("blockRenewalIfRuledByRemindersFeePolicy:: parameters: renewalContext: {}", renewalContext);

Loan loan = renewalContext.getLoan();
Integer lastFeeBilledCount = loan.getLastReminderFeeBilledNumber();
OverdueFinePolicy overdueFinePolicy = loan.getOverdueFinePolicy();
Boolean allowRenewalWithReminders = overdueFinePolicy.getRemindersPolicy().getAllowRenewalOfItemsWithReminderFees();

if ((lastFeeBilledCount != null && lastFeeBilledCount > 0) && Boolean.FALSE.equals(allowRenewalWithReminders)) {
String reason = "Renewals not allowed for loans with reminders.";
log.info("createBlockedRenewalDueToReminderFeesPolicyError:: {}", reason);
return failed(createBlockedRenewalDueToReminderFeesPolicyError("Renewals not allowed for loans with reminders.", reason));
BlockOverrides overrides = BlockOverrides.from(getObjectProperty(renewalContext.getRenewalRequest(), "overrideBlocks"));
final boolean overrideRenewalBlock = overrides.getRenewalBlockOverride().isRequested();
final boolean overrideRenewalBlockDueDateRequired = overrides.getRenewalDueDateRequiredBlockOverride().isRequested();
if (overrideRenewalBlock || overrideRenewalBlockDueDateRequired) {
return succeeded(renewalContext);
} else if (renewalBlockedDueToReminders(renewalContext)) {
return failed(createBlockedRenewalDueToReminderFeesPolicyError());
} else {
return succeeded(renewalContext);
}
}

private HttpFailure createBlockedRenewalDueToReminderFeesPolicyError(String message, String reason) {
log.debug("createBlockedRenewalDueToReminderFeesPolicyError:: parameters message: {}, reason: {}", message,
reason);
private HttpFailure createBlockedRenewalDueToReminderFeesPolicyError() {
String reasonAndMessage = "Renewals not allowed for loans with reminders.";
log.debug("createBlockedRenewalDueToReminderFeesPolicyError");
return singleValidationError(new ValidationError(reasonAndMessage, "reason", reasonAndMessage));
}

return singleValidationError(new ValidationError(message, "reason", reason));
private static boolean renewalBlockedDueToReminders(RenewalContext renewalContext) {
Loan loan = renewalContext.getLoan();
return loanHasReminders(loan) && policyBlocksRenewalWithReminders(loan);
}
private static Boolean loanHasReminders(Loan loan) {
return loan.getLastReminderFeeBilledNumber() != null && loan.getLastReminderFeeBilledNumber() > 0;
}
private static boolean policyBlocksRenewalWithReminders (Loan loan) {
OverdueFinePolicy overdueFinePolicy = loan.getOverdueFinePolicy();
Boolean allowRenewalWithReminders = overdueFinePolicy.getRemindersPolicy().getAllowRenewalOfItemsWithReminderFees();
return Boolean.FALSE.equals(allowRenewalWithReminders);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ public static ValidationError errorForNotMatchingOverrideCases(LoanPolicy loanPo
"renewal date falls outside of the date ranges in the loan policy, " +
"items cannot be renewed when there is an active recall request, " +
DECLARED_LOST_ITEM_RENEWED_ERROR + ", item is Aged to lost, " +
"renewal would not change the due date";
"renewal would not change the due date, " +
"loan has reminder fees";

return loanPolicyValidationError(loanPolicy, reason);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,9 @@ private Result<Loan> overrideRenewal(Loan loan, ZonedDateTime systemDate,
return processRenewal(newDueDateResult, loan, comment);
}

if (loan.getLastReminderFeeBilledNumber() != null && loan.getLastReminderFeeBilledNumber()>0) {
return processRenewal(newDueDateResult, loan, comment);
}
return failedValidation(errorForNotMatchingOverrideCases(loanPolicy));

} catch (Exception e) {
Expand Down
3 changes: 2 additions & 1 deletion src/test/java/api/loans/OverrideRenewByBarcodeTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,8 @@ void cannotOverrideRenewalWhenLoanDoesNotMatchAnyOfOverrideCases() {
"renewal date falls outside of the date ranges in the loan policy, " +
"items cannot be renewed when there is an active recall request, " +
"item is Declared lost, item is Aged to lost, " +
"renewal would not change the due date"))));
"renewal would not change the due date, " +
"loan has reminder fees"))));
}

@Test
Expand Down
54 changes: 54 additions & 0 deletions src/test/java/api/loans/ReminderFeeTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import api.support.fakes.FakeModNotify;
import api.support.http.IndividualResource;
import api.support.http.ItemResource;
import api.support.http.OkapiHeaders;
import api.support.http.UserResource;
import io.vertx.core.json.JsonObject;
import org.folio.circulation.support.http.client.Response;
Expand All @@ -22,6 +23,7 @@
import static api.support.fixtures.CalendarExamples.CASE_FIRST_DAY_CLOSED_FOLLOWING_OPEN;
import static api.support.fixtures.CalendarExamples.FIRST_DAY;
import static api.support.fixtures.ItemExamples.basedUponSmallAngryPlanet;
import static api.support.utl.BlockOverridesUtils.buildOkapiHeadersWithPermissions;
import static api.support.utl.PatronNoticeTestHelper.*;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static java.time.ZoneOffset.UTC;
Expand Down Expand Up @@ -56,6 +58,8 @@ class ReminderFeeTests extends APITests {

private UUID remindersTwoDaysBetweenNotOnClosedDaysPolicyId;

private static final String OVERRIDE_RENEWAL_BLOCK_PERMISSION = "circulation.override-renewal-block";

@BeforeEach
void beforeEach() {

Expand Down Expand Up @@ -679,6 +683,56 @@ void willAllowRenewalIfHasRemindersButRemindersAllowed() {

}

@Test
void willRenewWithOverride() {
useFallbackPolicies(
loanPolicyId,
requestPolicyId,
noticePolicyId,
remindersTwoDaysBetweenIncludeClosedDaysPolicyId,
lostItemFeePolicyId);

// Check out item, all days open service point
final IndividualResource response = checkOutFixture.checkOutByBarcode(
new CheckOutByBarcodeRequestBuilder()
.forItem(item)
.to(borrower)
.on(loanDate)
.at(servicePointsFixture.cd1()));
final JsonObject loan = response.getJson();
ZonedDateTime dueDate = DateFormatUtil.parseDateTime(loan.getString("dueDate"));

final OkapiHeaders okapiHeaders = buildOkapiHeadersWithPermissions(
OVERRIDE_RENEWAL_BLOCK_PERMISSION);

waitAtMost(1, SECONDS).until(scheduledNoticesClient::getAll, hasSize(1));

ZonedDateTime latestRunTime = dueDate.plusDays(2).truncatedTo(DAYS.toChronoUnit()).plusMinutes(1);
scheduledNoticeProcessingClient.runScheduledDigitalRemindersProcessing(latestRunTime);
// Two days after due date, send reminder
verifyNumberOfScheduledNotices(1);
verifyNumberOfSentNotices(1);
verifyNumberOfPublishedEvents(NOTICE, 1);
verifyNumberOfPublishedEvents(NOTICE_ERROR, 0);
waitAtMost(1, SECONDS).until(accountsClient::getAll, hasSize(1));

// Attempt renewal with override when Reminders Policy allowRenewalOfItemsWithReminderFees
// is set to 'False' and loan has reminders already sent out
JsonObject renewedLoan = loansFixture.renewLoan(
new RenewByBarcodeRequestBuilder()
.forItem(item)
.forUser(borrower)
.withServicePointId(servicePointsFixture.cd1().getId().toString())
.withOverrideBlocks(
new RenewBlockOverrides()
.withRenewalBlock(new JsonObject())
.withComment("TEST_COMMENT").create()),
okapiHeaders).getJson();

assertThat("renewal count should be 1 after renewal",
renewedLoan.getInteger("renewalCount"), is(1));
}

@Test
void willResetRemindersRescheduleNoticeOnRenewal() {
useFallbackPolicies(
Expand Down
6 changes: 4 additions & 2 deletions src/test/java/api/requests/RequestsAPILoanRenewalTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,8 @@ void forbidRenewalOverrideWhenRecallIsForDifferentItemOfSameInstance() {
"renewal date falls outside of the date ranges in the loan policy, " +
"items cannot be renewed when there is an active recall request, " +
"item is Declared lost, item is Aged to lost, " +
"renewal would not change the due date"))));
"renewal would not change the due date, " +
"loan has reminder fees"))));
}

@Test
Expand Down Expand Up @@ -623,7 +624,8 @@ void forbidRenewalOverrideWhenTitleLevelRecallRequestExistsForDifferentItemOfSam
"renewal date falls outside of the date ranges in the loan policy, " +
"items cannot be renewed when there is an active recall request, " +
"item is Declared lost, item is Aged to lost, " +
"renewal would not change the due date"))));
"renewal would not change the due date, " +
"loan has reminder fees"))));
}

@Test
Expand Down

0 comments on commit 0542fbe

Please sign in to comment.