diff --git a/NEWS.md b/NEWS.md
index 9b58faeb74..fc0f2b0dc1 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,34 @@
-## 24.3.0
-
-* [CIRC-2156](https://folio-org.atlassian.net/browse/CIRC-2156) Upgrade "holdings-storage" to 8.0
+## 24.3.0 2024-10-31
+
+* Support floating collections (CIRC-2136)
+* Correct permissions change in MODCAL-136 (MODCAL-136)
+* Upgrade to RMB v35.3.0 (CIRC-2163)
+* Add TLR Hold requests handling to the print slips logic (CIRC-2134)
+* Add support for interface instance-storage 11.0 (CIRC-2153)
+* Upgrade the API version in the ModuleDescriptor-template.json (CIRC-2156)
+* Delete obsolete notes permission (CIRC-2154)
+* Extend the loan representation with additional fields required for Due date receipt (CIRC-2144)
+* Revert CIRC-2100 PR and rework for PrintEventDetail pagination, searching, and sorting (CIRC-2148)
+* Allow specifying item location when creating title-level requests (CIRC-2141)
+* Bypass pickup SP check for Delivery requests (CIRC-2122)
+* Fetch print details while fetching request details based on CQL query (CIRC-2100)
+* Error message - after service point changes from `yes` to `no` as Pickup location (CIRC-1980)
+* Implement POST API (CIRC-2099)
+* Add test case on enableRequestPrintDetailsSetting (CIRC-2096)
+* Create API wrapping settings CRUD (CIRC-2111)
+* Update error response status and schema for request creation (CIRC-2104)
+* Update interface holdings-storage to version 7.0 (CIRC-2095)
+* Add isDcb flag value in the loan event for Check-out (CIRC-2084)
+* Sort allowed service points by name (CIRC-2094)
+* Fix Kafka configuration (CIRC-2037)
+* Fix empty template token, `chargeDate`, in reminder notices (CIRC-2077)
+* Support override of renewal block due to reminders (CIRC-2019)
+* Keep due date when it is within recall return interval (CIRC-2043)
+* Ignore `Loan related fee/fine closed` events when loan is already closed (CIRC-2066)
+* Apply alternate loan period when multiple requests are in fulfillment in progress (CIRC-2026)
+* Add missing permissions (CIRC-2070)
+* Set returnDate for lost loans (CIRC-2044)
+* Fix item details not fully populated when response contains more than 50 loans (CIRC-2059)
## 24.2.0 2024-03-21
diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json
index 0e507b90cf..689e529e11 100644
--- a/descriptors/ModuleDescriptor-template.json
+++ b/descriptors/ModuleDescriptor-template.json
@@ -132,7 +132,8 @@
"methods": ["POST"],
"pathPattern": "/circulation/loans/{id}/change-due-date",
"permissionsRequired": [
- "circulation.loans.change-due-date.post"
+ "circulation.loans.change-due-date.post",
+ "configuration.entries.collection.get"
],
"modulePermissions": [
"modperms.circulation.loans.change-due-date.post"
@@ -1151,7 +1152,8 @@
"accounts.item.post",
"feefineactions.item.post",
"circulation-storage.loans-history.collection.get",
- "calendar.endpoint.dates.get"
+ "calendar.endpoint.calendars.surroundingOpenings.get",
+ "calendar.endpoint.calendars.allOpenings.get"
],
"schedule": {
"cron": "1 0 * * *",
@@ -1797,6 +1799,8 @@
"proxiesfor.collection.get",
"patron-notice.post",
"configuration.entries.collection.get",
+ "calendar.endpoint.calendars.surroundingOpenings.get",
+ "calendar.endpoint.calendars.allOpenings.get",
"mod-settings.entries.item.get",
"mod-settings.entries.collection.get",
"mod-settings.global.read.circulation",
@@ -1811,7 +1815,8 @@
"displayName" : "module permissions for one op",
"description" : "to reduce X-Okapi-Token size",
"subPermissions": [
- "calendar.endpoint.dates.get",
+ "calendar.endpoint.calendars.surroundingOpenings.get",
+ "calendar.endpoint.calendars.allOpenings.get",
"circulation-storage.loans.item.post",
"circulation-storage.loans.item.get",
"circulation-storage.loans.collection.get",
@@ -1909,7 +1914,8 @@
"accounts.refund.post",
"accounts.cancel.post",
"configuration.entries.collection.get",
- "calendar.endpoint.dates.get",
+ "calendar.endpoint.calendars.surroundingOpenings.get",
+ "calendar.endpoint.calendars.allOpenings.get",
"actual-cost-record-storage.actual-cost-records.collection.get",
"actual-cost-record-storage.actual-cost-records.item.get",
"actual-cost-fee-fine-cancel.post",
@@ -1948,7 +1954,8 @@
"users.collection.get",
"addresstypes.collection.get",
"proxiesfor.collection.get",
- "calendar.endpoint.dates.get",
+ "calendar.endpoint.calendars.surroundingOpenings.get",
+ "calendar.endpoint.calendars.allOpenings.get",
"configuration.entries.collection.get",
"mod-settings.entries.item.get",
"mod-settings.entries.collection.get",
@@ -2133,7 +2140,8 @@
"usergroups.item.get",
"proxiesfor.collection.get",
"patron-notice.post",
- "calendar.endpoint.dates.get",
+ "calendar.endpoint.calendars.surroundingOpenings.get",
+ "calendar.endpoint.calendars.allOpenings.get",
"scheduled-notice-storage.scheduled-notices.collection.delete",
"scheduled-notice-storage.scheduled-notices.item.post",
"configuration.entries.collection.get",
@@ -2192,7 +2200,8 @@
"displayName": "module permissions for one op",
"description": "to reduce X-Okapi-Token size",
"subPermissions": [
- "calendar.endpoint.dates.get",
+ "calendar.endpoint.calendars.surroundingOpenings.get",
+ "calendar.endpoint.calendars.allOpenings.get",
"circulation-storage.loan-policies.item.get",
"circulation-storage.loans.item.get",
"circulation-storage.loans.item.put",
@@ -2302,7 +2311,8 @@
"usergroups.item.get",
"proxiesfor.collection.get",
"patron-notice.post",
- "calendar.endpoint.dates.get",
+ "calendar.endpoint.calendars.surroundingOpenings.get",
+ "calendar.endpoint.calendars.allOpenings.get",
"configuration.entries.collection.get",
"mod-settings.entries.item.get",
"mod-settings.entries.collection.get",
diff --git a/pom.xml b/pom.xml
index 964270022e..cb9a7e5828 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
mod-circulation
org.folio
- 24.3.0-SNAPSHOT
+ 24.4.0-SNAPSHOT
Apache License 2.0
diff --git a/src/main/java/org/folio/circulation/domain/Item.java b/src/main/java/org/folio/circulation/domain/Item.java
index 2dc5e1469e..5fd69c5255 100644
--- a/src/main/java/org/folio/circulation/domain/Item.java
+++ b/src/main/java/org/folio/circulation/domain/Item.java
@@ -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;
@@ -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) {
@@ -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;
@@ -348,59 +352,86 @@ 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() != null
+ && 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);
}
diff --git a/src/main/java/org/folio/circulation/domain/Location.java b/src/main/java/org/folio/circulation/domain/Location.java
index a052fc9a17..5143ae4af9 100644
--- a/src/main/java/org/folio/circulation/domain/Location.java
+++ b/src/main/java/org/folio/circulation/domain/Location.java
@@ -16,6 +16,7 @@ public class Location {
String discoveryDisplayName;
@NonNull Collection servicePointIds;
UUID primaryServicePointId;
+ Boolean isFloatingCollection;
@NonNull Institution institution;
@NonNull Campus campus;
@NonNull Library library;
@@ -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());
}
@@ -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);
}
diff --git a/src/main/java/org/folio/circulation/domain/UpdateItem.java b/src/main/java/org/folio/circulation/domain/UpdateItem.java
index 9ba0caa300..cbf459554a 100644
--- a/src/main/java/org/folio/circulation/domain/UpdateItem.java
+++ b/src/main/java/org/folio/circulation/domain/UpdateItem.java
@@ -57,11 +57,12 @@ private Result- 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());
}
}
diff --git a/src/main/java/org/folio/circulation/domain/notice/TemplateContextUtil.java b/src/main/java/org/folio/circulation/domain/notice/TemplateContextUtil.java
index 5e66ecc48c..afc471a854 100644
--- a/src/main/java/org/folio/circulation/domain/notice/TemplateContextUtil.java
+++ b/src/main/java/org/folio/circulation/domain/notice/TemplateContextUtil.java
@@ -25,6 +25,7 @@
import org.folio.circulation.domain.FeeFineAction;
import org.folio.circulation.domain.Instance;
import org.folio.circulation.domain.Item;
+import org.folio.circulation.domain.ItemStatus;
import org.folio.circulation.domain.Loan;
import org.folio.circulation.domain.Location;
import org.folio.circulation.domain.Request;
@@ -213,7 +214,8 @@ private static JsonObject createItemContext(Item item) {
.put("displaySummary", item.getDisplaySummary())
.put("descriptionOfPieces", item.getDescriptionOfPieces());
- Location location = item.getLocation();
+ Location location = (item.canFloatThroughCheckInServicePoint() && item.isInStatus(ItemStatus.AVAILABLE)) ?
+ item.getFloatDestinationLocation() : item.getLocation();
if (location != null) {
log.info("createItemContext:: location is not null");
diff --git a/src/main/java/org/folio/circulation/domain/representations/ItemSummaryRepresentation.java b/src/main/java/org/folio/circulation/domain/representations/ItemSummaryRepresentation.java
index 9e6cd27cfd..1dde6fc960 100644
--- a/src/main/java/org/folio/circulation/domain/representations/ItemSummaryRepresentation.java
+++ b/src/main/java/org/folio/circulation/domain/representations/ItemSummaryRepresentation.java
@@ -12,6 +12,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.circulation.domain.Item;
+import org.folio.circulation.domain.ItemStatus;
import org.folio.circulation.domain.Location;
import org.folio.circulation.domain.ServicePoint;
@@ -79,7 +80,10 @@ public JsonObject createItemSummary(Item item) {
final Location location = item.getLocation();
- if (location != null) {
+ if (item.canFloatThroughCheckInServicePoint() && item.isInStatus(ItemStatus.AVAILABLE)) {
+ itemSummary.put("location", new JsonObject()
+ .put("name", item.getFloatDestinationLocation().getName()));
+ } else if (location != null) {
log.info("createItemSummary:: location is not null");
itemSummary.put("location", new JsonObject()
.put("name", location.getName()));
diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/inventory/ItemRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/inventory/ItemRepository.java
index 3603768dda..ffe39a8bce 100644
--- a/src/main/java/org/folio/circulation/infrastructure/storage/inventory/ItemRepository.java
+++ b/src/main/java/org/folio/circulation/infrastructure/storage/inventory/ItemRepository.java
@@ -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;
@@ -91,6 +92,7 @@ public CompletableFuture> 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");
@@ -108,8 +110,14 @@ public CompletableFuture> 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.isInStatus(AVAILABLE) && item.canFloatThroughCheckInServicePoint()) {
+ remove(updatedItemRepresentation, TEMPORARY_LOCATION_ID);
+ write(updatedItemRepresentation, TEMPORARY_LOCATION_ID,
+ item.getFloatDestinationLocationId());
+ }
final var lastCheckIn = item.getLastCheckIn();
@@ -176,7 +184,8 @@ private CompletableFuture>> 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>> fetchLocations(
diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/inventory/LocationRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/inventory/LocationRepository.java
index 6099d95589..39708f8478 100644
--- a/src/main/java/org/folio/circulation/infrastructure/storage/inventory/LocationRepository.java
+++ b/src/main/java/org/folio/circulation/infrastructure/storage/inventory/LocationRepository.java
@@ -40,6 +40,7 @@
import org.folio.circulation.support.SingleRecordFetcher;
import org.folio.circulation.support.fetching.CqlQueryFinder;
import org.folio.circulation.support.http.client.CqlQuery;
+import org.folio.circulation.support.http.client.PageLimit;
import org.folio.circulation.support.results.Result;
public class LocationRepository {
@@ -87,6 +88,11 @@ public CompletableFuture> getPermanentLocation(Item item) {
return getLocation(item, Item::getPermanentLocationId);
}
+ public CompletableFuture> getFloatDestinationLocation(Item item) {
+ log.debug("getFloatDestinationLocation:: parameters item: {}", item);
+ return getLocation(item, Item::getFloatDestinationLocationId);
+ }
+
private CompletableFuture> getLocation(Item item,
Function
- locationIdGetter) {
log.debug("getLocation:: parameters item: {}", item);
@@ -315,7 +321,7 @@ public CompletableFuture>> fetchLocationsForServiceP
log.debug("fetchLocationsForServicePoint:: parameters servicePointId: {}", servicePointId);
return new CqlQueryFinder<>(locationsStorageClient, "locations", new LocationMapper()::toDomain)
- .findByQuery(CqlQuery.match("servicePointIds", servicePointId))
+ .findByQuery(CqlQuery.match("servicePointIds", servicePointId), PageLimit.maximumLimit())
.thenApply(r -> r.map(MultipleRecords::getRecords));
}
diff --git a/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java b/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java
index a63e63446b..192485e7d2 100644
--- a/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java
+++ b/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java
@@ -107,6 +107,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(
diff --git a/src/main/java/org/folio/circulation/resources/CheckInProcessAdapter.java b/src/main/java/org/folio/circulation/resources/CheckInProcessAdapter.java
index f721f48863..d9ec1c876d 100644
--- a/src/main/java/org/folio/circulation/resources/CheckInProcessAdapter.java
+++ b/src/main/java/org/folio/circulation/resources/CheckInProcessAdapter.java
@@ -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;
@@ -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;
@@ -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;
@@ -75,6 +78,7 @@ class CheckInProcessAdapter {
RequestQueueRepository requestQueueRepository,
UpdateItem updateItem, UpdateRequestQueue requestQueueUpdate,
LoanRepository loanRepository, ServicePointRepository servicePointRepository,
+ LocationRepository locationRepository,
UserRepository userRepository,
AddressTypeRepository addressTypeRepository,
LogCheckInService logCheckInService,
@@ -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;
@@ -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),
@@ -297,4 +303,19 @@ CompletableFuture> findFulfillableRequest(CheckInContext
return requestQueueService.findRequestFulfillableByItem(context.getItem(), context.getRequestQueue())
.thenApply(r -> r.map(context::withHighestPriorityFulfillableRequest));
}
+
+ CompletableFuture> 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)))
+ .thenCompose(it -> locationRepository.getFloatDestinationLocation(it.value()))
+ .thenApply(location -> Result.succeeded(item.withFloatDestinationLocation(location.value())));
+ } else {
+ return Result.ofAsync(item);
+ }
+ }
+
}
diff --git a/src/main/java/org/folio/circulation/storage/mappers/ItemMapper.java b/src/main/java/org/folio/circulation/storage/mappers/ItemMapper.java
index aa56840d65..9d4174a41d 100644
--- a/src/main/java/org/folio/circulation/storage/mappers/ItemMapper.java
+++ b/src/main/java/org/folio/circulation/storage/mappers/ItemMapper.java
@@ -20,6 +20,7 @@
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")),
@@ -27,6 +28,7 @@ public Item toDomain(JsonObject representation) {
CallNumberComponents.fromItemJson(representation),
getProperty(representation, "effectiveShelvingOrder"),
Location.unknown(getProperty(representation, "permanentLocationId")),
+ Location.unknown(),
getInTransitServicePoint(representation), false,
Holdings.unknown(getProperty(representation, "holdingsRecordId")),
Instance.unknown(),
diff --git a/src/main/java/org/folio/circulation/storage/mappers/LocationMapper.java b/src/main/java/org/folio/circulation/storage/mappers/LocationMapper.java
index 105267b7bb..bc004c233e 100644
--- a/src/main/java/org/folio/circulation/storage/mappers/LocationMapper.java
+++ b/src/main/java/org/folio/circulation/storage/mappers/LocationMapper.java
@@ -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;
@@ -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")),
diff --git a/src/test/java/api/loans/scenarios/FloatingItemsTests.java b/src/test/java/api/loans/scenarios/FloatingItemsTests.java
new file mode 100644
index 0000000000..6eccec7cf5
--- /dev/null
+++ b/src/test/java/api/loans/scenarios/FloatingItemsTests.java
@@ -0,0 +1,423 @@
+package api.loans.scenarios;
+
+import api.support.APITests;
+import api.support.CheckInByBarcodeResponse;
+import api.support.builders.CheckInByBarcodeRequestBuilder;
+import api.support.builders.LocationBuilder;
+import api.support.fixtures.ItemExamples;
+import api.support.http.IndividualResource;
+import api.support.http.ItemResource;
+import io.vertx.core.json.JsonObject;
+import org.folio.circulation.support.utils.ClockUtil;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.core.IsNull;
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static api.support.matchers.UUIDMatcher.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class FloatingItemsTests extends APITests {
+
+ @Test
+ void willSetFloatingItemsTemporaryLocationToFloatingCollectionAtServicePoint() {
+
+ // Floating collection served by service point 'cd1'.
+ final IndividualResource floatingCollection = locationsFixture.floatingCollection();
+
+ // Another floating collection serviced by another service point
+ final IndividualResource servicePointTwo = servicePointsFixture.cd2();
+ IndividualResource otherFloatingCollection = locationsFixture.createLocation(
+ new LocationBuilder()
+ .withName("Floating collection 2")
+ .forInstitution(UUID.randomUUID())
+ .forCampus(UUID.randomUUID())
+ .forLibrary(UUID.randomUUID())
+ .withCode("FLOAT2")
+ .isFloatingCollection(true)
+ .withPrimaryServicePoint(servicePointTwo.getId()));
+
+ final IndividualResource james = usersFixture.james();
+
+ final IndividualResource holdingsInFloatingLocation =
+ holdingsFixture.createHoldingsRecord(UUID.randomUUID(), floatingCollection.getId());
+
+ IndividualResource nod = itemsClient.create(ItemExamples.basedUponNod(
+ materialTypesFixture.book().getId(),
+ loanTypesFixture.canCirculate().getId())
+ .withBarcode("565578437802")
+ .forHolding(holdingsInFloatingLocation.getId()));
+
+ final IndividualResource loan = checkOutFixture.checkOutByBarcode(nod, james);
+
+ final CheckInByBarcodeResponse checkInResponse = checkInFixture.checkInByBarcode(
+ new CheckInByBarcodeRequestBuilder().forItem(nod).at(servicePointTwo.getId()));
+
+ JsonObject itemRepresentation = checkInResponse.getItem();
+
+ assertThat("item should be present in response",
+ itemRepresentation, IsNull.notNullValue());
+
+ assertThat("ID should be included for item",
+ itemRepresentation.getString("id"), is(nod.getId()));
+
+ assertThat("barcode should be included for item",
+ itemRepresentation.getString("barcode"), CoreMatchers.is("565578437802"));
+
+ assertThat("item status should be 'Available'",
+ itemRepresentation.getJsonObject("status").getString("name"), CoreMatchers.is("Available"));
+
+ assertThat("available item should not have a destination",
+ itemRepresentation.containsKey("inTransitDestinationServicePointId"),
+ CoreMatchers.is(false));
+
+ assertThat( "The check-in response should display the item's new location.",
+ itemRepresentation.getJsonObject("location").getString("name"), CoreMatchers.is("Floating collection 2"));
+
+ JsonObject staffSlipContext = checkInResponse.getStaffSlipContext();
+
+ assertThat( "The staff slip context should display the item's new location.",
+ staffSlipContext.getJsonObject("item").getString("effectiveLocationSpecific"), CoreMatchers.is("Floating collection 2"));
+
+
+ JsonObject loanRepresentation = checkInResponse.getLoan();
+
+ assertThat("closed loan should be present in response",
+ loanRepresentation, IsNull.notNullValue());
+
+ assertThat("item (in loan) should not have a destination",
+ loanRepresentation.getJsonObject("item")
+ .containsKey("inTransitDestinationServicePointId"), CoreMatchers.is(false));
+
+ JsonObject updatedNod = itemsClient.getById(nod.getId()).getJson();
+
+ assertThat("stored item status should be 'Available'",
+ updatedNod.getJsonObject("status").getString("name"), CoreMatchers.is("Available"));
+
+ assertThat("available item in storage should not have a destination",
+ updatedNod.containsKey("inTransitDestinationServicePointId"), CoreMatchers.is(false));
+
+ assertThat("available item's temporary location set to other floating collection",
+ updatedNod.getString("temporaryLocationId"), CoreMatchers.is(otherFloatingCollection.getId().toString()));
+
+ final JsonObject storedLoan = loansStorageClient.getById(loan.getId()).getJson();
+
+ assertThat("stored loan status should be 'Closed'",
+ storedLoan.getJsonObject("status").getString("name"), CoreMatchers.is("Closed"));
+
+ assertThat("item status snapshot in storage should be 'Available'",
+ storedLoan.getString("itemStatus"), CoreMatchers.is("Available"));
+
+ assertThat("Checkin Service Point Id should be stored",
+ storedLoan.getString("checkinServicePointId"), is(servicePointTwo.getId()));
+
+ }
+
+ @Test
+ void willPutFloatingItemInTransitWhenCheckInServicePointServesNoFloatingCollection() {
+ final IndividualResource floatingCollection = locationsFixture.floatingCollection();
+
+ final IndividualResource servicePointTwo = servicePointsFixture.cd2();
+
+ // Location without floating collection, different service point
+ locationsFixture.createLocation(
+ new LocationBuilder()
+ .withName("Location 2")
+ .forInstitution(UUID.randomUUID())
+ .forCampus(UUID.randomUUID())
+ .forLibrary(UUID.randomUUID())
+ .withCode("FLOAT2")
+ .isFloatingCollection(false)
+ .withPrimaryServicePoint(servicePointTwo.getId()));
+
+ final IndividualResource james = usersFixture.james();
+
+ final IndividualResource holdingsInFloatingLocation =
+ holdingsFixture.createHoldingsRecord(UUID.randomUUID(), floatingCollection.getId());
+
+ IndividualResource nod = itemsClient.create(ItemExamples.basedUponNod(
+ materialTypesFixture.book().getId(),
+ loanTypesFixture.canCirculate().getId())
+ .withBarcode("565578437802")
+ .forHolding(holdingsInFloatingLocation.getId()));
+
+ final IndividualResource loan = checkOutFixture.checkOutByBarcode(nod, james);
+
+ final CheckInByBarcodeResponse checkInResponse = checkInFixture.checkInByBarcode(
+ new CheckInByBarcodeRequestBuilder()
+ .forItem(nod)
+ .at(servicePointTwo.getId()));
+
+ JsonObject itemRepresentation = checkInResponse.getItem();
+
+ System.out.println(itemsClient.get(nod).getJson().encodePrettily());
+
+ assertThat("item should be present in response",
+ itemRepresentation, IsNull.notNullValue());
+
+ assertThat("ID should be included for item",
+ itemRepresentation.getString("id"), is(nod.getId()));
+
+ assertThat("barcode should be included for item",
+ itemRepresentation.getString("barcode"), CoreMatchers.is("565578437802"));
+
+ assertThat("item status should be 'In transit'",
+ itemRepresentation.getJsonObject("status").getString("name"), CoreMatchers.is("In transit"));
+
+ assertThat("item should have a destination",
+ itemRepresentation.containsKey("inTransitDestinationServicePointId"),
+ CoreMatchers.is(true));
+
+ JsonObject loanRepresentation = checkInResponse.getLoan();
+
+ assertThat("closed loan should be present in response",
+ loanRepresentation, IsNull.notNullValue());
+
+ assertThat("item (in loan) should have a destination",
+ loanRepresentation.getJsonObject("item")
+ .containsKey("inTransitDestinationServicePointId"), CoreMatchers.is(true));
+
+ JsonObject updatedNod = itemsClient.getById(nod.getId()).getJson();
+
+ assertThat("stored item status should be 'In transit'",
+ updatedNod.getJsonObject("status").getString("name"), CoreMatchers.is("In transit"));
+
+ assertThat("item in storage should have a destination",
+ updatedNod.containsKey("inTransitDestinationServicePointId"), CoreMatchers.is(true));
+
+ final JsonObject storedLoan = loansStorageClient.getById(loan.getId()).getJson();
+
+ assertThat("stored loan status should be 'Closed'",
+ storedLoan.getJsonObject("status").getString("name"), CoreMatchers.is("Closed"));
+
+ assertThat("item status snapshot in storage should be 'In transit'",
+ storedLoan.getString("itemStatus"), CoreMatchers.is("In transit"));
+
+ assertThat("CheckIn Service Point id should be stored",
+ storedLoan.getString("checkinServicePointId"), is(servicePointTwo.getId()));
+
+ }
+
+ @Test
+ void willPutFloatingItemInTransitWhenHoldRequestWasIssuedAtDifferentServicePoint() {
+ final IndividualResource floatingCollection = locationsFixture.floatingCollection();
+
+ final IndividualResource servicePointTwo = servicePointsFixture.cd3();
+ final IndividualResource pickUpServicePoint = servicePointsFixture.cd2();
+
+
+ // Floating collection (location), different service point
+ locationsFixture.createLocation(
+ new LocationBuilder()
+ .withName("Floating collection 2")
+ .forInstitution(UUID.randomUUID())
+ .forCampus(UUID.randomUUID())
+ .forLibrary(UUID.randomUUID())
+ .withCode("FLOAT2")
+ .isFloatingCollection(true)
+ .withPrimaryServicePoint(servicePointTwo.getId()));
+
+ // Location without floating collection, with pickup service point
+ locationsFixture.createLocation(
+ new LocationBuilder()
+ .withName("Location 3")
+ .forInstitution(UUID.randomUUID())
+ .forCampus(UUID.randomUUID())
+ .forLibrary(UUID.randomUUID())
+ .withCode("LOC3")
+ .isFloatingCollection(false)
+ .withPrimaryServicePoint(pickUpServicePoint.getId()));
+
+
+ final IndividualResource james = usersFixture.james();
+ final IndividualResource jessica = usersFixture.jessica();
+
+ final IndividualResource instance = instancesFixture.basedUponDunkirk();
+ final IndividualResource holdingsInFloatingLocation =
+ holdingsFixture.createHoldingsRecord(UUID.randomUUID(), floatingCollection.getId());
+
+ IndividualResource nod = itemsClient.create(ItemExamples.basedUponNod(
+ materialTypesFixture.book().getId(),
+ loanTypesFixture.canCirculate().getId())
+ .withBarcode("565578437802")
+ .forHolding(holdingsInFloatingLocation.getId()));
+
+ // Check out from floating collection
+ final IndividualResource loan = checkOutFixture.checkOutByBarcode(nod, james);
+
+ requestsFixture.placeItemLevelHoldShelfRequest(
+ new ItemResource(nod, holdingsInFloatingLocation,instance), jessica, ClockUtil.getZonedDateTime(), pickUpServicePoint.getId());
+
+ // Check in at service point serving a floating collection.
+ final CheckInByBarcodeResponse checkInResponse = checkInFixture.checkInByBarcode(
+ new CheckInByBarcodeRequestBuilder()
+ .forItem(nod)
+ .at(servicePointTwo.getId()));
+
+ JsonObject itemRepresentation = checkInResponse.getItem();
+
+ System.out.println(itemsClient.get(nod).getJson().encodePrettily());
+
+ assertThat("item should be present in response",
+ itemRepresentation, IsNull.notNullValue());
+
+ assertThat("ID should be included for item",
+ itemRepresentation.getString("id"), is(nod.getId()));
+
+ assertThat("barcode should be included for item",
+ itemRepresentation.getString("barcode"), CoreMatchers.is("565578437802"));
+
+ assertThat("item status should be 'In transit'",
+ itemRepresentation.getJsonObject("status").getString("name"), CoreMatchers.is("In transit"));
+
+ assertThat("available item should have a destination",
+ itemRepresentation.containsKey("inTransitDestinationServicePointId"),
+ CoreMatchers.is(true));
+
+ assertThat( "The check-in response should display the item's original location.",
+ itemRepresentation.getJsonObject("location").getString("name"), CoreMatchers.is("Floating collection"));
+
+ JsonObject staffSlipContext = checkInResponse.getStaffSlipContext();
+
+ assertThat( "The staff slip context should display the item's original location.",
+ staffSlipContext.getJsonObject("item").getString("effectiveLocationSpecific"), CoreMatchers.is("Floating collection"));
+
+ JsonObject loanRepresentation = checkInResponse.getLoan();
+
+ assertThat("closed loan should be present in response",
+ loanRepresentation, IsNull.notNullValue());
+
+ assertThat("in transit item in storage should have a destination",
+ loanRepresentation.getJsonObject("item")
+ .getString("inTransitDestinationServicePointId"),
+ CoreMatchers.is(pickUpServicePoint.getId().toString()));
+
+ assertThat("item (in loan) should have a destination",
+ loanRepresentation.getJsonObject("item")
+ .containsKey("inTransitDestinationServicePointId"), CoreMatchers.is(true));
+
+ JsonObject updatedNod = itemsClient.getById(nod.getId()).getJson();
+
+ assertThat("stored item status should be 'In transit'",
+ updatedNod.getJsonObject("status").getString("name"), CoreMatchers.is("In transit"));
+
+ assertThat("in transit item in storage should have a destination",
+ updatedNod.getString("inTransitDestinationServicePointId"),
+ is(pickUpServicePoint.getId()));
+
+ final JsonObject storedLoan = loansStorageClient.getById(loan.getId()).getJson();
+
+ assertThat("stored loan status should be 'Closed'",
+ storedLoan.getJsonObject("status").getString("name"), CoreMatchers.is("Closed"));
+
+ assertThat("item status snapshot in storage should be 'In transit'",
+ storedLoan.getString("itemStatus"), CoreMatchers.is("In transit"));
+
+ assertThat("Checkin Service Point Id should be stored",
+ storedLoan.getString("checkinServicePointId"), is(servicePointTwo.getId()));
+
+
+ }
+
+
+ @Test
+ void willPutItemInTransitIfFloatingLocationWasOverriddenByNonFloatingLocation() {
+ // floating collection served by service point cd1.
+ final IndividualResource floatingCollection = locationsFixture.floatingCollection();
+
+ final IndividualResource servicePointTwo = servicePointsFixture.cd3();
+ final IndividualResource pickUpServicePoint = servicePointsFixture.cd2();
+
+ // Floating collection (location), different service point
+ locationsFixture.createLocation(
+ new LocationBuilder()
+ .withName("Floating collection 2")
+ .forInstitution(UUID.randomUUID())
+ .forCampus(UUID.randomUUID())
+ .forLibrary(UUID.randomUUID())
+ .withCode("FLOAT2")
+ .isFloatingCollection(true)
+ .withPrimaryServicePoint(servicePointTwo.getId()));
+
+ // Location without floating collection, with pickup service point
+ IndividualResource location3 = locationsFixture.createLocation(
+ new LocationBuilder()
+ .withName("Location 3")
+ .forInstitution(UUID.randomUUID())
+ .forCampus(UUID.randomUUID())
+ .forLibrary(UUID.randomUUID())
+ .withCode("LOC3")
+ .isFloatingCollection(false)
+ .withPrimaryServicePoint(pickUpServicePoint.getId()));
+
+ final IndividualResource holdingsInFloatingLocation =
+ holdingsFixture.createHoldingsRecord(UUID.randomUUID(), floatingCollection.getId());
+
+ IndividualResource nod = itemsClient.create(ItemExamples.basedUponNod(
+ materialTypesFixture.book().getId(),
+ loanTypesFixture.canCirculate().getId())
+ .withPermanentLocation(location3)
+ .withBarcode("565578437802")
+ .forHolding(holdingsInFloatingLocation.getId()));
+
+ final IndividualResource james = usersFixture.james();
+
+ final IndividualResource loan = checkOutFixture.checkOutByBarcode(nod, james);
+
+ final CheckInByBarcodeResponse checkInResponse = checkInFixture.checkInByBarcode(
+ new CheckInByBarcodeRequestBuilder()
+ .forItem(nod)
+ .at(servicePointTwo.getId()));
+
+ JsonObject itemRepresentation = checkInResponse.getItem();
+
+ System.out.println(itemsClient.get(nod).getJson().encodePrettily());
+
+ assertThat("item should be present in response",
+ itemRepresentation, IsNull.notNullValue());
+
+ assertThat("ID should be included for item",
+ itemRepresentation.getString("id"), is(nod.getId()));
+
+ assertThat("barcode should be included for item",
+ itemRepresentation.getString("barcode"), CoreMatchers.is("565578437802"));
+
+ assertThat("item status should be 'In transit'",
+ itemRepresentation.getJsonObject("status").getString("name"), CoreMatchers.is("In transit"));
+
+ assertThat("available item should have a destination",
+ itemRepresentation.containsKey("inTransitDestinationServicePointId"),
+ CoreMatchers.is(true));
+
+ JsonObject loanRepresentation = checkInResponse.getLoan();
+
+ assertThat("closed loan should be present in response",
+ loanRepresentation, IsNull.notNullValue());
+
+ assertThat("item (in loan) should have a destination",
+ loanRepresentation.getJsonObject("item")
+ .containsKey("inTransitDestinationServicePointId"), CoreMatchers.is(true));
+
+ JsonObject updatedNod = itemsClient.getById(nod.getId()).getJson();
+
+ assertThat("stored item status should be 'In transit'",
+ updatedNod.getJsonObject("status").getString("name"), CoreMatchers.is("In transit"));
+
+ assertThat("in transit item in storage should have a destination",
+ updatedNod.getString("inTransitDestinationServicePointId"),
+ is(pickUpServicePoint.getId()));
+
+ final JsonObject storedLoan = loansStorageClient.getById(loan.getId()).getJson();
+
+ assertThat("stored loan status should be 'Closed'",
+ storedLoan.getJsonObject("status").getString("name"), CoreMatchers.is("Closed"));
+
+ assertThat("item status snapshot in storage should be 'In transit'",
+ storedLoan.getString("itemStatus"), CoreMatchers.is("In transit"));
+
+ assertThat("Checkin Service Point Id should be stored",
+ storedLoan.getString("checkinServicePointId"), is(servicePointTwo.getId()));
+
+ }
+}
diff --git a/src/test/java/api/requests/RequestsAPICreationTests.java b/src/test/java/api/requests/RequestsAPICreationTests.java
index e0ad4032ed..901c9e19ae 100644
--- a/src/test/java/api/requests/RequestsAPICreationTests.java
+++ b/src/test/java/api/requests/RequestsAPICreationTests.java
@@ -687,7 +687,7 @@ void createTitleLevelRequestWhenTlrEnabledSetLocationNoItems() {
assertThat(response.getJson(), hasErrorWith(
hasMessage("Cannot create page TLR for this instance ID - no pageable " +
"available items found in requested location")));
- }
+ }
@Test
void cannotCreateRequestWithNonExistentRequestLevelWhenTlrEnabled() {
diff --git a/src/test/java/api/support/builders/LocationBuilder.java b/src/test/java/api/support/builders/LocationBuilder.java
index c12a2cc9a2..496d8c2f62 100644
--- a/src/test/java/api/support/builders/LocationBuilder.java
+++ b/src/test/java/api/support/builders/LocationBuilder.java
@@ -22,9 +22,10 @@ public class LocationBuilder extends JsonBuilder implements Builder {
private final UUID primaryServicePointId;
private final Set otherServicePointIds;
private final String effectiveLocationPrimaryServicePointName;
+ private final Boolean isFloatingCollection;
public LocationBuilder() {
- this(null, null, null, null, null, null, null, new HashSet<>(), null);
+ this(null, null, null, null, null, null, null, new HashSet<>(), null, false);
}
private LocationBuilder(
@@ -36,7 +37,8 @@ private LocationBuilder(
UUID libraryId,
UUID primaryServicePointId,
Set otherServicePointIds,
- String effectiveLocationPrimaryServicePointName) {
+ String effectiveLocationPrimaryServicePointName,
+ boolean isFloatingCollection) {
if (otherServicePointIds == null) {
otherServicePointIds = new HashSet<>();
@@ -51,6 +53,7 @@ private LocationBuilder(
this.primaryServicePointId = primaryServicePointId;
this.otherServicePointIds = otherServicePointIds;
this.effectiveLocationPrimaryServicePointName = effectiveLocationPrimaryServicePointName;
+ this.isFloatingCollection = isFloatingCollection;
}
@Override
@@ -75,6 +78,7 @@ public JsonObject create() {
write(representation, "servicePointIds", mappedServicePointIds);
write(representation, "effectiveLocationPrimaryServicePointName", effectiveLocationPrimaryServicePointName);
}
+ write(representation, "isFloatingCollection", isFloatingCollection);
return representation;
}
@@ -89,7 +93,8 @@ public LocationBuilder withName(String name) {
this.libraryId,
this.primaryServicePointId,
this.otherServicePointIds,
- this.effectiveLocationPrimaryServicePointName);
+ this.effectiveLocationPrimaryServicePointName,
+ this.isFloatingCollection);
}
public LocationBuilder withCode(String code) {
@@ -102,7 +107,8 @@ public LocationBuilder withCode(String code) {
this.libraryId,
this.primaryServicePointId,
this.otherServicePointIds,
- this.effectiveLocationPrimaryServicePointName);
+ this.effectiveLocationPrimaryServicePointName,
+ this.isFloatingCollection);
}
public LocationBuilder forInstitution(UUID institutionId) {
@@ -115,7 +121,8 @@ public LocationBuilder forInstitution(UUID institutionId) {
this.libraryId,
this.primaryServicePointId,
this.otherServicePointIds,
- this.effectiveLocationPrimaryServicePointName);
+ this.effectiveLocationPrimaryServicePointName,
+ this.isFloatingCollection);
}
public LocationBuilder forCampus(UUID campusId) {
@@ -128,7 +135,8 @@ public LocationBuilder forCampus(UUID campusId) {
this.libraryId,
this.primaryServicePointId,
this.otherServicePointIds,
- this.effectiveLocationPrimaryServicePointName);
+ this.effectiveLocationPrimaryServicePointName,
+ this.isFloatingCollection);
}
public LocationBuilder forLibrary(UUID libraryId) {
@@ -141,7 +149,8 @@ public LocationBuilder forLibrary(UUID libraryId) {
libraryId,
this.primaryServicePointId,
this.otherServicePointIds,
- this.effectiveLocationPrimaryServicePointName);
+ this.effectiveLocationPrimaryServicePointName,
+ this.isFloatingCollection);
}
public LocationBuilder withPrimaryServicePoint(UUID primaryServicePointId) {
@@ -154,7 +163,8 @@ public LocationBuilder withPrimaryServicePoint(UUID primaryServicePointId) {
this.libraryId,
primaryServicePointId,
this.otherServicePointIds,
- this.effectiveLocationPrimaryServicePointName)
+ this.effectiveLocationPrimaryServicePointName,
+ this.isFloatingCollection)
.servedBy(primaryServicePointId);
}
@@ -168,7 +178,8 @@ public LocationBuilder withDiscoveryDisplayName(String discoveryDisplayName) {
this.libraryId,
this.primaryServicePointId,
this.otherServicePointIds,
- this.effectiveLocationPrimaryServicePointName);
+ this.effectiveLocationPrimaryServicePointName,
+ this.isFloatingCollection);
}
public LocationBuilder withEffectiveLocationPrimaryServicePointName(String effectiveLocationPrimaryServicePointName) {
@@ -181,10 +192,26 @@ public LocationBuilder withEffectiveLocationPrimaryServicePointName(String effec
this.libraryId,
primaryServicePointId,
this.otherServicePointIds,
- effectiveLocationPrimaryServicePointName)
+ effectiveLocationPrimaryServicePointName,
+ this.isFloatingCollection)
.servedBy(primaryServicePointId);
}
+ public LocationBuilder isFloatingCollection(boolean isFloatingCollection) {
+ return new LocationBuilder(
+ this.name,
+ this.code,
+ this.discoveryDisplayName,
+ this.institutionId,
+ this.campusId,
+ this.libraryId,
+ primaryServicePointId,
+ this.otherServicePointIds,
+ this.effectiveLocationPrimaryServicePointName,
+ isFloatingCollection
+ );
+ }
+
public LocationBuilder servedBy(UUID servicePointId) {
final HashSet servicePoints = new HashSet<>(otherServicePointIds);
@@ -207,6 +234,7 @@ public LocationBuilder servedBy(Set servicePoints) {
this.libraryId,
this.primaryServicePointId,
newServicePointIds,
- this.effectiveLocationPrimaryServicePointName);
+ this.effectiveLocationPrimaryServicePointName,
+ this.isFloatingCollection);
}
}
diff --git a/src/test/java/api/support/examples/LocationExamples.java b/src/test/java/api/support/examples/LocationExamples.java
index 28811230d3..1b3b2f2c27 100644
--- a/src/test/java/api/support/examples/LocationExamples.java
+++ b/src/test/java/api/support/examples/LocationExamples.java
@@ -109,4 +109,17 @@ public LocationBuilder fourthFloorLocation() {
.withPrimaryServicePoint(primaryServicePointId)
.servedBy(otherServicePointIds);
}
+
+ public LocationBuilder floatingCollection() {
+ return new LocationBuilder()
+ .withName("Floating collection")
+ .withCode("NU/JC/DL/FC")
+ .withDiscoveryDisplayName("Floating collection")
+ .forInstitution(institutionId)
+ .forCampus(jubileeCampusId)
+ .forLibrary(djanoglyLibraryId)
+ .withPrimaryServicePoint(primaryServicePointId)
+ .isFloatingCollection(true);
+ }
+
}
diff --git a/src/test/java/api/support/fixtures/LocationsFixture.java b/src/test/java/api/support/fixtures/LocationsFixture.java
index 53b947260e..27a2fec2c3 100644
--- a/src/test/java/api/support/fixtures/LocationsFixture.java
+++ b/src/test/java/api/support/fixtures/LocationsFixture.java
@@ -68,6 +68,14 @@ public IndividualResource thirdFloor() {
return locationRecordCreator.createIfAbsent(
locationExamples.thirdFloor());
}
+
+ public IndividualResource floatingCollection() {
+ final LocationExamples locationExamples = getLocationExamples();
+
+ return locationRecordCreator.createIfAbsent(
+ locationExamples.floatingCollection());
+ }
+
public IndividualResource fourthServicePoint() {
final LocationExamples locationExamples = getLocationExamplesForCd4();
diff --git a/src/test/java/org/folio/circulation/domain/InstanceRequestItemsComparerTests.java b/src/test/java/org/folio/circulation/domain/InstanceRequestItemsComparerTests.java
index 81cf284ec4..fc93f354b9 100644
--- a/src/test/java/org/folio/circulation/domain/InstanceRequestItemsComparerTests.java
+++ b/src/test/java/org/folio/circulation/domain/InstanceRequestItemsComparerTests.java
@@ -251,7 +251,7 @@ private static Item createItem(UUID withServicePointId) {
if (withServicePointId != null) {
location = new Location(null, null, null, null,
- List.of(withServicePointId), null,
+ List.of(withServicePointId), null, false,
Institution.unknown(null), Campus.unknown(null),
Library.unknown(null), ServicePoint.unknown(null));
}
diff --git a/src/test/java/org/folio/circulation/domain/LoanCheckInServiceTest.java b/src/test/java/org/folio/circulation/domain/LoanCheckInServiceTest.java
index 87024996d7..be02c84890 100644
--- a/src/test/java/org/folio/circulation/domain/LoanCheckInServiceTest.java
+++ b/src/test/java/org/folio/circulation/domain/LoanCheckInServiceTest.java
@@ -47,7 +47,7 @@ void isInHouseUseWhenNonPrimaryServicePointServesHomeLocation() {
@NonNull UUID homeServicePointId = UUID.randomUUID();
Item item = Item.from(itemRepresentation)
.withLocation(new Location(null, null, null, null,
- List.of(checkInServicePoint), homeServicePointId, Institution.unknown(),
+ List.of(checkInServicePoint), homeServicePointId, false, Institution.unknown(),
Campus.unknown(), Library.unknown(),
ServicePoint.unknown(homeServicePointId.toString())));
@@ -106,7 +106,7 @@ void isNotInHouseUseWhenServicePointDoesNotServeHomeLocation() {
private Location locationPrimarilyServing(@NonNull UUID homeServicePointId) {
return new Location(null, null, null, null,
- List.of(UUID.randomUUID()), homeServicePointId, Institution.unknown(),
+ List.of(UUID.randomUUID()), homeServicePointId, false, Institution.unknown(),
Campus.unknown(), Library.unknown(),
ServicePoint.unknown(homeServicePointId.toString()));
}
diff --git a/src/test/java/org/folio/circulation/domain/OverdueFineServiceTest.java b/src/test/java/org/folio/circulation/domain/OverdueFineServiceTest.java
index 9856d01ea8..5c7a44d997 100644
--- a/src/test/java/org/folio/circulation/domain/OverdueFineServiceTest.java
+++ b/src/test/java/org/folio/circulation/domain/OverdueFineServiceTest.java
@@ -611,7 +611,7 @@ private Item createItem() {
return Item.from(item)
.withLocation(new Location(null, LOCATION_NAME, null, null, emptyList(),
- SERVICE_POINT_ID, Institution.unknown(), Campus.unknown(), Library.unknown(),
+ SERVICE_POINT_ID, false, Institution.unknown(), Campus.unknown(), Library.unknown(),
ServicePoint.unknown()))
.withInstance(new Instance(UUID.randomUUID().toString(), TITLE, emptyList(), contributors, emptyList(), emptyList()))
.withMaterialType(new MaterialType(ITEM_MATERIAL_TYPE_ID.toString(), ITEM_MATERIAL_TYPE_NAME, null));
diff --git a/src/test/java/org/folio/circulation/infrastructure/storage/inventory/ItemRepositoryTests.java b/src/test/java/org/folio/circulation/infrastructure/storage/inventory/ItemRepositoryTests.java
index ce6afc117a..f32c4306b4 100644
--- a/src/test/java/org/folio/circulation/infrastructure/storage/inventory/ItemRepositoryTests.java
+++ b/src/test/java/org/folio/circulation/infrastructure/storage/inventory/ItemRepositoryTests.java
@@ -167,7 +167,8 @@ private ItemRepository createRepository(CollectionResourceClient itemsClient, Co
}
private Item dummyItem() {
- return new Item(null, null, null, null, null, null, null, null, false,
+ return new Item(null, null, null, null, null, null, null, null,
+ null, false,
Holdings.unknown(), Instance.unknown(), MaterialType.unknown(),
LoanType.unknown(), ItemDescription.unknown());
}
diff --git a/src/test/java/org/folio/circulation/rules/Text2DroolsTest.java b/src/test/java/org/folio/circulation/rules/Text2DroolsTest.java
index 9e5512ab8e..f9ade91b9c 100644
--- a/src/test/java/org/folio/circulation/rules/Text2DroolsTest.java
+++ b/src/test/java/org/folio/circulation/rules/Text2DroolsTest.java
@@ -688,6 +688,7 @@ private MultiMap params(String itId, String ltId, String ptId, String lId) {
private Location createLocation(String institutionId, String libraryId, String campusId) {
return new Location(null, null, null, null, emptyList(), null,
+ false,
Institution.unknown(institutionId), Campus.unknown(campusId), Library.unknown(libraryId),
ServicePoint.unknown());
}
diff --git a/src/test/java/org/folio/circulation/services/FeeFineFacadeTest.java b/src/test/java/org/folio/circulation/services/FeeFineFacadeTest.java
index edb63950aa..d9c19a77af 100644
--- a/src/test/java/org/folio/circulation/services/FeeFineFacadeTest.java
+++ b/src/test/java/org/folio/circulation/services/FeeFineFacadeTest.java
@@ -124,7 +124,7 @@ void shouldForwardFailureIfAnAccountIsNotRefunded() throws Exception {
private CreateAccountCommand.CreateAccountCommandBuilder createCommandBuilder() {
final Item item = Item.from(new JsonObject())
.withLocation(new Location(null, "Main library", null, null, emptyList(),
- null, Institution.unknown(), Campus.unknown(),
+ null, false, Institution.unknown(), Campus.unknown(),
Library.unknown(), ServicePoint.unknown()));
return CreateAccountCommand.builder()