Skip to content

Commit

Permalink
CIRC-2136 Add support for floating collections
Browse files Browse the repository at this point in the history
  • Loading branch information
nielserik committed Aug 28, 2024
1 parent 1d3bbe2 commit bf8f964
Show file tree
Hide file tree
Showing 18 changed files with 565 additions and 32 deletions.
48 changes: 39 additions & 9 deletions src/main/java/org/folio/circulation/domain/Item.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public class Item {
@Getter
private final String shelvingOrder;
@NonNull private final Location permanentLocation;

@Getter
private final Location floatDestinationLocation;
private final ServicePoint inTransitDestinationServicePoint;

private boolean changed;
Expand All @@ -57,7 +60,7 @@ public static Item from(JsonObject representation) {
}
public Item(String id, JsonObject itemRepresentation, Location effectiveLocation,
LastCheckIn lastCheckIn, CallNumberComponents callNumberComponents,
String shelvingOrder, Location permanentLocation,
String shelvingOrder, Location permanentLocation, Location floatDestinationLocation,
ServicePoint inTransitDestinationServicePoint, boolean changed,
Holdings holdings, Instance instance, MaterialType materialType,
LoanType loanType, ItemDescription description) {
Expand All @@ -69,6 +72,7 @@ public Item(String id, JsonObject itemRepresentation, Location effectiveLocation
this.callNumberComponents = callNumberComponents;
this.shelvingOrder = shelvingOrder;
this.permanentLocation = permanentLocation;
this.floatDestinationLocation = floatDestinationLocation;
this.inTransitDestinationServicePoint = inTransitDestinationServicePoint;
this.changed = changed;
this.holdings = holdings;
Expand Down Expand Up @@ -348,59 +352,85 @@ public String getPermanentLocationId() {
return firstNonBlank(permanentLocation.getId(), holdings.getPermanentLocationId());
}

public String getFloatDestinationLocationId() {
return floatDestinationLocation != null ? floatDestinationLocation.getId() : null;
}

public boolean canFloatThroughCheckInServicePoint() {
return getLocation() != null &&
getLocation().isFloatingCollection()
&& getFloatDestinationLocation().getId() != null;
}

public Item withLocation(Location newLocation) {
return new Item(this.id, this.itemRepresentation, newLocation,
this.lastCheckIn, this.callNumberComponents, this.shelvingOrder,
this.permanentLocation, this.inTransitDestinationServicePoint, this.changed,
this.permanentLocation, this.floatDestinationLocation,
this.inTransitDestinationServicePoint, this.changed,
this.holdings, this.instance, this.materialType, this.loanType, description);
}

public Item withMaterialType(@NonNull MaterialType materialType) {
return new Item(this.id, this.itemRepresentation, this.location,
this.lastCheckIn, this.callNumberComponents, this.shelvingOrder,
this.permanentLocation, this.inTransitDestinationServicePoint, this.changed,
this.permanentLocation, this.floatDestinationLocation,
this.inTransitDestinationServicePoint, this.changed,
this.holdings, this.instance, materialType, this.loanType, this.description);
}

public Item withHoldings(@NonNull Holdings holdings) {
return new Item(this.id, this.itemRepresentation, this.location,
this.lastCheckIn, this.callNumberComponents, this.shelvingOrder,
this.permanentLocation, this.inTransitDestinationServicePoint, this.changed,
this.permanentLocation, this.floatDestinationLocation,
this.inTransitDestinationServicePoint, this.changed,
holdings, this.instance, this.materialType, this.loanType, this.description);
}

public Item withInstance(@NonNull Instance instance) {
return new Item(this.id, this.itemRepresentation, this.location,
this.lastCheckIn, this.callNumberComponents, this.shelvingOrder,
this.permanentLocation, this.inTransitDestinationServicePoint, this.changed,
this.permanentLocation, this.floatDestinationLocation,
this.inTransitDestinationServicePoint, this.changed,
this.holdings, instance, this.materialType, this.loanType, this.description);
}

public Item withLoanType(@NonNull LoanType loanType) {
return new Item(this.id, this.itemRepresentation, this.location,
this.lastCheckIn, this.callNumberComponents, this.shelvingOrder,
this.permanentLocation, this.inTransitDestinationServicePoint, this.changed,
this.permanentLocation, this.floatDestinationLocation,
this.inTransitDestinationServicePoint, this.changed,
this.holdings, this.instance, this.materialType, loanType, this.description);
}

public Item withLastCheckIn(@NonNull LastCheckIn lastCheckIn) {
return new Item(this.id, this.itemRepresentation, this.location,
lastCheckIn, this.callNumberComponents, this.shelvingOrder,
this.permanentLocation, this.inTransitDestinationServicePoint, this.changed,
this.permanentLocation, this.floatDestinationLocation,
this.inTransitDestinationServicePoint, this.changed,
this.holdings, this.instance, this.materialType, this.loanType, this.description);
}

public Item withPermanentLocation(Location permanentLocation) {
return new Item(this.id, this.itemRepresentation, this.location,
this.lastCheckIn, this.callNumberComponents, this.shelvingOrder,
permanentLocation, this.inTransitDestinationServicePoint, this.changed,
permanentLocation, this.floatDestinationLocation,
this.inTransitDestinationServicePoint, this.changed,
this.holdings, this.instance, this.materialType, this.loanType, this.description);
}

public Item withFloatDestinationLocation(Location floatDestinationLocation) {
return new Item(this.id, this.itemRepresentation, this.location,
this.lastCheckIn, this.callNumberComponents, this.shelvingOrder,
this.permanentLocation, floatDestinationLocation,
this.inTransitDestinationServicePoint, this.changed,
this.holdings, this.instance, this.materialType, this.loanType, this.description);
}

public Item withInTransitDestinationServicePoint(ServicePoint servicePoint) {
return new Item(this.id, this.itemRepresentation, this.location,
this.lastCheckIn, this.callNumberComponents, this.shelvingOrder,
this.permanentLocation, servicePoint, this.changed, this.holdings,
this.permanentLocation, this.floatDestinationLocation,
servicePoint, this.changed, this.holdings,
this.instance, this.materialType, this.loanType, this.description);
}

Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/folio/circulation/domain/Location.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class Location {
String discoveryDisplayName;
@NonNull Collection<UUID> servicePointIds;
UUID primaryServicePointId;
Boolean isFloatingCollection;
@NonNull Institution institution;
@NonNull Campus campus;
@NonNull Library library;
Expand All @@ -27,6 +28,7 @@ public static Location unknown() {

public static Location unknown(String id) {
return new Location(id, null, null, null, List.of(), null,
false,
Institution.unknown(null), Campus.unknown(null), Library.unknown(null),
ServicePoint.unknown());
}
Expand Down Expand Up @@ -85,23 +87,31 @@ public ServicePoint getPrimaryServicePoint() {
return primaryServicePoint;
}

public boolean isFloatingCollection() {
return isFloatingCollection;
}

public Location withInstitution(Institution institution) {
return new Location(id, name, code, discoveryDisplayName, servicePointIds, primaryServicePointId,
isFloatingCollection,
institution, campus, library, primaryServicePoint);
}

public Location withCampus(Campus campus) {
return new Location(id, name, code, discoveryDisplayName, servicePointIds, primaryServicePointId,
isFloatingCollection,
institution, campus, library, primaryServicePoint);
}

public Location withLibrary(Library library) {
return new Location(id, name, code, discoveryDisplayName, servicePointIds, primaryServicePointId,
isFloatingCollection,
institution, campus, library, primaryServicePoint);
}

public Location withPrimaryServicePoint(ServicePoint servicePoint) {
return new Location(id, name, code, discoveryDisplayName, servicePointIds, primaryServicePointId,
isFloatingCollection,
institution, campus, library, servicePoint);
}

Expand Down
7 changes: 4 additions & 3 deletions src/main/java/org/folio/circulation/domain/UpdateItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,12 @@ private Result<Item> changeItemOnCheckIn(Item item, Request request, UUID checkI
return changeItemWithOutstandingRequest(item, request, checkInServicePointId);
} else {
if(Optional.ofNullable(item.getLocation())
.map(location -> location.homeLocationIsServedBy(checkInServicePointId))
.map(location ->
location.homeLocationIsServedBy(checkInServicePointId)
|| (item.canFloatThroughCheckInServicePoint()))
.orElse(false)) {
return succeeded(item.available());
}
else {
} else {
return succeeded(item.inTransitToHome());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static java.util.concurrent.CompletableFuture.supplyAsync;
import static java.util.function.Function.identity;
import static org.folio.circulation.domain.ItemStatus.AVAILABLE;
import static org.folio.circulation.domain.ItemStatus.IN_TRANSIT;
import static org.folio.circulation.domain.MultipleRecords.CombinationMatchers.matchRecordsById;
import static org.folio.circulation.domain.representations.ItemProperties.LAST_CHECK_IN;
import static org.folio.circulation.domain.representations.ItemProperties.STATUS_PROPERTY;
Expand Down Expand Up @@ -91,6 +92,7 @@ public CompletableFuture<Result<Item>> updateItem(Item item) {
log.debug("updateItem:: parameters item: {}", item);

final String IN_TRANSIT_DESTINATION_SERVICE_POINT_ID = "inTransitDestinationServicePointId";
final String TEMPORARY_LOCATION_ID = "temporaryLocationId";

if (item == null) {
log.info("updateItem:: item is null");
Expand All @@ -108,8 +110,14 @@ public CompletableFuture<Result<Item>> updateItem(Item item) {
new JsonObject().put("name", item.getStatus().getValue()));

remove(updatedItemRepresentation, IN_TRANSIT_DESTINATION_SERVICE_POINT_ID);
write(updatedItemRepresentation, IN_TRANSIT_DESTINATION_SERVICE_POINT_ID,
item.getInTransitDestinationServicePointId());
if (item.isInStatus(IN_TRANSIT)) {
write(updatedItemRepresentation, IN_TRANSIT_DESTINATION_SERVICE_POINT_ID,
item.getInTransitDestinationServicePointId());
} else if (item.canFloatThroughCheckInServicePoint()) {
remove(updatedItemRepresentation, TEMPORARY_LOCATION_ID);
write(updatedItemRepresentation, TEMPORARY_LOCATION_ID,
item.getFloatDestinationLocationId());
}

final var lastCheckIn = item.getLastCheckIn();

Expand Down Expand Up @@ -176,7 +184,8 @@ private CompletableFuture<Result<MultipleRecords<Item>>> fetchLocations(
return result.combineAfter(this::fetchLocations,
(items, locations) -> items
.combineRecords(locations, Item::getPermanentLocationId, Item::withPermanentLocation, null)
.combineRecords(locations, Item::getEffectiveLocationId, Item::withLocation, null));
.combineRecords(locations, Item::getEffectiveLocationId, Item::withLocation, null)
.combineRecords(locations, Item::getFloatDestinationLocationId, Item::withFloatDestinationLocation, null));
}

private CompletableFuture<Result<Map<String, Location>>> fetchLocations(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ private void checkIn(RoutingContext routingContext) {
.thenComposeAsync(checkInLoan -> checkInLoan.combineAfter(
processAdapter::updateRequestQueue, CheckInContext::withRequestQueue))
.thenComposeAsync(r -> r.after(processAdapter::findFulfillableRequest))
.thenComposeAsync(checkInContextResult ->
checkInContextResult.combineAfter(processAdapter::findFloatingDestination,
CheckInContext::withItemAndUpdatedLoan))
.thenComposeAsync(updateRequestQueueResult -> updateRequestQueueResult.combineAfter(
processAdapter::updateItem, CheckInContext::withItemAndUpdatedLoan))
.thenApply(handleItemStatus -> handleItemStatus.next(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.apache.logging.log4j.Logger;
import org.folio.circulation.domain.CheckInContext;
import org.folio.circulation.domain.Item;
import org.folio.circulation.domain.Location;
import org.folio.circulation.domain.Loan;
import org.folio.circulation.domain.LoanCheckInService;
import org.folio.circulation.domain.OverdueFineService;
Expand All @@ -27,6 +28,7 @@
import org.folio.circulation.infrastructure.storage.feesandfines.FeeFineOwnerRepository;
import org.folio.circulation.infrastructure.storage.feesandfines.FeeFineRepository;
import org.folio.circulation.infrastructure.storage.inventory.ItemRepository;
import org.folio.circulation.infrastructure.storage.inventory.LocationRepository;
import org.folio.circulation.infrastructure.storage.loans.LoanPolicyRepository;
import org.folio.circulation.infrastructure.storage.loans.LoanRepository;
import org.folio.circulation.infrastructure.storage.loans.OverdueFinePolicyRepository;
Expand Down Expand Up @@ -57,6 +59,7 @@ class CheckInProcessAdapter {
private final UpdateRequestQueue requestQueueUpdate;
private final LoanRepository loanRepository;
private final ServicePointRepository servicePointRepository;
private final LocationRepository locationRepository;
private final UserRepository userRepository;
private final AddressTypeRepository addressTypeRepository;
private final LogCheckInService logCheckInService;
Expand All @@ -75,6 +78,7 @@ class CheckInProcessAdapter {
RequestQueueRepository requestQueueRepository,
UpdateItem updateItem, UpdateRequestQueue requestQueueUpdate,
LoanRepository loanRepository, ServicePointRepository servicePointRepository,
LocationRepository locationRepository,
UserRepository userRepository,
AddressTypeRepository addressTypeRepository,
LogCheckInService logCheckInService,
Expand All @@ -93,6 +97,7 @@ class CheckInProcessAdapter {
this.requestQueueUpdate = requestQueueUpdate;
this.loanRepository = loanRepository;
this.servicePointRepository = servicePointRepository;
this.locationRepository = locationRepository;
this.userRepository = userRepository;
this.addressTypeRepository = addressTypeRepository;
this.logCheckInService = logCheckInService;
Expand Down Expand Up @@ -134,6 +139,7 @@ public static CheckInProcessAdapter newInstance(Clients clients,
requestQueueRepository),
loanRepository,
new ServicePointRepository(clients),
LocationRepository.using(clients),
userRepository,
new AddressTypeRepository(clients),
new LogCheckInService(clients),
Expand Down Expand Up @@ -296,4 +302,17 @@ CompletableFuture<Result<CheckInContext>> findFulfillableRequest(CheckInContext
return requestQueueService.findRequestFulfillableByItem(context.getItem(), context.getRequestQueue())
.thenApply(r -> r.map(context::withHighestPriorityFulfillableRequest));
}

CompletableFuture<Result<Item>> findFloatingDestination(CheckInContext context) {
Item item = context.getItem();
if (item.getLocation().isFloatingCollection()) {
return locationRepository.fetchLocationsForServicePoint(context.getCheckInServicePointId().toString())
.thenApply(rLocations -> rLocations.map(locations -> locations.stream()
.filter(Location::isFloatingCollection).findFirst()
.map(item::withFloatDestinationLocation).orElse(item)));
} else {
return Result.ofAsync(item);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
import io.vertx.core.json.JsonObject;

public class ItemMapper {

public Item toDomain(JsonObject representation) {
return new Item(getProperty(representation, "id"), representation,
Location.unknown(getProperty(representation, "effectiveLocationId")),
LastCheckIn.fromItemJson(representation),
CallNumberComponents.fromItemJson(representation),
getProperty(representation, "effectiveShelvingOrder"),
Location.unknown(getProperty(representation, "permanentLocationId")),
Location.unknown(),
getInTransitServicePoint(representation), false,
Holdings.unknown(getProperty(representation, "holdingsRecordId")),
Instance.unknown(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static org.folio.circulation.support.json.JsonPropertyFetcher.getArrayProperty;
import static org.folio.circulation.support.json.JsonPropertyFetcher.getProperty;
import static org.folio.circulation.support.json.JsonPropertyFetcher.getBooleanProperty;


import java.util.Collection;
import java.util.Optional;
Expand All @@ -24,6 +26,7 @@ public Location toDomain(JsonObject representation) {
getProperty(representation, "discoveryDisplayName"),
getServicePointIds(representation),
getPrimaryServicePointId(representation),
getBooleanProperty(representation, "isFloatingCollection"),
Institution.unknown(getProperty(representation, "institutionId")),
Campus.unknown(getProperty(representation, "campusId")),
Library.unknown(getProperty(representation, "libraryId")),
Expand Down
Loading

0 comments on commit bf8f964

Please sign in to comment.