Skip to content

Commit

Permalink
CIRC-2141 Allow specifying item location when creating title-level re… (
Browse files Browse the repository at this point in the history
#1491)

* CIRC-2141 Allow specifying item location when creating title-level requests
  • Loading branch information
JanisSaldabols authored Oct 3, 2024
1 parent 69d9888 commit cf733df
Show file tree
Hide file tree
Showing 14 changed files with 145 additions and 15 deletions.
6 changes: 5 additions & 1 deletion ramls/request.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"requestLevel": {
"description": "Level of the request - Item or Title",
"type": "string",
"enum": ["Item"]
"enum": ["Item", "Title"]
},
"requestDate": {
"description": "Date the request was made",
Expand Down Expand Up @@ -433,6 +433,10 @@
"description": "Request fields used for search",
"type": "object",
"$ref": "request-search-index.json"
},
"itemLocationCode": {
"description": "Allow specifying item location when creating title-level requests",
"type": "string"
}
},
"additionalProperties": false,
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/folio/circulation/domain/Campus.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ public static Campus unknown() {
}

public static Campus unknown(String id) {
return new Campus(id, null);
return new Campus(id, null, null);
}

String id;
String name;
String code;
}
3 changes: 2 additions & 1 deletion src/main/java/org/folio/circulation/domain/Institution.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ public static Institution unknown() {
}

public static Institution unknown(String id) {
return new Institution(id, null);
return new Institution(id, null, null);
}

String id;
String name;
String code;
}
8 changes: 8 additions & 0 deletions src/main/java/org/folio/circulation/domain/Item.java
Original file line number Diff line number Diff line change
Expand Up @@ -415,4 +415,12 @@ public String getLendingLibraryCode() {
public String getDcbItemTitle() {
return getProperty(itemRepresentation, "instanceTitle");
}

public boolean isAtLocation(String locationCode) {
return locationCode != null && getLocation() != null && (
locationCode.equals(getLocation().getCode()) ||
locationCode.equals(getLocation().getLibrary().getCode()) ||
locationCode.equals(getLocation().getCampus().getCode()) ||
locationCode.equals(getLocation().getInstitution().getCode()));
}
}
3 changes: 2 additions & 1 deletion src/main/java/org/folio/circulation/domain/Library.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ public static Library unknown() {
}

public static Library unknown(String id) {
return new Library(id, null);
return new Library(id, null, null);
}

String id;
String name;
String code;
}
5 changes: 5 additions & 0 deletions src/main/java/org/folio/circulation/domain/Request.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import static org.folio.circulation.domain.representations.RequestProperties.CANCELLATION_REASON_ID;
import static org.folio.circulation.domain.representations.RequestProperties.CANCELLATION_REASON_NAME;
import static org.folio.circulation.domain.representations.RequestProperties.CANCELLATION_REASON_PUBLIC_DESCRIPTION;
import static org.folio.circulation.domain.representations.RequestProperties.ITEM_LOCATION_CODE;
import static org.folio.circulation.domain.representations.RequestProperties.HOLDINGS_RECORD_ID;
import static org.folio.circulation.domain.representations.RequestProperties.HOLD_SHELF_EXPIRATION_DATE;
import static org.folio.circulation.domain.representations.RequestProperties.INSTANCE_ID;
Expand Down Expand Up @@ -388,6 +389,10 @@ public String getPatronComments() {
return getProperty(requestRepresentation, "patronComments");
}

public String geItemLocationCode() {
return getProperty(requestRepresentation, ITEM_LOCATION_CODE);
}

public Request truncateRequestExpirationDateToTheEndOfTheDay(ZoneId zone) {
ZonedDateTime requestExpirationDate = getRequestExpirationDate();
if (requestExpirationDate != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ private RequestProperties() { }
public static final String REQUESTER_ID = "requesterId";
public static final String FULFILLMENT_PREFERENCE = "fulfillmentPreference";
public static final String PICKUP_SERVICE_POINT_ID = "pickupServicePointId";
public static final String ITEM_LOCATION_CODE = "itemLocationCode";
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ public CompletableFuture<Result<MultipleRecords<Location>>> fetchLocations(
new LocationMapper()::toDomain);

return fetcher.findByIds(locationIds)
.thenCompose(this::loadLibrariesForLocations);
.thenCompose(this::loadLibrariesForLocations)
.thenCompose(this::loadCampusesForLocations)
.thenCompose(this::loadInstitutionsForLocations);
}

private CompletableFuture<Result<Location>> loadLibrary(Location location) {
Expand Down Expand Up @@ -208,6 +210,19 @@ public CompletableFuture<Result<Map<String, Library>>> getLibraries(
.thenApply(mapResult(records -> records.toMap(Library::getId)));
}

private CompletableFuture<Result<MultipleRecords<Location>>> loadCampusesForLocations(
Result<MultipleRecords<Location>> multipleRecordsResult) {

log.debug("loadCampusesForLocations:: parameters multipleRecordsResult: {}",
() -> resultAsString(multipleRecordsResult));

return multipleRecordsResult.combineAfter(
locations -> getCampuses(locations.getRecords()), (locations, campuses) ->
locations.mapRecords(location -> location.withCampus(
campuses.getOrDefault(location.getCampusId(), Campus.unknown(location.getCampusId())))));

}

public CompletableFuture<Result<Map<String, Campus>>> getCampuses(
Collection<Location> locations) {

Expand All @@ -223,6 +238,19 @@ public CompletableFuture<Result<Map<String, Campus>>> getCampuses(
.thenApply(mapResult(records -> records.toMap(Campus::getId)));
}

private CompletableFuture<Result<MultipleRecords<Location>>> loadInstitutionsForLocations(
Result<MultipleRecords<Location>> multipleRecordsResult) {

log.debug("loadInstitutionsForLocations:: parameters multipleRecordsResult: {}",
() -> resultAsString(multipleRecordsResult));

return multipleRecordsResult.combineAfter(
locations -> getInstitutions(locations.getRecords()), (locations, institutions) ->
locations.mapRecords(location -> location.withInstitution(
institutions.getOrDefault(location.getInstitutionId(), Institution.unknown(location.getInstitutionId())))));

}

public CompletableFuture<Result<Map<String, Institution>>> getInstitutions(
Collection<Location> locations) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.folio.circulation.domain.RequestType.PAGE;
import static org.folio.circulation.domain.representations.RequestProperties.ITEM_LOCATION_CODE;
import static org.folio.circulation.domain.representations.RequestProperties.INSTANCE_ID;
import static org.folio.circulation.support.ValidationErrorFailure.failedValidation;
import static org.folio.circulation.support.results.Result.of;
Expand Down Expand Up @@ -69,7 +70,21 @@ private Result<List<Item>> refusePageRequestWhenNoAvailablePageableItemsExist(Re
return failedValidation(message, INSTANCE_ID, request.getInstanceId());
}

return of(() -> availablePageableItems);
if (request.geItemLocationCode() == null) {
return of(() -> availablePageableItems);
} else {
List<Item> finalAvailablePageableItems = availablePageableItems.stream()
.filter(item -> item.isAtLocation(request.geItemLocationCode()))
.toList();
if (finalAvailablePageableItems.isEmpty()) {
String message = "Cannot create page TLR for this instance ID - no pageable available " +
"items found in requested location";
log.info("{}. Instance ID: {}, Requested location code {}",
message, request.getInstanceId(), request.geItemLocationCode());
return failedValidation(message, ITEM_LOCATION_CODE, request.geItemLocationCode());
}
return of(() -> finalAvailablePageableItems);
}
}

private static Item pickClosestItem(Collection<Location> requestedLocations, List<Item> availableItems) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
public class CampusMapper {
public Campus toDomain(JsonObject representation) {
return new Campus(getProperty(representation, "id"),
getProperty(representation, "name"));
getProperty(representation, "name"),
getProperty(representation, "code"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
public class InstitutionMapper {
public Institution toDomain(JsonObject representation) {
return new Institution(getProperty(representation, "id"),
getProperty(representation, "name"));
getProperty(representation, "name"),
getProperty(representation, "code"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
public class LibraryMapper {
public Library toDomain(JsonObject representation) {
return new Library(getProperty(representation, "id"),
getProperty(representation, "name"));
getProperty(representation, "name"),
getProperty(representation, "code"));
}
}
58 changes: 58 additions & 0 deletions src/test/java/api/requests/RequestsAPICreationTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,64 @@ void canCreateTitleLevelRequestWhenTlrEnabled() {
assertThat(publishedEvents.filterToList(byEventType("LOAN_DUE_DATE_CHANGED")), hasSize(0));
}

@ParameterizedTest
@CsvSource(value = {
"NU/JC/DL/3F",
"DLRC",
"JC",
"NU"
})
void createTitleLevelRequestWhenTlrEnabledSetLocation(String locationCode) {
UUID patronId = usersFixture.charlotte().getId();
final UUID pickupServicePointId = servicePointsFixture.cd1().getId();

final var items = itemsFixture.createMultipleItemsForTheSameInstance(2);
UUID instanceId = items.get(0).getInstanceId();

configurationsFixture.enableTlrFeature();

IndividualResource requestResource = requestsClient.create(new RequestBuilder()
.page()
.withNoHoldingsRecordId()
.withNoItemId()
.titleRequestLevel()
.withInstanceId(instanceId)
.withPickupServicePointId(pickupServicePointId)
.withRequesterId(patronId)
.withItemLocationCode(locationCode));

JsonObject request = requestResource.getJson();
assertThat(request.getString("requestLevel"), is("Title"));
}

@Test
void createTitleLevelRequestWhenTlrEnabledSetLocationNoItems() {
UUID patronId = usersFixture.charlotte().getId();
final UUID pickupServicePointId = servicePointsFixture.cd1().getId();

final var items = itemsFixture.createMultipleItemsForTheSameInstance(2);
UUID instanceId = items.get(0).getInstanceId();

configurationsFixture.enableTlrFeature();

Response response = requestsClient.attemptCreate(
new RequestBuilder()
.page()
.withNoHoldingsRecordId()
.withNoItemId()
.titleRequestLevel()
.withInstanceId(instanceId)
.withPickupServicePointId(pickupServicePointId)
.withRequesterId(patronId)
.withItemLocationCode("DoesNotExist")
.create());

assertThat(response.getStatusCode(), is(422));
assertThat(response.getJson(), hasErrorWith(
hasMessage("Cannot create page TLR for this instance ID - no pageable " +
"available items found in requested location")));
}

@Test
void cannotCreateRequestWithNonExistentRequestLevelWhenTlrEnabled() {
UUID patronId = usersFixture.charlotte().getId();
Expand Down
17 changes: 11 additions & 6 deletions src/test/java/api/support/builders/RequestBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static api.support.utl.DateTimeUtils.getLocalDatePropertyForDateWithTime;
import static java.time.ZoneOffset.UTC;
import static java.util.stream.Collectors.toList;
import static org.folio.circulation.domain.representations.RequestProperties.ITEM_LOCATION_CODE;
import static org.folio.circulation.support.json.JsonPropertyFetcher.getDateTimeProperty;
import static org.folio.circulation.support.json.JsonPropertyFetcher.getIntegerProperty;
import static org.folio.circulation.support.json.JsonPropertyFetcher.getLocalDateProperty;
Expand Down Expand Up @@ -63,6 +64,7 @@ public class RequestBuilder extends JsonBuilder implements Builder {
private final Tags tags;
private final String patronComments;
private final BlockOverrides blockOverrides;
private final String itemLocationCode;
private final PrintDetails printDetails;

public RequestBuilder() {
Expand Down Expand Up @@ -91,6 +93,7 @@ public RequestBuilder() {
null,
null,
null,
null,
null);
}

Expand Down Expand Up @@ -123,6 +126,7 @@ public static RequestBuilder from(IndividualResource response) {
new Tags((toStream(representation.getJsonObject("tags"), "tagList").collect(toList()))),
getProperty(representation, "patronComments"),
null,
getProperty(representation, ITEM_LOCATION_CODE),
PrintDetails.fromRepresentation(representation)
);
}
Expand Down Expand Up @@ -152,6 +156,7 @@ public JsonObject create() {
put(request, "cancelledDate", formatDateTimeOptional(cancelledDate));
put(request, "pickupServicePointId", this.pickupServicePointId);
put(request, "patronComments", this.patronComments);
put(request, ITEM_LOCATION_CODE, this.itemLocationCode);

if (itemSummary != null) {
final JsonObject itemRepresentation = new JsonObject();
Expand Down Expand Up @@ -340,12 +345,12 @@ public static class PrintDetails {
public static PrintDetails fromRepresentation(JsonObject representation) {
JsonObject printDetails = representation.getJsonObject("printDetails");
if (printDetails != null) {
final Integer printCount = printDetails.getInteger("printCount");
final String requesterId = printDetails.getString("requesterId");
final Boolean isPrinted = printDetails.getBoolean("isPrinted");
final String printEventDate = printDetails.getString("printEventDate");
return new PrintDetails(printCount, requesterId, isPrinted,
printEventDate);
final Integer printCount = printDetails.getInteger("printCount");
final String requesterId = printDetails.getString("requesterId");
final Boolean isPrinted = printDetails.getBoolean("isPrinted");
final String printEventDate = printDetails.getString("printEventDate");
return new PrintDetails(printCount, requesterId, isPrinted,
printEventDate);
}
return null;
}
Expand Down

0 comments on commit cf733df

Please sign in to comment.