diff --git a/NEWS.md b/NEWS.md index 3166249a92..20ba9c5e7d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,6 @@ +## 24.5.0-SNAPSHOT 2025-xx-xx +* Replace deprecated mod-configuration with mod-settings for fetching tenant's locale settings ([CIRC-2295](https://folio-org.atlassian.net/browse/CIRC-2295)) + ## 24.4.0 2025-03-12 * Patron notices for the trigger “Item recalled” not sent if the item is not 1st in the title request queue (CIRC-2168) * Fix automated patron blocks permission issue (CIRC-2185) diff --git a/README.md b/README.md index 9894420c9f..a51bd4f922 100644 --- a/README.md +++ b/README.md @@ -265,17 +265,17 @@ Below is a short summary summary of most of the validation checks performed when Each includes an example of the error message provided and the parameter key included with the error. -|Check|Example Message|Parameter Key|Notes| -|---|---|---|---| -|Item does not exist|No item with barcode 036000291452 exists|itemBarcode| | -|Holding does not exist| | |otherwise it is not possible to lookup circulation rules| -|Item is already checked out|Item is already checked out|itemBarcode| | -|Existing open loan for item|Cannot check out item that already has an open loan|itemBarcode| | -|Proxy relationship is valid|Cannot check out item via proxy when relationship is invalid| |only if proxying| -|User must be requesting user|User checking out must be requester awaiting pickup|userBarcode|if there is an outstanding fulfillable request for item| -|User does not exist|Could not find user with matching barcode|userBarcode| | -|User needs to be active and not expired|Cannot check out to inactive user|userBarcode| | -|Proxy user needs to be active and not expired|Cannot check out via inactive proxying user|proxyUserBarcode|only if proxying| +| Check | Example Message | Parameter Key | Notes | +|-----------------------------------------------|--------------------------------------------------------------|------------------|----------------------------------------------------------| +| Item does not exist | No item with barcode 036000291452 exists | itemBarcode | | +| Holding does not exist | | | otherwise it is not possible to lookup circulation rules | +| Item is already checked out | Item is already checked out | itemBarcode | | +| Existing open loan for item | Cannot check out item that already has an open loan | itemBarcode | | +| Proxy relationship is valid | Cannot check out item via proxy when relationship is invalid | | only if proxying | +| User must be requesting user | User checking out must be requester awaiting pickup | userBarcode | if there is an outstanding fulfillable request for item | +| User does not exist | Could not find user with matching barcode | userBarcode | | +| User needs to be active and not expired | Cannot check out to inactive user | userBarcode | | +| Proxy user needs to be active and not expired | Cannot check out via inactive proxying user | proxyUserBarcode | only if proxying | ### Renew By Barcode @@ -404,19 +404,19 @@ based on the patron's patron group and the item's material type, loan type, and During the circulation process an item can change between a variety of states, below is a table describing the most common states defined at the moment. -| Name | Description | -|---|---| -| Available | This item is available to be lent to a patron | -| Checked out | This item is currently checked out to a patron | -| Awaiting pickup | This item is awaiting pickup by a patron who has a request at the top of the queue| +| Name | Description | +|-----------------|------------------------------------------------------------------------------------| +| Available | This item is available to be lent to a patron | +| Checked out | This item is currently checked out to a patron | +| Awaiting pickup | This item is awaiting pickup by a patron who has a request at the top of the queue | ### Request Status -| Name | Description | -|---|---| -| Open - Not yet filled | The requested item is not yet available to the requesting user | -| Open - Awaiting pickup | The item is available to the requesting user | -| Closed - Filled | | +| Name | Description | +|------------------------|----------------------------------------------------------------| +| Open - Not yet filled | The requested item is not yet available to the requesting user | +| Open - Awaiting pickup | The item is available to the requesting user | +| Closed - Filled | | ### Storing Information from Other Records @@ -530,7 +530,7 @@ content-length: 230 } ``` ### Configuration setting for CheckoutLock Feature - To enable this feature for a tenant, we need to add the below configuration in mod-settings. See https://issues.folio.org/browse/UXPROD-3515 to know more about this feature. + To enable this feature for a tenant, we need to add the below configuration in mod-settings. See [UXPROD-3515](https://issues.folio.org/browse/UXPROD-3515) to know more about this feature. #### Permissions To make a post call to mod-settings, user should have below permissions. @@ -551,19 +551,19 @@ POST https://{okapi-location}/settings/entries ``` | parameter | Type | Description | -|---------|-------------|-------------------------------------------------------------------------------------------------------| -| `id` | UUID | id should be provided of type UUID. | -| `scope` | String | Scope should be the module name. Here, it will be "mod-circulation" | -| `key` | String | Key should be feature name which we are enabling the settings. Here, it will be "checkoutLockFeature" | -| `value` | Json Object | Settings for checkout lock feature | +|-----------|-------------|-------------------------------------------------------------------------------------------------------| +| `id` | UUID | id should be provided of type UUID. | +| `scope` | String | Scope should be the module name. Here, it will be "mod-circulation" | +| `key` | String | Key should be feature name which we are enabling the settings. Here, it will be "checkoutLockFeature" | +| `value` | Json Object | Settings for checkout lock feature | | Value options | Type | Description | |------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `checkOutLockFeatureEnabled` | boolean | Indicates whether or not to enable this feature for the tenant. Default value is false(disabled). | | `noOfRetryAttempts` | int | The maximum number of times to retry the lock ackquiring process during checkout. Once the retry is exhausted, system will return error. Default value is 30. | -| `retryInterval` | int | The amount of time to wait between retries in milliseconds. Default value is 250. | -| `lockTtl` | int | Maximum amount of time(milliseconds) that the lock should exist for a patron. After this time, the lock will gets deleted and lock will be provided for another request. Default value is 3000 | +| `retryInterval` | int | The amount of time to wait between retries in milliseconds. Default value is 250. | +| `lockTtl` | int | Maximum amount of time(milliseconds) that the lock should exist for a patron. After this time, the lock will gets deleted and lock will be provided for another request. Default value is 3000 | ## Additional Information diff --git a/src/main/java/org/folio/circulation/domain/ConfigurationService.java b/src/main/java/org/folio/circulation/domain/ConfigurationService.java index 5a65305f57..63cbe9c280 100644 --- a/src/main/java/org/folio/circulation/domain/ConfigurationService.java +++ b/src/main/java/org/folio/circulation/domain/ConfigurationService.java @@ -1,16 +1,10 @@ package org.folio.circulation.domain; -import static org.folio.circulation.domain.MultipleRecords.from; - import java.lang.invoke.MethodHandles; -import java.time.ZoneId; -import java.time.ZoneOffset; import java.util.Collection; - import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import io.vertx.core.json.JsonObject; public class ConfigurationService { @@ -18,30 +12,9 @@ public class ConfigurationService { private static final int DEFAULT_SCHEDULED_NOTICES_PROCESSING_LIMIT = 100; private static final int DEFAULT_CHECKOUT_TIMEOUT_DURATION_IN_MINUTES = 3; - private static final ZoneId DEFAULT_DATE_TIME_ZONE = ZoneOffset.UTC; - private static final String TIMEZONE_KEY = "timezone"; - private static final String RECORDS_NAME = "configs"; private static final String CHECKOUT_TIMEOUT_DURATION_KEY = "checkoutTimeoutDuration"; private static final String CHECKOUT_TIMEOUT_KEY = "checkoutTimeout"; - ZoneId findDateTimeZone(JsonObject representation) { - return from(representation, Configuration::new, RECORDS_NAME) - .map(MultipleRecords::getRecords) - .map(this::findDateTimeZone) - .orElse(DEFAULT_DATE_TIME_ZONE); - } - - public ZoneId findDateTimeZone(Collection configurations) { - final ZoneId chosenTimeZone = configurations.stream() - .map(this::applyTimeZone) - .findFirst() - .orElse(DEFAULT_DATE_TIME_ZONE); - - log.debug("findDateTimeZone:: timezone={}", chosenTimeZone); - - return chosenTimeZone; - } - public Integer findSchedulerNoticesLimit(Collection configurations) { final Integer noticesLimit = configurations.stream() .map(this::applySchedulerNoticesLimit) @@ -94,18 +67,4 @@ private Integer applySchedulerNoticesLimit(Configuration config) { ? Integer.valueOf(value) : DEFAULT_SCHEDULED_NOTICES_PROCESSING_LIMIT; } - - private ZoneId applyTimeZone(Configuration config) { - String value = config.getValue(); - return StringUtils.isBlank(value) - ? DEFAULT_DATE_TIME_ZONE - : parseDateTimeZone(value); - } - - private ZoneId parseDateTimeZone(String value) { - String timezone = new JsonObject(value).getString(TIMEZONE_KEY); - return StringUtils.isBlank(timezone) - ? DEFAULT_DATE_TIME_ZONE - : ZoneId.of(timezone); - } } diff --git a/src/main/java/org/folio/circulation/domain/CreateRequestService.java b/src/main/java/org/folio/circulation/domain/CreateRequestService.java index 9dc5e349cf..b010e06e07 100644 --- a/src/main/java/org/folio/circulation/domain/CreateRequestService.java +++ b/src/main/java/org/folio/circulation/domain/CreateRequestService.java @@ -83,7 +83,7 @@ public CompletableFuture> createRequest( log.debug("createRequest:: parameters requestAndRelatedRecords: {}", () -> requestAndRelatedRecords); final var requestRepository = repositories.getRequestRepository(); - final var configurationRepository = repositories.getConfigurationRepository(); + final var settingsRepository = repositories.getSettingsRepository(); final var automatedBlocksValidator = requestBlockValidators.getAutomatedPatronBlocksValidator(); final var manualBlocksValidator = requestBlockValidators.getManualPatronBlocksValidator(); @@ -103,7 +103,7 @@ public CompletableFuture> createRequest( .thenComposeAsync(r -> r.after(when(this::shouldCheckItem, this::checkItem, this::doNothing))) .thenComposeAsync(r -> r.after(this::checkPolicy)) .thenApply(r -> r.next(this::refuseHoldOrRecallTlrWhenPageableItemExists)) - .thenComposeAsync(r -> r.combineAfter(configurationRepository::findTimeZoneConfiguration, + .thenComposeAsync(r -> r.combineAfter(settingsRepository::lookupTimeZoneSettings, RequestAndRelatedRecords::withTimeZone)) .thenApply(r -> r.next(errorHandler::failWithValidationErrors)) .thenComposeAsync(r -> r.after(updateUponRequest.updateItem::onRequestCreateOrUpdate)) diff --git a/src/main/java/org/folio/circulation/domain/Instance.java b/src/main/java/org/folio/circulation/domain/Instance.java index 28d20c0aa4..557d9666e9 100644 --- a/src/main/java/org/folio/circulation/domain/Instance.java +++ b/src/main/java/org/folio/circulation/domain/Instance.java @@ -13,7 +13,7 @@ @ToString(onlyExplicitlyIncluded = true) public class Instance { public static Instance unknown() { - return new Instance(null, null,null, emptyList(), emptyList(), emptyList(), emptyList(), emptyList()); + return new Instance(null, null,null, emptyList(), emptyList(), emptyList(), emptyList(), emptyList(), emptyList()); } @ToString.Include @@ -27,6 +27,7 @@ public static Instance unknown() { @NonNull Collection publication; @NonNull Collection editions; @NonNull Collection physicalDescriptions; + @NonNull Collection series; public Stream getContributorNames() { return contributors.stream() @@ -41,6 +42,11 @@ public String getPrimaryContributorName() { .orElse(null); } + public Stream getSeriesStatementValues() { + return series.stream() + .map(SeriesStatement::getValue); + } + // TODO: replace this stub with proper implementation public boolean isNotFound() { return id == null; diff --git a/src/main/java/org/folio/circulation/domain/Item.java b/src/main/java/org/folio/circulation/domain/Item.java index 95118a115a..01c6c46d12 100644 --- a/src/main/java/org/folio/circulation/domain/Item.java +++ b/src/main/java/org/folio/circulation/domain/Item.java @@ -288,6 +288,10 @@ public Collection getAdministrativeNotes() { return description.getAdministrativeNotes(); } + public Collection getSeriesStatementValues() { + return instance.getSeriesStatementValues().toList(); + } + private ServicePoint getPrimaryServicePoint() { return location.getPrimaryServicePoint(); } diff --git a/src/main/java/org/folio/circulation/domain/ItemDescription.java b/src/main/java/org/folio/circulation/domain/ItemDescription.java index decea437ef..48772eb15a 100644 --- a/src/main/java/org/folio/circulation/domain/ItemDescription.java +++ b/src/main/java/org/folio/circulation/domain/ItemDescription.java @@ -9,7 +9,7 @@ @Value public class ItemDescription { public static ItemDescription unknown() { - return new ItemDescription(null, null, null, null, null, null, null, null, List.of(), null, List.of()); + return new ItemDescription(null, null, null, null, null, null, null, null, List.of(), null, List.of(), List.of()); } String barcode; @@ -23,4 +23,5 @@ public static ItemDescription unknown() { @NonNull Collection yearCaption; String accessionNumber; @NonNull Collection administrativeNotes; + @NonNull Collection seriesStatements; } diff --git a/src/main/java/org/folio/circulation/domain/MoveRequestService.java b/src/main/java/org/folio/circulation/domain/MoveRequestService.java index d13be99cb9..05a6dc5d46 100644 --- a/src/main/java/org/folio/circulation/domain/MoveRequestService.java +++ b/src/main/java/org/folio/circulation/domain/MoveRequestService.java @@ -9,7 +9,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.folio.circulation.domain.validation.RequestLoanValidator; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.requests.RequestPolicyRepository; import org.folio.circulation.infrastructure.storage.requests.RequestQueueRepository; @@ -25,7 +24,6 @@ public class MoveRequestService { private final MoveRequestProcessAdapter moveRequestProcessAdapter; private final RequestLoanValidator requestLoanValidator; private final RequestNoticeSender requestNoticeSender; - private final ConfigurationRepository configurationRepository; private final EventPublisher eventPublisher; private final RequestQueueRepository requestQueueRepository; private final SettingsRepository settingsRepository; @@ -34,8 +32,8 @@ public class MoveRequestService { public MoveRequestService(RequestRepository requestRepository, RequestPolicyRepository requestPolicyRepository, UpdateUponRequest updateUponRequest, MoveRequestProcessAdapter moveRequestHelper, RequestLoanValidator requestLoanValidator, RequestNoticeSender requestNoticeSender, - ConfigurationRepository configurationRepository, EventPublisher eventPublisher, - RequestQueueRepository requestQueueRepository, SettingsRepository settingsRepository) { + EventPublisher eventPublisher, RequestQueueRepository requestQueueRepository, + SettingsRepository settingsRepository) { this.requestRepository = requestRepository; this.requestPolicyRepository = requestPolicyRepository; @@ -43,7 +41,6 @@ public MoveRequestService(RequestRepository requestRepository, RequestPolicyRepo this.moveRequestProcessAdapter = moveRequestHelper; this.requestLoanValidator = requestLoanValidator; this.requestNoticeSender = requestNoticeSender; - this.configurationRepository = configurationRepository; this.eventPublisher = eventPublisher; this.requestQueueRepository = requestQueueRepository; this.settingsRepository = settingsRepository; @@ -61,7 +58,7 @@ public CompletableFuture> moveRequest( .thenComposeAsync(r -> r.after(requestQueueRepository::get)) .thenApply(r -> r.map(this::pagedRequestIfDestinationItemAvailable)) .thenCompose(r -> r.after(this::validateUpdateRequest)) - .thenComposeAsync(r -> r.combineAfter(configurationRepository::findTimeZoneConfiguration, + .thenComposeAsync(r -> r.combineAfter(settingsRepository::lookupTimeZoneSettings, RequestAndRelatedRecords::withTimeZone)) .thenCompose(r -> r.after(updateUponRequest.updateRequestQueue::onMovedTo)) .thenComposeAsync(r -> r.after(this::updateRelatedObjects)) diff --git a/src/main/java/org/folio/circulation/domain/SeriesStatement.java b/src/main/java/org/folio/circulation/domain/SeriesStatement.java new file mode 100644 index 0000000000..aeca4222b4 --- /dev/null +++ b/src/main/java/org/folio/circulation/domain/SeriesStatement.java @@ -0,0 +1,9 @@ +package org.folio.circulation.domain; + +import lombok.Value; + +@Value +public class SeriesStatement { + String authorityId; + String value; +} diff --git a/src/main/java/org/folio/circulation/domain/UpdateRequestQueue.java b/src/main/java/org/folio/circulation/domain/UpdateRequestQueue.java index c85ea50747..c283208b4b 100644 --- a/src/main/java/org/folio/circulation/domain/UpdateRequestQueue.java +++ b/src/main/java/org/folio/circulation/domain/UpdateRequestQueue.java @@ -14,15 +14,14 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.folio.circulation.domain.policy.ExpirationDateManagement; import org.folio.circulation.domain.policy.library.ClosedLibraryStrategy; import org.folio.circulation.infrastructure.storage.CalendarRepository; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.ServicePointRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.requests.RequestQueueRepository; import org.folio.circulation.infrastructure.storage.requests.RequestRepository; import org.folio.circulation.resources.context.ReorderRequestContext; @@ -36,7 +35,7 @@ public class UpdateRequestQueue { private final RequestQueueRepository requestQueueRepository; private final RequestRepository requestRepository; private final ServicePointRepository servicePointRepository; - private final ConfigurationRepository configurationRepository; + private final SettingsRepository settingsRepository; private final RequestQueueService requestQueueService; private final CalendarRepository calendarRepository; private static final String NOT_DEFINED_INTERVAL = ""; @@ -45,14 +44,14 @@ public UpdateRequestQueue( RequestQueueRepository requestQueueRepository, RequestRepository requestRepository, ServicePointRepository servicePointRepository, - ConfigurationRepository configurationRepository, + SettingsRepository settingsRepository, RequestQueueService requestQueueService, CalendarRepository calendarRepository) { this.requestQueueRepository = requestQueueRepository; this.requestRepository = requestRepository; this.servicePointRepository = servicePointRepository; - this.configurationRepository = configurationRepository; + this.settingsRepository = settingsRepository; this.requestQueueService = requestQueueService; this.calendarRepository = calendarRepository; } @@ -62,7 +61,7 @@ public static UpdateRequestQueue using(Clients clients, RequestQueueRepository requestQueueRepository) { return new UpdateRequestQueue(requestQueueRepository, - requestRepository, new ServicePointRepository(clients), new ConfigurationRepository(clients), + requestRepository, new ServicePointRepository(clients), new SettingsRepository(clients), RequestQueueService.using(clients), new CalendarRepository(clients)); } @@ -163,7 +162,7 @@ private CompletableFuture> awaitPickup(Request request, String pickupServicePointId = request.getPickupServicePointId(); return servicePointRepository.getServicePointById(pickupServicePointId) - .thenCombineAsync(configurationRepository.findTimeZoneConfiguration(), + .thenCombineAsync(settingsRepository.lookupTimeZoneSettings(), Result.combined((servicePoint, tenantTimeZone) -> populateHoldShelfExpirationDate( request.withPickupServicePoint(servicePoint), @@ -394,7 +393,7 @@ private RequestQueue orderQueueByRequestPosition(RequestQueue queue) { List requests = queue.getRequests() .stream() .sorted(comparingInt(Request::getPosition)) - .collect(Collectors.toList()); + .toList(); return new RequestQueue(requests); } diff --git a/src/main/java/org/folio/circulation/domain/mapper/InventoryMapper.java b/src/main/java/org/folio/circulation/domain/mapper/InventoryMapper.java index d1925c647e..e0ff10994c 100644 --- a/src/main/java/org/folio/circulation/domain/mapper/InventoryMapper.java +++ b/src/main/java/org/folio/circulation/domain/mapper/InventoryMapper.java @@ -23,6 +23,7 @@ public static JsonObject createItemContext(Item item) { String yearCaptionsToken = String.join("; ", item.getYearCaption()); String copyNumber = item.getCopyNumber() != null ? item.getCopyNumber() : ""; String administrativeNotes = String.join("; ", item.getAdministrativeNotes()); + String series = String.join("; ", item.getSeriesStatementValues()); JsonObject itemContext = createInstanceContext(item.getInstance(), item) .put("barcode", item.getBarcode()) @@ -38,7 +39,8 @@ public static JsonObject createItemContext(Item item) { .put("displaySummary", item.getDisplaySummary()) .put("descriptionOfPieces", item.getDescriptionOfPieces()) .put("accessionNumber", item.getAccessionNumber()) - .put("administrativeNotes", administrativeNotes); + .put("administrativeNotes", administrativeNotes) + .put("seriesStatements", series); var location = (item.canFloatThroughCheckInServicePoint() && item.isInStatus(ItemStatus.AVAILABLE)) ? item.getFloatDestinationLocation() : item.getLocation(); @@ -88,6 +90,7 @@ public static JsonObject createInstanceContext(Instance instance, Item item) { .put("datesOfPublication", instance.getPublication().stream(). map(Publication::getDateOfPublication).collect(joining("; "))) .put("editions", String.join("; ", instance.getEditions())) + .put("seriesStatements", instance.getSeriesStatementValues().collect(joining("; '"))) .put("physicalDescriptions", String.join("; ", instance.getPhysicalDescriptions())); } } diff --git a/src/main/java/org/folio/circulation/domain/notice/schedule/ScheduledDigitalReminderHandler.java b/src/main/java/org/folio/circulation/domain/notice/schedule/ScheduledDigitalReminderHandler.java index 526478d26e..d28d249b0c 100644 --- a/src/main/java/org/folio/circulation/domain/notice/schedule/ScheduledDigitalReminderHandler.java +++ b/src/main/java/org/folio/circulation/domain/notice/schedule/ScheduledDigitalReminderHandler.java @@ -10,7 +10,7 @@ import org.folio.circulation.domain.LoanAction; import org.folio.circulation.domain.policy.RemindersPolicy; import org.folio.circulation.infrastructure.storage.CalendarRepository; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.feesandfines.FeeFineOwnerRepository; import org.folio.circulation.infrastructure.storage.loans.LoanPolicyRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; @@ -62,7 +62,7 @@ public class ScheduledDigitalReminderHandler extends LoanScheduledNoticeHandler private final CollectionResourceClient accountsStorageClient; private final CollectionResourceClient feeFineActionsStorageClient; - private final ConfigurationRepository configurationRepository; + private final SettingsRepository settingsRepository; static final String ACCOUNT_FEE_FINE_ID_VALUE = "6b830703-f828-4e38-a0bb-ee81deacbd03"; @@ -74,7 +74,7 @@ public class ScheduledDigitalReminderHandler extends LoanScheduledNoticeHandler public ScheduledDigitalReminderHandler(Clients clients, LoanRepository loanRepository) { super(clients, loanRepository); - configurationRepository = new ConfigurationRepository(clients); + this.settingsRepository = new SettingsRepository(clients); this.systemTime = ClockUtil.getZonedDateTime(); this.loanPolicyRepository = new LoanPolicyRepository(clients); this.calendarRepository = new CalendarRepository(clients); @@ -135,8 +135,7 @@ private CompletableFuture> isOpenDay(ScheduledNoticeContext noti } private CompletableFuture getSystemTimeInTenantsZone() { - return configurationRepository - .findTimeZoneConfiguration() + return settingsRepository.lookupTimeZoneSettings() .thenApply(tenantTimeZone -> systemTime.withZoneSameInstant(tenantTimeZone.value())); } @@ -258,7 +257,7 @@ protected CompletableFuture> findNextRuntimeAndBuildNoti log.debug("buildNextNotice:: parameters notice context: {}, reminder config: {}", context, nextReminder); - return configurationRepository.findTimeZoneConfiguration() + return settingsRepository.lookupTimeZoneSettings() .thenCompose(tenantTimeZone -> nextReminder.nextNoticeDueOn(systemTime, tenantTimeZone.value(), context.getLoan().getCheckoutServicePointId(), calendarRepository)) .thenApply(r -> r.map(nextRunTime -> buildNextNotice(context, nextReminder, nextRunTime))); 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 9c5a3205e3..b20f8eb9d3 100644 --- a/src/main/java/org/folio/circulation/domain/representations/ItemSummaryRepresentation.java +++ b/src/main/java/org/folio/circulation/domain/representations/ItemSummaryRepresentation.java @@ -46,6 +46,7 @@ public JsonObject createItemSummary(Item item) { write(itemSummary, "volume", item.getVolume()); write(itemSummary, "copyNumber", item.getCopyNumber()); write(itemSummary, "editions", item.getEditions()); + write(itemSummary, "seriesStatements", item.getSeriesStatementValues()); write(itemSummary, "datesOfPublication", item.getDatesOfPublication()); write(itemSummary, "physicalDescriptions", item.getPhysicalDescriptions()); write(itemSummary, "administrativeNotes", item.getAdministrativeNotes()); diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/CalendarRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/CalendarRepository.java index 739a7386f7..86c1519429 100644 --- a/src/main/java/org/folio/circulation/infrastructure/storage/CalendarRepository.java +++ b/src/main/java/org/folio/circulation/infrastructure/storage/CalendarRepository.java @@ -39,11 +39,11 @@ public class CalendarRepository { "%s/all-openings?startDate=%s&endDate=%s&includeClosed=false&limit=%d"; private final CollectionResourceClient calendarClient; - private final ConfigurationRepository configurationRepository; + private final SettingsRepository settingsRepository; public CalendarRepository(Clients clients) { this.calendarClient = clients.calendarStorageClient(); - this.configurationRepository = new ConfigurationRepository(clients); + this.settingsRepository = new SettingsRepository(clients); } public CompletableFuture> lookupOpeningDays( @@ -75,7 +75,7 @@ public CompletableFuture>> fetchOpeningDaysBetween return calendarClient.get(path) .thenCombineAsync( - configurationRepository.findTimeZoneConfiguration(), + settingsRepository.lookupTimeZoneSettings(), Result.combined(CalendarRepository::getOpeningDaysFromOpeningDayCollection) ); } diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/ConfigurationRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/ConfigurationRepository.java index 79a240bc05..577ca69954 100644 --- a/src/main/java/org/folio/circulation/infrastructure/storage/ConfigurationRepository.java +++ b/src/main/java/org/folio/circulation/infrastructure/storage/ConfigurationRepository.java @@ -3,7 +3,6 @@ import static org.folio.circulation.domain.MultipleRecords.from; import static org.folio.circulation.support.http.client.CqlQuery.exactMatch; -import java.time.ZoneId; import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.function.Function; @@ -92,13 +91,6 @@ private static LoanAnonymizationConfiguration getFirstConfiguration( return LoanAnonymizationConfiguration.from(period); } - public CompletableFuture> findTimeZoneConfiguration() { - Result cqlQueryResult = defineModuleNameAndConfigNameFilter( - "ORG", "localeSettings"); - - return lookupConfigurations(cqlQueryResult, applySearchDateTimeZone()); - } - private CompletableFuture> lookupConfigurations( Result cqlQueryResult, Function, T> searchStrategy) { @@ -119,11 +111,6 @@ private Result defineModuleNameAndConfigNameFilter(String moduleName, return moduleQuery.combine(configNameQuery, CqlQuery::and); } - private Function, ZoneId> applySearchDateTimeZone() { - return configurations -> new ConfigurationService() - .findDateTimeZone(configurations.getRecords()); - } - private Function, Integer> applySearchSchedulerNoticesLimit() { return configurations -> new ConfigurationService() .findSchedulerNoticesLimit(configurations.getRecords()); diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/SettingsRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/SettingsRepository.java index cd738cd94b..747fa0c854 100644 --- a/src/main/java/org/folio/circulation/infrastructure/storage/SettingsRepository.java +++ b/src/main/java/org/folio/circulation/infrastructure/storage/SettingsRepository.java @@ -1,6 +1,7 @@ package org.folio.circulation.infrastructure.storage; import io.vertx.core.json.JsonObject; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.folio.circulation.domain.Configuration; @@ -14,6 +15,8 @@ import org.folio.circulation.support.results.Result; import java.lang.invoke.MethodHandles; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.util.Collection; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -25,6 +28,11 @@ import static org.folio.circulation.support.results.Result.succeeded; public class SettingsRepository { + private static final ZoneId DEFAULT_DATE_TIME_ZONE = ZoneOffset.UTC; + private static final String TIMEZONE_KEY = "timezone"; + private static final String TIMEZONE_SETTINGS_SCOPE = "stripes-core.prefs.manage"; + private static final String TIMEZONE_SETTINGS_KEY = "tenantLocaleSettings"; + private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); private final GetManyRecordsClient settingsClient; private final ConfigurationRepository configurationRepository; @@ -61,6 +69,33 @@ public CompletableFuture> lookupTlrSettings() { .thenCompose(r -> r.after(this::buildTlrSettings)); } + public CompletableFuture> lookupTimeZoneSettings() { + log.info("lookupTimeZoneSettings:: fetching timezone settings"); + return fetchSettings(TIMEZONE_SETTINGS_SCOPE, TIMEZONE_SETTINGS_KEY) + .thenApply(r -> r.map(records -> records.mapRecords(Configuration::new))) + .thenApply(r -> r.map(r1 -> r1.getRecords().stream().findFirst() + .map(this::applyTimeZone) + .orElse(DEFAULT_DATE_TIME_ZONE))) + .thenApply(r -> r.mapFailure(failure -> { + log.warn("lookupTimeZoneSettings:: Error while fetching timezone settings {}", failure); + return succeeded(DEFAULT_DATE_TIME_ZONE); + })); + } + + private ZoneId applyTimeZone(Configuration config) { + String value = config.getValue(); + return StringUtils.isBlank(value) + ? DEFAULT_DATE_TIME_ZONE + : parseDateTimeZone(value); + } + + private ZoneId parseDateTimeZone(String value) { + String timezone = new JsonObject(value).getString(TIMEZONE_KEY); + return StringUtils.isBlank(timezone) + ? DEFAULT_DATE_TIME_ZONE + : ZoneId.of(timezone); + } + private CompletableFuture>> fetchSettings(String scope, String key) { return fetchSettings(scope, List.of(key)); } diff --git a/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java b/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java index f55c6eabe1..2c82a88d58 100644 --- a/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java +++ b/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java @@ -16,7 +16,6 @@ import org.folio.circulation.domain.representations.CheckInByBarcodeRequest; import org.folio.circulation.domain.representations.CheckInByBarcodeResponse; import org.folio.circulation.domain.validation.CheckInValidators; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; @@ -78,8 +77,6 @@ private void checkIn(RoutingContext routingContext) { PatronActionSessionRepository.using(clients, loanRepository, userRepository)); final RequestNoticeSender requestNoticeSender = RequestNoticeSender.using(clients); - - final ConfigurationRepository configurationRepository = new ConfigurationRepository(clients); final SettingsRepository settingsRepository = new SettingsRepository(clients); refuseWhenLoggedInUserNotPresent(context) @@ -92,7 +89,7 @@ private void checkIn(RoutingContext routingContext) { .thenApply(checkInValidators::refuseWhenClaimedReturnedIsNotResolved) .thenComposeAsync(r -> r.combineAfter(settingsRepository::lookupTlrSettings, CheckInContext::withTlrSettings)) - .thenComposeAsync(r -> r.combineAfter(configurationRepository::findTimeZoneConfiguration, + .thenComposeAsync(r -> r.combineAfter(settingsRepository::lookupTimeZoneSettings, CheckInContext::withTimeZone)) .thenComposeAsync(findItemResult -> findItemResult.combineAfter( processAdapter::getRequestQueue, CheckInContext::withRequestQueue)) diff --git a/src/main/java/org/folio/circulation/resources/CheckOutByBarcodeResource.java b/src/main/java/org/folio/circulation/resources/CheckOutByBarcodeResource.java index 2467dc4a1d..a49754c588 100644 --- a/src/main/java/org/folio/circulation/resources/CheckOutByBarcodeResource.java +++ b/src/main/java/org/folio/circulation/resources/CheckOutByBarcodeResource.java @@ -38,7 +38,6 @@ import org.folio.circulation.domain.representations.CheckOutByBarcodeRequest; import org.folio.circulation.domain.validation.CheckOutValidators; import org.folio.circulation.infrastructure.storage.CheckOutLockRepository; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; import org.folio.circulation.infrastructure.storage.loans.LoanPolicyRepository; @@ -120,7 +119,6 @@ CompletableFuture> checkOut(CheckOutByBarcodeReque final var lostItemPolicyRepository = new LostItemPolicyRepository(clients); final var patronNoticePolicyRepository = new PatronNoticePolicyRepository(clients); final var patronGroupRepository = new PatronGroupRepository(clients); - final var configurationRepository = new ConfigurationRepository(clients); final var scheduledNoticesRepository = ScheduledNoticesRepository.using(clients); final var scheduledNoticeService = new LoanScheduledNoticeService(scheduledNoticesRepository, patronNoticePolicyRepository); @@ -164,7 +162,7 @@ CompletableFuture> checkOut(CheckOutByBarcodeReque .thenComposeAsync(validators::refuseWhenItemLimitIsReached) .thenCompose(validators::refuseWhenItemIsNotLoanable) .thenApply(r -> r.next(errorHandler::failWithValidationErrors)) - .thenCompose(r -> r.combineAfter(configurationRepository::findTimeZoneConfiguration, + .thenCompose(r -> r.combineAfter(settingsRepository::lookupTimeZoneSettings, LoanAndRelatedRecords::withTimeZone)) .thenComposeAsync(r -> r.after(overdueFinePolicyRepository::lookupOverdueFinePolicy)) .thenComposeAsync(r -> r.after(lostItemPolicyRepository::lookupLostItemPolicy)); diff --git a/src/main/java/org/folio/circulation/resources/FeeFineScheduledNoticeProcessingResource.java b/src/main/java/org/folio/circulation/resources/FeeFineScheduledNoticeProcessingResource.java index 3779e76682..cf84f9941a 100644 --- a/src/main/java/org/folio/circulation/resources/FeeFineScheduledNoticeProcessingResource.java +++ b/src/main/java/org/folio/circulation/resources/FeeFineScheduledNoticeProcessingResource.java @@ -11,7 +11,7 @@ import org.folio.circulation.domain.notice.schedule.FeeFineScheduledNoticeHandler; import org.folio.circulation.domain.notice.schedule.ScheduledNotice; import org.folio.circulation.domain.notice.schedule.TriggeringEvent; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; import org.folio.circulation.infrastructure.storage.notices.ScheduledNoticesRepository; import org.folio.circulation.infrastructure.storage.requests.RequestRepository; @@ -36,7 +36,7 @@ public FeeFineScheduledNoticeProcessingResource(HttpClient client) { @Override protected CompletableFuture>> findNoticesToSend( - ConfigurationRepository configurationRepository, + SettingsRepository settingsRepository, ScheduledNoticesRepository scheduledNoticesRepository, PatronActionSessionRepository patronActionSessionRepository, PageLimit pageLimit) { diff --git a/src/main/java/org/folio/circulation/resources/GroupingScheduledNoticeProcessingResource.java b/src/main/java/org/folio/circulation/resources/GroupingScheduledNoticeProcessingResource.java index f32e303d42..8a394828da 100644 --- a/src/main/java/org/folio/circulation/resources/GroupingScheduledNoticeProcessingResource.java +++ b/src/main/java/org/folio/circulation/resources/GroupingScheduledNoticeProcessingResource.java @@ -27,7 +27,7 @@ import org.folio.circulation.domain.notice.schedule.grouping.ScheduledNoticeGroupDefinition; import org.folio.circulation.domain.notice.schedule.TriggeringEvent; import org.folio.circulation.domain.notice.schedule.grouping.ScheduledNoticeGroupDefinitionFactory; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; import org.folio.circulation.infrastructure.storage.notices.ScheduledNoticesRepository; import org.folio.circulation.infrastructure.storage.requests.RequestRepository; @@ -57,7 +57,7 @@ public abstract class GroupingScheduledNoticeProcessingResource "triggeringEvent", "noticeConfig.format", "noticeConfig.timing") .map(CqlSortClause::ascending) - .collect(toList()) + .toList() ); protected GroupingScheduledNoticeProcessingResource(HttpClient client, String rootPath, @@ -82,19 +82,19 @@ protected abstract GroupedScheduledNoticeHandler getHandler(Clients clients, @Override protected CompletableFuture>> findNoticesToSend( - ConfigurationRepository configurationRepository, + SettingsRepository settingsRepository, ScheduledNoticesRepository scheduledNoticesRepository, PatronActionSessionRepository patronActionSessionRepository, PageLimit pageLimit) { log.debug("findNoticesToSend:: pageLimit: {}", pageLimit.getLimit()); - return getTimeLimit(configurationRepository) + return getTimeLimit(settingsRepository) .thenCompose(r -> r.after(timeLimit -> findNotices(scheduledNoticesRepository, pageLimit, timeLimit))); } private CompletableFuture> getTimeLimit( - ConfigurationRepository configurationRepository) { + SettingsRepository settingsRepository) { log.debug("getTimeLimit:: realTime: {}", realTime); @@ -102,7 +102,7 @@ private CompletableFuture> getTimeLimit( return ofAsync(ClockUtil.getZonedDateTime()); } - return configurationRepository.findTimeZoneConfiguration() + return settingsRepository.lookupTimeZoneSettings() .thenApply(r -> r.map(this::startOfTodayInTimeZone)); } @@ -146,7 +146,7 @@ private List> groupNotices(MultipleRecords>> findNoticesToSend( - ConfigurationRepository configurationRepository, + SettingsRepository settingsRepository, ScheduledNoticesRepository scheduledNoticesRepository, PatronActionSessionRepository patronActionSessionRepository, PageLimit pageLimit) { diff --git a/src/main/java/org/folio/circulation/resources/OverdueFineScheduledNoticeProcessingResource.java b/src/main/java/org/folio/circulation/resources/OverdueFineScheduledNoticeProcessingResource.java index 2d25bab2af..6dc132ea77 100644 --- a/src/main/java/org/folio/circulation/resources/OverdueFineScheduledNoticeProcessingResource.java +++ b/src/main/java/org/folio/circulation/resources/OverdueFineScheduledNoticeProcessingResource.java @@ -21,7 +21,7 @@ import org.folio.circulation.domain.notice.schedule.ScheduledNotice; import org.folio.circulation.domain.notice.schedule.grouping.OverdueFineScheduledNoticeGroupDefinitionFactory; import org.folio.circulation.domain.notice.session.PatronSessionRecord; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; import org.folio.circulation.infrastructure.storage.notices.ScheduledNoticesRepository; import org.folio.circulation.infrastructure.storage.sessions.PatronActionSessionRepository; @@ -51,11 +51,11 @@ protected GroupedScheduledNoticeHandler getHandler(Clients clients, @Override protected CompletableFuture>> findNoticesToSend( - ConfigurationRepository configurationRepository, + SettingsRepository settingsRepository, ScheduledNoticesRepository scheduledNoticesRepository, PatronActionSessionRepository patronActionSessionRepository, PageLimit pageLimit) { - return super.findNoticesToSend(configurationRepository, scheduledNoticesRepository, + return super.findNoticesToSend(settingsRepository, scheduledNoticesRepository, patronActionSessionRepository, pageLimit) .thenCompose(r -> r.after(notices -> filterNotices(notices, patronActionSessionRepository))); } diff --git a/src/main/java/org/folio/circulation/resources/RequestCollectionResource.java b/src/main/java/org/folio/circulation/resources/RequestCollectionResource.java index a222818735..09f7850fd3 100644 --- a/src/main/java/org/folio/circulation/resources/RequestCollectionResource.java +++ b/src/main/java/org/folio/circulation/resources/RequestCollectionResource.java @@ -32,7 +32,6 @@ import org.folio.circulation.domain.validation.RequestLoanValidator; import org.folio.circulation.domain.validation.ServicePointPickupLocationValidator; import org.folio.circulation.infrastructure.storage.CalendarRepository; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.ServicePointRepository; import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; @@ -218,7 +217,7 @@ void delete(RoutingContext routingContext) { final var requestQueueService = RequestQueueService.using(clients); final var updateRequestQueue = new UpdateRequestQueue(new RequestQueueRepository( requestRepository), requestRepository, new ServicePointRepository(clients), - new ConfigurationRepository(clients), requestQueueService, new CalendarRepository(clients)); + new SettingsRepository(clients), requestQueueService, new CalendarRepository(clients)); UpdateItem updateItem = new UpdateItem(itemRepository, requestQueueService); @@ -280,7 +279,6 @@ void move(RoutingContext routingContext) { final var loanPolicyRepository = new LoanPolicyRepository(clients); final var requestPolicyRepository = new RequestPolicyRepository(clients); - final var configurationRepository = new ConfigurationRepository(clients); final var settingsRepository = new SettingsRepository(clients); final var updateUponRequest = new UpdateUponRequest(new UpdateItem(itemRepository, @@ -296,7 +294,7 @@ void move(RoutingContext routingContext) { final var moveRequestService = new MoveRequestService( requestRepository, requestPolicyRepository, updateUponRequest, moveRequestProcessAdapter, new RequestLoanValidator(new ItemByInstanceIdFinder(clients.holdingsStorage(), itemRepository), loanRepository), - RequestNoticeSender.using(clients), configurationRepository, eventPublisher, + RequestNoticeSender.using(clients), eventPublisher, requestQueueRepository, settingsRepository); fromFutureResult(requestRepository.getById(id)) diff --git a/src/main/java/org/folio/circulation/resources/RequestFromRepresentationService.java b/src/main/java/org/folio/circulation/resources/RequestFromRepresentationService.java index 5b6ba0d90d..ce7750ec38 100644 --- a/src/main/java/org/folio/circulation/resources/RequestFromRepresentationService.java +++ b/src/main/java/org/folio/circulation/resources/RequestFromRepresentationService.java @@ -2,7 +2,6 @@ import static java.lang.String.join; import static java.util.concurrent.CompletableFuture.completedFuture; -import static java.util.stream.Collectors.toList; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.folio.circulation.domain.EcsRequestPhase.INTERMEDIATE; @@ -67,7 +66,6 @@ import org.folio.circulation.domain.configuration.TlrSettingsConfiguration; import org.folio.circulation.domain.validation.ProxyRelationshipValidator; import org.folio.circulation.domain.validation.ServicePointPickupLocationValidator; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.ServicePointRepository; import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.HoldingsRepository; @@ -98,7 +96,6 @@ class RequestFromRepresentationService { private final UserRepository userRepository; private final LoanRepository loanRepository; private final ServicePointRepository servicePointRepository; - private final ConfigurationRepository configurationRepository; private final SettingsRepository settingsRepository; private final RequestPolicyRepository requestPolicyRepository; private final ProxyRelationshipValidator proxyRelationshipValidator; @@ -122,7 +119,6 @@ public RequestFromRepresentationService(Request.Operation operation, this.userRepository = repositories.getUserRepository(); this.loanRepository = repositories.getLoanRepository(); this.servicePointRepository = repositories.getServicePointRepository(); - this.configurationRepository = repositories.getConfigurationRepository(); this.settingsRepository = repositories.getSettingsRepository(); this.requestPolicyRepository = repositories.getRequestPolicyRepository(); @@ -148,7 +144,7 @@ CompletableFuture> getRequestFrom(JsonObject re .thenApply(this::refuseWhenNoRequestDate) .thenApply(r -> r.map(this::removeRelatedRecordInformation)) .thenApply(r -> r.map(this::removeProcessingParameters)) - .thenCompose(r -> r.combineAfter(configurationRepository::findTimeZoneConfiguration, + .thenCompose(r -> r.combineAfter(settingsRepository::lookupTimeZoneSettings, Request::truncateRequestExpirationDateToTheEndOfTheDay)) .thenComposeAsync(r -> r.after(when( this::shouldFetchInstance, this::fetchInstance, req -> ofAsync(() -> req)))) @@ -338,7 +334,7 @@ private CompletableFuture> fetchItemAndLoanForRecallTlrCreation( .map(Item::getItemId) .filter(itemId -> request.getInstanceItemsRequestPolicies().get(itemId) .allowsType(RECALL)) - .collect(toList()); + .toList(); return loanRepository.findLoanWithClosestDueDate(recallableItemIds, recalledLoansIds) //Loan is null means that we have no items that haven't been recalled. In this case we diff --git a/src/main/java/org/folio/circulation/resources/RequestQueueResource.java b/src/main/java/org/folio/circulation/resources/RequestQueueResource.java index 059eb284c5..3373075578 100644 --- a/src/main/java/org/folio/circulation/resources/RequestQueueResource.java +++ b/src/main/java/org/folio/circulation/resources/RequestQueueResource.java @@ -11,7 +11,6 @@ import java.lang.invoke.MethodHandles; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -25,7 +24,6 @@ import org.folio.circulation.domain.representations.logs.LogEventType; import org.folio.circulation.domain.validation.RequestQueueValidation; import org.folio.circulation.infrastructure.storage.CalendarRepository; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.ServicePointRepository; import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; @@ -122,13 +120,12 @@ private void reorderQueue(RoutingContext routingContext, RequestQueueType reques final var loanRepository = new LoanRepository(clients, itemRepository, userRepository); final var requestRepository = RequestRepository.using(clients, itemRepository, userRepository, loanRepository); - final var configurationRepository = new ConfigurationRepository(clients); final var settingsRepository = new SettingsRepository(clients); final var requestQueueRepository = new RequestQueueRepository(requestRepository); final UpdateRequestQueue updateRequestQueue = new UpdateRequestQueue( requestQueueRepository, requestRepository, new ServicePointRepository(clients), - configurationRepository, RequestQueueService.using(clients), new CalendarRepository(clients)); + settingsRepository, RequestQueueService.using(clients), new CalendarRepository(clients)); validateTlrFeatureStatus(settingsRepository, requestQueueType, idParamValue) .thenCompose(r -> r.after(tlrSettings -> @@ -168,7 +165,7 @@ private Result publishReorderedQueue(EventPublisher event reorderRequestContext.after(r -> { CompletableFuture.runAsync(() -> { - List reordered = r.getReorderRequestToRequestMap().values().stream().filter(Request::hasChangedPosition).collect(Collectors.toList()); + List reordered = r.getReorderRequestToRequestMap().values().stream().filter(Request::hasChangedPosition).toList(); eventPublisher.publishLogRecord(mapToRequestLogEventJson(reordered), LogEventType.REQUEST_REORDERED); }); return null; diff --git a/src/main/java/org/folio/circulation/resources/RequestScheduledNoticeProcessingResource.java b/src/main/java/org/folio/circulation/resources/RequestScheduledNoticeProcessingResource.java index e611e7cf29..85110c6ab7 100644 --- a/src/main/java/org/folio/circulation/resources/RequestScheduledNoticeProcessingResource.java +++ b/src/main/java/org/folio/circulation/resources/RequestScheduledNoticeProcessingResource.java @@ -29,7 +29,7 @@ import org.folio.circulation.domain.notice.schedule.ItemAwareRequestScheduledNoticeHandler; import org.folio.circulation.domain.notice.schedule.ScheduledNotice; import org.folio.circulation.domain.notice.schedule.ScheduledNoticeContext; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; import org.folio.circulation.infrastructure.storage.notices.ScheduledNoticesRepository; import org.folio.circulation.infrastructure.storage.requests.RequestRepository; @@ -39,7 +39,6 @@ import org.folio.circulation.support.http.client.PageLimit; import org.folio.circulation.support.results.Result; import org.folio.circulation.support.utils.ClockUtil; -import org.folio.circulation.support.utils.LogUtil; import io.vertx.core.http.HttpClient; @@ -52,7 +51,7 @@ public RequestScheduledNoticeProcessingResource(HttpClient client) { @Override protected CompletableFuture>> findNoticesToSend( - ConfigurationRepository configurationRepository, + SettingsRepository settingsRepository, ScheduledNoticesRepository scheduledNoticesRepository, PatronActionSessionRepository patronActionSessionRepository, PageLimit pageLimit) { diff --git a/src/main/java/org/folio/circulation/resources/ScheduledDigitalRemindersProcessingResource.java b/src/main/java/org/folio/circulation/resources/ScheduledDigitalRemindersProcessingResource.java index d8b5f2ad2b..c37a8b27ec 100644 --- a/src/main/java/org/folio/circulation/resources/ScheduledDigitalRemindersProcessingResource.java +++ b/src/main/java/org/folio/circulation/resources/ScheduledDigitalRemindersProcessingResource.java @@ -6,7 +6,7 @@ import org.folio.circulation.domain.MultipleRecords; import org.folio.circulation.domain.notice.schedule.ScheduledDigitalReminderHandler; import org.folio.circulation.domain.notice.schedule.ScheduledNotice; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; import org.folio.circulation.infrastructure.storage.notices.ScheduledNoticesRepository; import org.folio.circulation.infrastructure.storage.requests.RequestRepository; @@ -37,7 +37,7 @@ public ScheduledDigitalRemindersProcessingResource(HttpClient client) { } @Override - protected CompletableFuture>> findNoticesToSend(ConfigurationRepository configurationRepository, ScheduledNoticesRepository scheduledNoticesRepository, PatronActionSessionRepository patronActionSessionRepository, PageLimit pageLimit) { + protected CompletableFuture>> findNoticesToSend(SettingsRepository settingsRepository, ScheduledNoticesRepository scheduledNoticesRepository, PatronActionSessionRepository patronActionSessionRepository, PageLimit pageLimit) { return CqlQuery.lessThanOrEqualTo("nextRunTime", formatDateTime(ClockUtil.getZonedDateTime().withZoneSameInstant(ZoneOffset.UTC))) .combine(exactMatch("noticeConfig.sendInRealTime", "true"), CqlQuery::and) .combine(exactMatch("triggeringEvent", DUE_DATE_WITH_REMINDER_FEE.getRepresentation()), CqlQuery::and) diff --git a/src/main/java/org/folio/circulation/resources/ScheduledNoticeProcessingResource.java b/src/main/java/org/folio/circulation/resources/ScheduledNoticeProcessingResource.java index 628ed92c3f..014bfd36a0 100644 --- a/src/main/java/org/folio/circulation/resources/ScheduledNoticeProcessingResource.java +++ b/src/main/java/org/folio/circulation/resources/ScheduledNoticeProcessingResource.java @@ -8,6 +8,7 @@ import org.folio.circulation.domain.MultipleRecords; import org.folio.circulation.domain.notice.schedule.ScheduledNotice; import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; import org.folio.circulation.infrastructure.storage.notices.ScheduledNoticesRepository; @@ -49,6 +50,7 @@ private void process(RoutingContext routingContext) { ScheduledNoticesRepository.using(clients); final ConfigurationRepository configurationRepository = new ConfigurationRepository(clients); + final var settingsRepository = new SettingsRepository(clients); final var itemRepository = new ItemRepository(clients); final var userRepository = new UserRepository(clients); final var loanRepository = new LoanRepository(clients, itemRepository, userRepository); @@ -58,7 +60,7 @@ private void process(RoutingContext routingContext) { clients, loanRepository, userRepository); safelyInitialise(configurationRepository::lookupSchedulerNoticesProcessingLimit) - .thenCompose(r -> r.after(limit -> findNoticesToSend(configurationRepository, + .thenCompose(r -> r.after(limit -> findNoticesToSend(settingsRepository, scheduledNoticesRepository, patronActionSessionRepository, limit))) .thenCompose(r -> r.after(notices -> handleNotices(clients, requestRepository, loanRepository, notices))) @@ -68,7 +70,7 @@ private void process(RoutingContext routingContext) { } protected abstract CompletableFuture>> findNoticesToSend( - ConfigurationRepository configurationRepository, + SettingsRepository settingsRepository, ScheduledNoticesRepository scheduledNoticesRepository, PatronActionSessionRepository patronActionSessionRepository, PageLimit pageLimit); diff --git a/src/main/java/org/folio/circulation/resources/renewal/RenewalResource.java b/src/main/java/org/folio/circulation/resources/renewal/RenewalResource.java index 5b80f9d066..90740bd451 100644 --- a/src/main/java/org/folio/circulation/resources/renewal/RenewalResource.java +++ b/src/main/java/org/folio/circulation/resources/renewal/RenewalResource.java @@ -85,7 +85,6 @@ import org.folio.circulation.domain.validation.overriding.OverridingBlockValidator; import org.folio.circulation.infrastructure.storage.AutomatedPatronBlocksRepository; import org.folio.circulation.infrastructure.storage.CalendarRepository; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.feesandfines.FeeFineOwnerRepository; import org.folio.circulation.infrastructure.storage.feesandfines.FeeFineRepository; @@ -169,7 +168,6 @@ private void renew(RoutingContext routingContext) { final StoreLoanAndItem storeLoanAndItem = new StoreLoanAndItem(loanRepository, itemRepository); final LoanRepresentation loanRepresentation = new LoanRepresentation(); - final ConfigurationRepository configurationRepository = new ConfigurationRepository(clients); final SettingsRepository settingsRepository = new SettingsRepository(clients); final LoanScheduledNoticeService scheduledNoticeService = LoanScheduledNoticeService.using(clients); final ReminderFeeScheduledNoticeService scheduledRemindersService = new ReminderFeeScheduledNoticeService(clients); @@ -212,7 +210,7 @@ private void renew(RoutingContext routingContext) { RenewalContext::withTlrSettings)) .thenComposeAsync(r -> r.after( ctx -> lookupRequestQueue(ctx, requestQueueRepository, errorHandler))) - .thenCompose(r -> r.combineAfter(configurationRepository::findTimeZoneConfiguration, + .thenCompose(r -> r.combineAfter(settingsRepository::lookupTimeZoneSettings, RenewalContext::withTimeZone)) .thenComposeAsync(r -> r.after(context -> renew(context, clients, errorHandler))) .thenApply(r -> r.next(errorHandler::failWithValidationErrors)) @@ -506,7 +504,7 @@ private Validator createManualPatronBlocksValidator(JsonObject r } private BlockOverrides getOverrideBlocks(JsonObject request) { - return BlockOverrides.from(getObjectProperty(request, "overrideBlocks")); + return BlockOverrides.from(getObjectProperty(request, OVERRIDE_BLOCKS)); } private CompletableFuture> refuseIfNoPermissionsForRenewalOverride( @@ -715,7 +713,7 @@ private Result renew(RenewalContext context, ZonedDateTime renew if (errors.isEmpty()) { final BlockOverrides blockOverrides = BlockOverrides.from(getObjectProperty( - context.getRenewalRequest(), "overrideBlocks")); + context.getRenewalRequest(), OVERRIDE_BLOCKS)); if (!blockOverrides.getPatronBlockOverride().isRequested() && !blockOverrides.getRenewalBlockOverride().isRequested()) { diff --git a/src/main/java/org/folio/circulation/services/agedtolost/ChargeLostFeesWhenAgedToLostService.java b/src/main/java/org/folio/circulation/services/agedtolost/ChargeLostFeesWhenAgedToLostService.java index 4f6f2372cf..5aa782b29d 100644 --- a/src/main/java/org/folio/circulation/services/agedtolost/ChargeLostFeesWhenAgedToLostService.java +++ b/src/main/java/org/folio/circulation/services/agedtolost/ChargeLostFeesWhenAgedToLostService.java @@ -1,5 +1,6 @@ package org.folio.circulation.services.agedtolost; +import static java.util.concurrent.CompletableFuture.completedFuture; import static java.util.stream.Collectors.toSet; import static org.folio.circulation.domain.FeeFine.LOST_ITEM_FEE_TYPE; import static org.folio.circulation.domain.FeeFine.LOST_ITEM_PROCESSING_FEE_TYPE; @@ -36,6 +37,7 @@ import org.apache.logging.log4j.Logger; import org.folio.circulation.StoreLoanAndItem; import org.folio.circulation.domain.FeeFine; +import org.folio.circulation.domain.FeeFineAction; import org.folio.circulation.domain.FeeFineOwner; import org.folio.circulation.domain.Loan; import org.folio.circulation.domain.MultipleRecords; @@ -136,7 +138,6 @@ private CompletableFuture> chargeLostFees( LoanToChargeFees loanToChargeFees) { return ofAsync(() -> loanToChargeFees) - .thenCompose(r -> r.after(actualCostRecordService::createIfNecessaryForAgedToLostItem)) .thenCompose(r -> r.after(this::chargeLostFeesForLoan)) .thenCompose(r -> r.after(eventPublisher::publishClosedLoanEvent)) .thenApply(r -> r.mapFailure(failure -> handleFailure(loanToChargeFees, failure.toString()))) @@ -149,18 +150,27 @@ private static Result handleFailure(LoanToChargeFees loan, String errorMes } private CompletableFuture> chargeLostFeesForLoan(LoanToChargeFees loanToChargeFees) { + Loan loan = loanToChargeFees.getLoan(); + // we can close loans that have no fee to charge // and billed immediately if (loanToChargeFees.shouldCloseLoan()) { - log.info("No age to lost fees/fines to charge immediately, closing loan [{}]", - loanToChargeFees.getLoan().getId()); - + log.info("No age to lost fees/fines to charge immediately, closing loan [{}]", loan.getId()); return closeLoanAsLostAndPaid(loanToChargeFees); } - Loan loan = loanToChargeFees.getLoan(); - return createAccountsForLoan(loanToChargeFees) - .after(feeFineFacade::createAccounts) + if (loanToChargeFees.hasNoFeeFineOwner()) { + log.warn("No fee/fine owner present for primary service point {}, skipping loan {}", + loanToChargeFees.getPrimaryServicePointId(), loan.getId()); + + return completedFuture(failed(singleValidationError( + "No fee/fine owner found for item's permanent location", + "servicePointId", loanToChargeFees.getPrimaryServicePointId()))); + } + + return actualCostRecordService.createIfNecessaryForAgedToLostItem(loanToChargeFees) + .thenApply(r -> r.next(this::createAccountsForLoan)) + .thenCompose(r -> r.after(feeFineFacade::createAccounts)) .thenCompose(r -> r.after(actions -> feeFineScheduledNoticeService.scheduleNoticesForAgedLostFeeFineCharged(loan, actions))) .thenCompose(r -> r.after(notUsed -> updateLoanBillingInfo(loanToChargeFees))); @@ -279,14 +289,6 @@ private Result loanFetchQuery() { } private Result validateCanCreateAccountForLoan(LoanToChargeFees loanToChargeFees) { - if (loanToChargeFees.hasNoFeeFineOwner()) { - log.warn("No fee/fine owner present for service point {}, skipping loan {}", - loanToChargeFees.getPrimaryServicePointId(), loanToChargeFees.getLoan().getId()); - - return failed(singleValidationError("No fee/fine owner found for item's effective location", - "servicePointId", loanToChargeFees.getPrimaryServicePointId())); - } - final LostItemPolicy lostItemPolicy = loanToChargeFees.getLoan().getLostItemPolicy(); if (lostItemPolicy.getSetCostFee().isChargeable() && loanToChargeFees.hasNoLostItemFee()) { diff --git a/src/main/java/org/folio/circulation/storage/mappers/InstanceMapper.java b/src/main/java/org/folio/circulation/storage/mappers/InstanceMapper.java index 6a8a116cc4..1aaae1d858 100644 --- a/src/main/java/org/folio/circulation/storage/mappers/InstanceMapper.java +++ b/src/main/java/org/folio/circulation/storage/mappers/InstanceMapper.java @@ -13,6 +13,7 @@ import org.folio.circulation.domain.Publication; import io.vertx.core.json.JsonObject; +import org.folio.circulation.domain.SeriesStatement; public class InstanceMapper { public Instance toDomain(JsonObject representation) { @@ -21,7 +22,7 @@ public Instance toDomain(JsonObject representation) { getProperty(representation, "title"), mapIdentifiers(representation), mapContributors(representation), mapPublication(representation), mapEditions(representation), - mapPhysicalDescriptions(representation)); + mapPhysicalDescriptions(representation), mapSeries(representation)); } private List mapContributors(JsonObject representation) { @@ -46,4 +47,8 @@ private List mapPhysicalDescriptions(JsonObject representation) { .collect(Collectors.toList()); } + private List mapSeries(JsonObject representation) { + return mapToList(representation, "series", new SeriesMapper()::toDomain); + } + } 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 39c39600d4..250ae8d70b 100644 --- a/src/main/java/org/folio/circulation/storage/mappers/ItemMapper.java +++ b/src/main/java/org/folio/circulation/storage/mappers/ItemMapper.java @@ -50,6 +50,8 @@ private ItemDescription getDescription(JsonObject representation) { .collect(Collectors.toList()), getProperty(representation, "accessionNumber"), toStream(representation, "administrativeNotes") + .collect(Collectors.toList()), + toStream(representation, "seriesStatements") .collect(Collectors.toList())); } diff --git a/src/main/java/org/folio/circulation/storage/mappers/SeriesMapper.java b/src/main/java/org/folio/circulation/storage/mappers/SeriesMapper.java new file mode 100644 index 0000000000..de97023733 --- /dev/null +++ b/src/main/java/org/folio/circulation/storage/mappers/SeriesMapper.java @@ -0,0 +1,15 @@ +package org.folio.circulation.storage.mappers; + +import io.vertx.core.json.JsonObject; +import org.folio.circulation.domain.SeriesStatement; + +import static org.folio.circulation.support.json.JsonPropertyFetcher.getProperty; + +public class SeriesMapper { + public SeriesStatement toDomain(JsonObject representation) { + + return new SeriesStatement( + getProperty(representation, "authorityId"), + getProperty(representation, "value")); + } +} diff --git a/src/test/java/api/loans/CheckOutCalculateDueDateShortTermTests.java b/src/test/java/api/loans/CheckOutCalculateDueDateShortTermTests.java index 2c9a8dddbe..0395ff20ef 100644 --- a/src/test/java/api/loans/CheckOutCalculateDueDateShortTermTests.java +++ b/src/test/java/api/loans/CheckOutCalculateDueDateShortTermTests.java @@ -20,13 +20,13 @@ import java.time.LocalTime; import java.time.ZoneId; -import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; +import api.support.fixtures.SettingsFixture; import org.folio.circulation.domain.policy.DueDateManagement; import org.folio.circulation.domain.policy.Period; import org.folio.circulation.support.http.client.Response; @@ -35,7 +35,6 @@ import api.support.APITests; import api.support.builders.CheckOutByBarcodeRequestBuilder; import api.support.builders.LoanPolicyBuilder; -import api.support.fixtures.ConfigurationExample; import api.support.http.IndividualResource; import io.vertx.core.json.JsonObject; @@ -64,7 +63,7 @@ void testRespectSelectedTimezoneForDueDateCalculations() throws Exception { String expectedTimeZone = "America/New_York"; int duration = 24; - Response response = configClient.create(ConfigurationExample.newYorkTimezoneConfiguration()) + Response response = settingsClient.create(SettingsFixture.newYorkTimezoneConfiguration()) .getResponse(); assertThat(response.getBody(), containsString(expectedTimeZone)); @@ -80,8 +79,8 @@ void testRespectSelectedTimezoneForDueDateCalculations() throws Exception { void testRespectUtcTimezoneForDueDateCalculations() throws Exception { int duration = 24; - Response response = configClient.create(ConfigurationExample.utcTimezoneConfiguration()) - .getResponse(); + Response response = settingsClient.create(SettingsFixture.utcTimezoneConfiguration()).getResponse(); + assertThat(response.getBody(), containsString(UTC.toString())); ZonedDateTime loanDate = ZonedDateTime.of(CASE_FRI_SAT_MON_DAY_ALL_PREV_DATE, @@ -96,9 +95,8 @@ void testRespectUtcTimezoneForDueDateCalculations() throws Exception { void testMoveToTheEndOfCurrentServicePointHoursRolloverScenario() throws Exception { int duration = 18; - Response response = configClient.create(ConfigurationExample.utcTimezoneConfiguration()) - .getResponse(); - assertThat(response.getBody(), containsString(ZoneOffset.UTC.toString())); + Response response = settingsClient.create(SettingsFixture.utcTimezoneConfiguration()).getResponse(); + assertThat(response.getBody(), containsString(UTC.toString())); ZonedDateTime loanDate = ZonedDateTime.of(CASE_CURRENT_IS_OPEN_CURR_DAY, TEST_TIME_MORNING, UTC); @@ -111,9 +109,8 @@ void testMoveToTheEndOfCurrentServicePointHoursRolloverScenario() throws Excepti void testMoveToTheEndOfCurrentServicePointHoursNextDayIsClosed() throws Exception { int duration = 1; - Response response = configClient.create(ConfigurationExample.utcTimezoneConfiguration()) - .getResponse(); - assertThat(response.getBody(), containsString(ZoneOffset.UTC.toString())); + Response response = settingsClient.create(SettingsFixture.utcTimezoneConfiguration()).getResponse(); + assertThat(response.getBody(), containsString(UTC.toString())); ZonedDateTime loanDate = ZonedDateTime.of(CASE_CURRENT_IS_OPEN_CURR_DAY, TEST_TIME_MORNING, UTC); ZonedDateTime expectedDueDate = atEndOfDay(CASE_CURRENT_IS_OPEN_CURR_DAY, UTC); diff --git a/src/test/java/api/loans/CheckOutCalculateDueDateTests.java b/src/test/java/api/loans/CheckOutCalculateDueDateTests.java index 5eb0f2b250..72e36aa1f8 100644 --- a/src/test/java/api/loans/CheckOutCalculateDueDateTests.java +++ b/src/test/java/api/loans/CheckOutCalculateDueDateTests.java @@ -15,9 +15,9 @@ import static api.support.fixtures.CalendarExamples.getCurrentAndNextFakeOpeningDayByServId; import static api.support.fixtures.CalendarExamples.getFirstFakeOpeningDayByServId; import static api.support.fixtures.CalendarExamples.getLastFakeOpeningDayByServId; -import static api.support.fixtures.ConfigurationExample.newYorkTimezoneConfiguration; -import static api.support.fixtures.ConfigurationExample.utcTimezoneConfiguration; import static api.support.fixtures.LibraryHoursExamples.CASE_CALENDAR_IS_UNAVAILABLE_SERVICE_POINT_ID; +import static api.support.fixtures.SettingsFixture.newYorkTimezoneConfiguration; +import static api.support.fixtures.SettingsFixture.utcTimezoneConfiguration; import static api.support.matchers.DateTimeMatchers.isEquivalentTo; import static api.support.matchers.ResponseStatusCodeMatcher.hasStatus; import static api.support.matchers.ValidationErrorMatchers.hasCode; @@ -76,7 +76,7 @@ class CheckOutCalculateDueDateTests extends APITests { @Test void testRespectSelectedTimezoneForDueDateCalculations() { - configClient.create(newYorkTimezoneConfiguration()); + settingsClient.create(newYorkTimezoneConfiguration()); final IndividualResource smallAngryPlanet = itemsFixture.basedUponSmallAngryPlanet(); final IndividualResource steve = usersFixture.steve(); @@ -100,7 +100,7 @@ void testRespectSelectedTimezoneForDueDateCalculations() { @Test void testRespectUtcTimezoneForDueDateCalculations() { - configClient.create(utcTimezoneConfiguration()); + settingsClient.create(utcTimezoneConfiguration()); final IndividualResource smallAngryPlanet = itemsFixture.basedUponSmallAngryPlanet(); final IndividualResource steve = usersFixture.steve(); diff --git a/src/test/java/api/loans/DueDateNotRealTimeScheduledNoticesProcessingTests.java b/src/test/java/api/loans/DueDateNotRealTimeScheduledNoticesProcessingTests.java index 633b198f4f..ca008907fb 100644 --- a/src/test/java/api/loans/DueDateNotRealTimeScheduledNoticesProcessingTests.java +++ b/src/test/java/api/loans/DueDateNotRealTimeScheduledNoticesProcessingTests.java @@ -1,5 +1,7 @@ package api.loans; +import static api.support.fixtures.SettingsFixture.timezoneConfigurationFor; +import static api.support.fixtures.SettingsFixture.utcTimezoneConfiguration; import static api.support.fixtures.TemplateContextMatchers.getLoanAdditionalInfoContextMatchers; import static api.support.fixtures.TemplateContextMatchers.getLoanPolicyContextMatchersForUnlimitedRenewals; import static api.support.fixtures.TemplateContextMatchers.getMultipleLoansContextMatcher; @@ -49,12 +51,12 @@ import lombok.val; class DueDateNotRealTimeScheduledNoticesProcessingTests extends APITests { - private final static UUID TEMPLATE_ID = UUID.randomUUID(); + private static final UUID TEMPLATE_ID = UUID.randomUUID(); private static final String LOAN_INFO_ADDED = "testing patron info"; @BeforeEach - public void setUp() { + void setUp() { templateFixture.createDummyNoticeTemplate(TEMPLATE_ID); } @@ -127,7 +129,7 @@ void uponAtDueDateNoticesShouldBeSentInGroups() { @Test void beforeRecurringNoticesAreRescheduled() { - configClient.create(ConfigurationExample.utcTimezoneConfiguration()); + settingsClient.create(utcTimezoneConfiguration()); Period beforePeriod = Period.weeks(1); Period recurringPeriod = Period.days(1); @@ -521,7 +523,7 @@ void noticeIsDeletedIfReferencedTemplateDoesNotExist() { verifyNumberOfScheduledNotices(1); - ZonedDateTime dueDate = parseDateTime(nodToJamesLoan.getJson().getString("dueDate"));; + ZonedDateTime dueDate = parseDateTime(nodToJamesLoan.getJson().getString("dueDate")); ZonedDateTime afterLoanDueDateTime = dueDate.plusDays(1); scheduledNoticeProcessingClient.runDueDateNotRealTimeNoticesProcessing(afterLoanDueDateTime); @@ -690,7 +692,7 @@ private void scheduledNotRealTimeNoticesShouldBeSentAtMidnightInTenantsTimeZone( ZonedDateTime systemTime = ZonedDateTime.of(2020, 6, 25, 0, 0, 0, 0, ZoneId.of(timeZoneId)) .plusMinutes(plusMinutes); mockClockManagerToReturnFixedDateTime(systemTime); - configClient.create(ConfigurationExample.timezoneConfigurationFor(timeZoneId)); + settingsClient.create(timezoneConfigurationFor(timeZoneId)); JsonObject uponAtDueDateNoticeConfig = new NoticeConfigurationBuilder() .withTemplateId(TEMPLATE_ID) diff --git a/src/test/java/api/loans/EndExpiredPatronActionSessionTests.java b/src/test/java/api/loans/EndExpiredPatronActionSessionTests.java index de3f752b12..d44582a5d5 100644 --- a/src/test/java/api/loans/EndExpiredPatronActionSessionTests.java +++ b/src/test/java/api/loans/EndExpiredPatronActionSessionTests.java @@ -17,11 +17,9 @@ import static org.hamcrest.Matchers.hasSize; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -55,7 +53,7 @@ public EndExpiredPatronActionSessionTests() { } @BeforeEach - public void before() { + void before() { JsonObject checkOutNoticeConfig = new NoticeConfigurationBuilder() .withTemplateId(CHECK_OUT_TEMPLATE_ID) .withCheckOutEvent() diff --git a/src/test/java/api/loans/LoanAPITests.java b/src/test/java/api/loans/LoanAPITests.java index d4d88957ae..4c28514774 100644 --- a/src/test/java/api/loans/LoanAPITests.java +++ b/src/test/java/api/loans/LoanAPITests.java @@ -2,6 +2,7 @@ import static api.requests.RequestsAPICreationTests.setupMissingItem; import static api.support.fakes.PublishedEvents.byEventType; +import static api.support.fixtures.SettingsFixture.utcTimezoneConfiguration; import static api.support.http.AdditionalHttpStatusCodes.UNPROCESSABLE_ENTITY; import static api.support.http.CqlQuery.queryFromTemplate; import static api.support.http.Limit.limit; @@ -351,7 +352,7 @@ void canGetMultipleFeesFinesForSingleLoan() { @Test void canGetMultipleFeesFinesForMultipleLoans() { - configClient.create(ConfigurationExample.utcTimezoneConfiguration()); + settingsClient.create(utcTimezoneConfiguration()); IndividualResource item1 = itemsFixture.basedUponSmallAngryPlanet(); final ItemResource item2 = itemsFixture.basedUponNod(); @@ -1078,7 +1079,7 @@ void canGetALoanById() { @Test void canGetLoanPolicyPropertiesForMultipleLoans() { - configClient.create(ConfigurationExample.utcTimezoneConfiguration()); + settingsClient.create(utcTimezoneConfiguration()); IndividualResource item1 = itemsFixture.basedUponSmallAngryPlanet(); final ItemResource item2 = itemsFixture.basedUponNod(); diff --git a/src/test/java/api/loans/RenewalAPITests.java b/src/test/java/api/loans/RenewalAPITests.java index 63c6ab507e..c265fbe396 100644 --- a/src/test/java/api/loans/RenewalAPITests.java +++ b/src/test/java/api/loans/RenewalAPITests.java @@ -22,6 +22,8 @@ import static api.support.fixtures.CalendarExamples.START_TIME_FIRST_PERIOD; import static api.support.fixtures.CalendarExamples.START_TIME_SECOND_PERIOD; import static api.support.fixtures.CalendarExamples.WEDNESDAY_DATE; +import static api.support.fixtures.SettingsFixture.newYorkTimezoneConfiguration; +import static api.support.fixtures.SettingsFixture.utcTimezoneConfiguration; import static api.support.http.CqlQuery.queryFromTemplate; import static api.support.matchers.EventActionMatchers.ITEM_RENEWED; import static api.support.matchers.EventMatchers.isValidLoanDueDateChangedEvent; @@ -111,7 +113,6 @@ import api.support.builders.RequestBuilder; import api.support.fakes.FakeModNotify; import api.support.fakes.FakePubSub; -import api.support.fixtures.ConfigurationExample; import api.support.fixtures.ItemExamples; import api.support.fixtures.TemplateContextMatchers; import api.support.http.CheckOutResource; @@ -196,7 +197,7 @@ void canRenewRollingLoanFromSystemDate() { @Test void canRenewRollingLoanFromCurrentDueDate() { - configClient.create(ConfigurationExample.utcTimezoneConfiguration()); + settingsClient.create(utcTimezoneConfiguration()); IndividualResource smallAngryPlanet = itemsFixture.basedUponSmallAngryPlanet(); final IndividualResource jessica = usersFixture.jessica(); @@ -874,7 +875,7 @@ void cannotRenewWhenItemIsDeclaredLost() { final Response response = attemptRenewal(smallAngryPlanet, jessica); assertThat(response.getJson(), hasErrorWith(allOf( - hasMessage("item is Declared lost"), + hasMessage(ITEM_IS_DECLARED_LOST), hasUUIDParameter("itemId", smallAngryPlanet.getId())))); assertThat(getOverridableBlockNames(response), hasItem("renewalBlock")); } @@ -1141,7 +1142,7 @@ void testMoveToEndOfCurrentServicePointHours() { void testRespectSelectedTimezoneForDueDateCalculations() { String expectedTimeZone = "America/New_York"; - Response response = configClient.create(ConfigurationExample.newYorkTimezoneConfiguration()) + Response response = settingsClient.create(newYorkTimezoneConfiguration()) .getResponse(); assertThat(response.getBody(), containsString(expectedTimeZone)); diff --git a/src/test/java/api/loans/agetolost/ScheduledAgeToLostFeeChargingApiTest.java b/src/test/java/api/loans/agetolost/ScheduledAgeToLostFeeChargingApiTest.java index 87fb375c29..936d3669df 100644 --- a/src/test/java/api/loans/agetolost/ScheduledAgeToLostFeeChargingApiTest.java +++ b/src/test/java/api/loans/agetolost/ScheduledAgeToLostFeeChargingApiTest.java @@ -298,6 +298,28 @@ void cannotChargeFeesWhenNoItemFeeType() { assertThatPublishedLoanLogRecordEventsAreValid(loanAfter.getJson()); } + @Test + void cannotChargeActualCostWithoutOwner() { + feeFineOwnerFixture.cleanUp(); // remove owner + + var policy = lostItemFeePoliciesFixture + .ageToLostAfterOneMinutePolicy() + .withActualCost(19.99) + .withLostItemProcessingFee(9.99); + useLostItemPolicy(lostItemFeePoliciesFixture.create(policy).getId()); + val item = itemsFixture.basedUponNod(ItemBuilder::withRandomBarcode); + val loanBefore = checkOutFixture.checkOutByBarcode(item, usersFixture.steve()); + + ageToLostFixture.ageToLostAndChargeFees(); + + IndividualResource loanAfter = loansFixture.getLoanById(loanBefore.getId()); + assertThat(loanAfter, hasNoLostItemFee()); + assertThat(loanAfter, hasNoLostItemProcessingFee()); + assertThat(accountsClient.getAll(), hasSize(0)); + assertThat(scheduledNoticesClient.getAll(), hasSize(0)); + assertThatPublishedLoanLogRecordEventsAreValid(loanAfter.getJson()); + } + @Test void shouldNotChargeFeesWhenDelayedBillingPeriodHasNotPassed() { val lostItemFeePolicy = lostItemFeePoliciesFixture diff --git a/src/test/java/api/requests/RequestsAPIRetrievalTests.java b/src/test/java/api/requests/RequestsAPIRetrievalTests.java index bac64efab1..7e0b23c8dc 100644 --- a/src/test/java/api/requests/RequestsAPIRetrievalTests.java +++ b/src/test/java/api/requests/RequestsAPIRetrievalTests.java @@ -20,7 +20,7 @@ import java.util.UUID; import static api.support.builders.ItemBuilder.CHECKED_OUT; -import static api.support.fixtures.ConfigurationExample.newYorkTimezoneConfiguration; +import static api.support.fixtures.SettingsFixture.newYorkTimezoneConfiguration; import static api.support.http.CqlQuery.noQuery; import static api.support.http.CqlQuery.queryFromTemplate; import static api.support.http.Limit.limit; @@ -765,7 +765,7 @@ void requestExpirationDateShouldStoreInTenantsTimeZone() { proxyRelationshipsFixture.nonExpiringProxyFor(sponsor, proxy); checkOutFixture.checkOutByBarcode(smallAngryPlanet); - configClient.create(newYorkTimezoneConfiguration()); + settingsClient.create(newYorkTimezoneConfiguration()); final IndividualResource createdRequest = requestsFixture.place( new RequestBuilder() diff --git a/src/test/java/api/requests/StaffSlipsTests.java b/src/test/java/api/requests/StaffSlipsTests.java index e8efb972e2..6242cf5461 100644 --- a/src/test/java/api/requests/StaffSlipsTests.java +++ b/src/test/java/api/requests/StaffSlipsTests.java @@ -260,6 +260,7 @@ void responseContainsSlipWithAllAvailableTokens(String countryCode, String prima .withInstance(new InstanceMapper().toDomain(itemResource.getInstance().getJson())); String contributorNames = item.getContributorNames().collect(joining("; ")); + String seriesStatements = String.join("; ",item.getSeriesStatementValues()); String yearCaptionsToken = String.join("; ", item.getYearCaption()); String copyNumber = item.getCopyNumber() != null ? item.getCopyNumber() : ""; @@ -272,6 +273,7 @@ void responseContainsSlipWithAllAvailableTokens(String countryCode, String prima assertEquals(expectedItemStatus.getValue(), itemContext.getString("status")); assertEquals(item.getPrimaryContributorName(), itemContext.getString("primaryContributor")); assertEquals(contributorNames, itemContext.getString("allContributors")); + assertEquals(seriesStatements, itemContext.getString("seriesStatements")); assertEquals(item.getEnumeration(), itemContext.getString("enumeration")); assertEquals(item.getVolume(), itemContext.getString("volume")); assertEquals(item.getChronology(), itemContext.getString("chronology")); @@ -582,7 +584,7 @@ void responseContainsSearchSlipsForTLR() { assertThat(response.getStatusCode(), is(HTTP_OK)); assertResponseHasItems(response, 1, SlipsType.SEARCH_SLIPS); assertResponseContains(response, SlipsType.SEARCH_SLIPS, holdRequest, steve); - assertThat(response.getJson(), hasJsonPath("searchSlips[0].item.title", + assertThat(response.getJson(), hasJsonPath("searchSlips[0].item.title", "The Long Way to a Small, Angry Planet")); } diff --git a/src/test/java/api/requests/scenarios/HoldShelfExpirationDateTests.java b/src/test/java/api/requests/scenarios/HoldShelfExpirationDateTests.java index d49a67036c..a7b7c08b8c 100644 --- a/src/test/java/api/requests/scenarios/HoldShelfExpirationDateTests.java +++ b/src/test/java/api/requests/scenarios/HoldShelfExpirationDateTests.java @@ -9,6 +9,7 @@ import static api.support.builders.RequestBuilder.OPEN_NOT_YET_FILLED; import static api.support.fixtures.CalendarExamples.CASE_CURRENT_DATE_CLOSE; import static api.support.fixtures.CalendarExamples.CASE_NEXT_DATE_OPEN; +import static api.support.fixtures.SettingsFixture.timezoneConfigurationFor; import static api.support.matchers.ItemStatusCodeMatcher.hasItemStatus; import static api.support.matchers.ResponseStatusCodeMatcher.hasStatus; import static api.support.matchers.TextDateTimeMatcher.isEquivalentTo; @@ -46,20 +47,19 @@ import api.support.APITests; import api.support.builders.CheckInByBarcodeRequestBuilder; -import api.support.fixtures.ConfigurationExample; import api.support.http.IndividualResource; import io.vertx.core.json.JsonObject; class HoldShelfExpirationDateTests extends APITests { @BeforeEach - public void setUp() { + void setUp() { // reset the clock before each test (just in case) setClock(Clock .fixed(getInstant(), UTC)); } @AfterEach - public void afterEach() { + void afterEach() { // The clock must be reset after each test. setDefaultClock(); } @@ -148,8 +148,7 @@ private void assertHoldShelfExpirationDateBasedOnStrategy(JsonObject storedReque assertThat("request hold shelf expiration date is " + amount + " " + interval.toString() + " in the future", storedRequest.getString("holdShelfExpirationDate"), isEquivalentTo(moveToBeginningOfNextServicePointHours2)); break; - case "cd9" : - case "cd10" : + case "cd9", "cd10" : ZonedDateTime moveToTheNextOpenDay = atEndOfDay(interval.addTo(getZonedDateTime(), amount)).plusDays(1); assertThat("request hold shelf expiration date is " + amount + " " + interval.toString() + " in the future", storedRequest.getString("holdShelfExpirationDate"), isEquivalentTo(moveToTheNextOpenDay)); @@ -203,8 +202,9 @@ void shouldUseTenantTimeZoneForLongTerm() { final int amount = 30; final ZoneId tenantTimeZone = ZoneId.of("America/New_York"); - IndividualResource updateTimeZoneConfig = configClient - .create(ConfigurationExample.timezoneConfigurationFor(tenantTimeZone.getId())); + IndividualResource updateTimeZoneConfig = settingsClient.create( + timezoneConfigurationFor(tenantTimeZone.getId()) + ); assertThat(updateTimeZoneConfig.getResponse().getStatusCode(), is(201)); final IndividualResource checkInServicePoint = servicePointsFixture.cd1(); @@ -241,8 +241,8 @@ void shouldUseTenantTimeZoneForShortTerm() { final int amount = 42; final ZoneId tenantTimeZone = ZoneId.of("America/New_York"); - IndividualResource updateTimeZoneConfig = configClient - .create(ConfigurationExample.timezoneConfigurationFor(tenantTimeZone.getId())); + IndividualResource updateTimeZoneConfig = settingsClient + .create(timezoneConfigurationFor(tenantTimeZone.getId())); assertThat(updateTimeZoneConfig.getResponse().getStatusCode(), is(201)); final IndividualResource checkInServicePoint = servicePointsFixture.cd5(); diff --git a/src/test/java/api/requests/scenarios/LoanDueDatesAfterRecallTests.java b/src/test/java/api/requests/scenarios/LoanDueDatesAfterRecallTests.java index 8ab2ea5b64..0f7e636922 100644 --- a/src/test/java/api/requests/scenarios/LoanDueDatesAfterRecallTests.java +++ b/src/test/java/api/requests/scenarios/LoanDueDatesAfterRecallTests.java @@ -4,7 +4,7 @@ import static api.support.fakes.PublishedEvents.byLogEventType; import static api.support.fixtures.CalendarExamples.CASE_FRI_SAT_MON_SERVICE_POINT_ID; import static api.support.fixtures.CalendarExamples.CASE_FRI_SAT_MON_SERVICE_POINT_NEXT_DAY; -import static api.support.fixtures.ConfigurationExample.timezoneConfigurationFor; +import static api.support.fixtures.SettingsFixture.timezoneConfigurationFor; import static api.support.http.CqlQuery.queryFromTemplate; import static api.support.matchers.JsonObjectMatcher.hasJsonPath; import static api.support.matchers.TextDateTimeMatcher.isEquivalentTo; @@ -80,12 +80,12 @@ public LoanDueDatesAfterRecallTests() { } @BeforeEach - public void setUp() { + void setUp() { setClock(Clock.fixed(getInstant(), ZoneOffset.UTC)); } @AfterEach - public void afterEach() { + void afterEach() { // The clock must be reset after each test. setDefaultClock(); } @@ -420,7 +420,7 @@ void changedDueDateAfterRecallingAnItemShouldRespectTenantTimezone() { final IndividualResource steve = usersFixture.steve(); final IndividualResource jessica = usersFixture.jessica(); - configClient.create(timezoneConfigurationFor(stockholmTimeZone)); + settingsClient.create(timezoneConfigurationFor(stockholmTimeZone)); final LoanPolicyBuilder canCirculateRollingPolicy = new LoanPolicyBuilder() .withName("Can Circulate Rolling With Recalls") diff --git a/src/test/java/api/requests/scenarios/MoveRequestTests.java b/src/test/java/api/requests/scenarios/MoveRequestTests.java index 2f9a30856c..67034214db 100644 --- a/src/test/java/api/requests/scenarios/MoveRequestTests.java +++ b/src/test/java/api/requests/scenarios/MoveRequestTests.java @@ -3,7 +3,7 @@ import static api.support.builders.ItemBuilder.AVAILABLE; import static api.support.builders.ItemBuilder.PAGED; import static api.support.builders.RequestBuilder.OPEN_AWAITING_PICKUP; -import static api.support.fixtures.ConfigurationExample.timezoneConfigurationFor; +import static api.support.fixtures.SettingsFixture.timezoneConfigurationFor; import static api.support.matchers.EventMatchers.isValidLoanDueDateChangedEvent; import static api.support.matchers.ItemStatusCodeMatcher.hasItemStatus; import static api.support.matchers.TextDateTimeMatcher.isEquivalentTo; @@ -73,7 +73,7 @@ class MoveRequestTests extends APITests { @AfterEach - public void afterEach() { + void afterEach() { // The clock must be reset after each test. setDefaultClock(); } @@ -1232,7 +1232,7 @@ void changedDueDateAfterRecallingAnItemShouldRespectTenantTimezone() { val steve = usersFixture.steve(); val jessica = usersFixture.jessica(); - configClient.create(timezoneConfigurationFor(stockholmTimeZone)); + settingsClient.create(timezoneConfigurationFor(stockholmTimeZone)); final LoanPolicyBuilder canCirculateRollingPolicy = new LoanPolicyBuilder() .withName("Can Circulate Rolling With Recalls") diff --git a/src/test/java/api/support/APITests.java b/src/test/java/api/support/APITests.java index cb85e1196d..f80efa471e 100644 --- a/src/test/java/api/support/APITests.java +++ b/src/test/java/api/support/APITests.java @@ -115,6 +115,7 @@ public abstract class APITests { protected final ResourceClient locationsClient = ResourceClient.forLocations(); protected final ResourceClient configClient = ResourceClient.forConfiguration(); + protected final ResourceClient settingsClient = ResourceClient.forSettingsStorage(); private final ResourceClient patronGroupsClient = ResourceClient.forPatronGroups(); diff --git a/src/test/java/api/support/builders/InstanceBuilder.java b/src/test/java/api/support/builders/InstanceBuilder.java index b849c16a34..0d1075d20a 100644 --- a/src/test/java/api/support/builders/InstanceBuilder.java +++ b/src/test/java/api/support/builders/InstanceBuilder.java @@ -22,13 +22,14 @@ public class InstanceBuilder extends JsonBuilder implements Builder { private final List> identifiers; private final JsonArray publication; private final JsonArray editions; + private final List> series; public InstanceBuilder(String title, UUID instanceTypeId) { this(UUID.randomUUID(), title, instanceTypeId); } public InstanceBuilder(UUID id, String title, UUID instanceTypeId) { - this(id, title, new JsonArray(), instanceTypeId, Collections.emptyList(), new JsonArray(), new JsonArray()); + this(id, title, new JsonArray(), instanceTypeId, Collections.emptyList(), new JsonArray(), new JsonArray(), Collections.emptyList()); } private InstanceBuilder(UUID id, @@ -37,7 +38,8 @@ private InstanceBuilder(UUID id, UUID instanceTypeId, List> identifiers, JsonArray publication, - JsonArray editions) { + JsonArray editions, + List> series) { this.id = id; this.title = title; @@ -46,6 +48,7 @@ private InstanceBuilder(UUID id, this.identifiers = identifiers; this.editions = editions; this.publication = publication; + this.series = series; } @Override @@ -61,6 +64,7 @@ public JsonObject create() { put(instance, "identifiers", identifiersToJson()); put(instance, "editions", editions); put(instance, "publication", publication); + put(instance, "series", seriesToJson()); return instance; } @@ -81,7 +85,8 @@ public InstanceBuilder withId(UUID id) { this.instanceTypeId, this.identifiers, this.publication, - this.editions); + this.editions, + this.series); } public Builder withInstanceTypeId(UUID instanceTypeId) { @@ -92,7 +97,8 @@ public Builder withInstanceTypeId(UUID instanceTypeId) { instanceTypeId, this.identifiers, this.publication, - this.editions); + this.editions, + this.series); } public InstanceBuilder withContributor(String name, UUID typeId) { @@ -117,7 +123,8 @@ private InstanceBuilder withContributors(JsonArray contributors) { this.instanceTypeId, this.identifiers, this.publication, - this.editions); + this.editions, + this.series); } public InstanceBuilder withSinglePublication(String publisher, String place, String dateOfPublication) { @@ -136,7 +143,8 @@ private InstanceBuilder withPublication(JsonArray publication) { this.instanceTypeId, this.identifiers, publication, - this.editions); + this.editions, + this.series); } public InstanceBuilder withSingleEdition(String edition) { @@ -151,7 +159,30 @@ private InstanceBuilder withEditions(JsonArray editions) { this.instanceTypeId, this.identifiers, this.publication, - editions); + editions, + this.series); + } + + public InstanceBuilder addSeriesStatement(UUID authorityId, String value) { + List> seriesCopy = new ArrayList<>(series); + seriesCopy.add(new ImmutablePair<>(authorityId, value)); + return new InstanceBuilder( + this.id, + this.title, + this.contributors, + this.instanceTypeId, + this.identifiers, + this.publication, + this.editions, + seriesCopy); + } + + private JsonArray seriesToJson() { + return new JsonArray(series.stream() + .map(pair -> new JsonObject() + .put("authorityId", pair.getKey().toString()) + .put("value", pair.getValue())) + .collect(Collectors.toList())); } public InstanceBuilder addIdentifier(UUID typeId, String value) { @@ -165,6 +196,7 @@ public InstanceBuilder addIdentifier(UUID typeId, String value) { this.instanceTypeId, identifiersCopy, this.publication, - this.editions); + this.editions, + this.series); } } diff --git a/src/test/java/api/support/fixtures/ConfigurationExample.java b/src/test/java/api/support/fixtures/ConfigurationExample.java index d7485a8e0a..f9eee1e5bf 100644 --- a/src/test/java/api/support/fixtures/ConfigurationExample.java +++ b/src/test/java/api/support/fixtures/ConfigurationExample.java @@ -1,38 +1,15 @@ package api.support.fixtures; -import static org.folio.circulation.support.json.JsonPropertyWriter.write; - import api.support.builders.ConfigRecordBuilder; import api.support.builders.PrintHoldRequestConfigurationBuilder; -import io.vertx.core.json.JsonObject; public class ConfigurationExample { - private static final String DEFAULT_TIME_ZONE_MODULE_NAME = "ORG"; - private static final String DEFAULT_TIME_ZONE_CONFIG_NAME = "localeSettings"; - private static final String US_LOCALE = "en-US"; private static final String DEFAULT_NOTIFICATION_SCHEDULER_MODULE_NAME = "NOTIFICATION_SCHEDULER"; private static final String DEFAULT_NOTIFICATION_SCHEDULER_CONFIG_NAME = "noticesLimit"; private ConfigurationExample() { } - public static ConfigRecordBuilder utcTimezoneConfiguration() { - return timezoneConfigurationFor("UTC"); - } - - public static ConfigRecordBuilder newYorkTimezoneConfiguration() { - return timezoneConfigurationFor("America/New_York"); - } - - public static ConfigRecordBuilder timezoneConfigurationFor(String timezone) { - return getLocaleAndTimeZoneConfiguration(timezone); - } - - private static ConfigRecordBuilder getLocaleAndTimeZoneConfiguration(String timezone) { - return new ConfigRecordBuilder(DEFAULT_TIME_ZONE_MODULE_NAME, DEFAULT_TIME_ZONE_CONFIG_NAME, - combinedTimeZoneConfig(timezone).encodePrettily()); - } - public static ConfigRecordBuilder schedulerNoticesLimitConfiguration(String limit){ return new ConfigRecordBuilder(DEFAULT_NOTIFICATION_SCHEDULER_MODULE_NAME, DEFAULT_NOTIFICATION_SCHEDULER_CONFIG_NAME, limit); @@ -46,11 +23,4 @@ public static ConfigRecordBuilder setPrintHoldRequestsEnabled(boolean enabled) { .encodePrettily()); } - private static JsonObject combinedTimeZoneConfig(String timezone) { - final JsonObject encodedValue = new JsonObject(); - write(encodedValue, "locale", US_LOCALE); - write(encodedValue, "timezone", timezone); - return encodedValue; - } - } diff --git a/src/test/java/api/support/fixtures/InstanceExamples.java b/src/test/java/api/support/fixtures/InstanceExamples.java index b4b3b71160..5cd0433b83 100644 --- a/src/test/java/api/support/fixtures/InstanceExamples.java +++ b/src/test/java/api/support/fixtures/InstanceExamples.java @@ -13,7 +13,8 @@ public static InstanceBuilder basedUponSmallAngryPlanet( booksInstanceTypeId) .withContributor("Chambers, Becky", personalContributorNameTypeId, true) .withSingleEdition("First American Edition") - .withSinglePublication("Alfred A. Knopf", "New York", "2016"); + .withSinglePublication("Alfred A. Knopf", "New York", "2016") + .addSeriesStatement(UUID.randomUUID(),"Small, Angry Planet not part of any series"); } public static InstanceBuilder basedUponNod( diff --git a/src/test/java/api/support/fixtures/SettingsFixture.java b/src/test/java/api/support/fixtures/SettingsFixture.java index 41220f073d..77eb5ceb65 100644 --- a/src/test/java/api/support/fixtures/SettingsFixture.java +++ b/src/test/java/api/support/fixtures/SettingsFixture.java @@ -10,6 +10,9 @@ public class SettingsFixture { private static final UUID GENERAL_TLR_SETTINGS_ID = UUID.randomUUID(); private static final UUID REGULAR_TLR_SETTINGS_ID = UUID.randomUUID(); + private static final String DEFAULT_TIME_ZONE_SCOPE = "stripes-core.prefs.manage"; + private static final String DEFAULT_TIME_ZONE_KEY = "tenantLocaleSettings"; + private static final String US_LOCALE = "en-US"; private final ResourceClient settingsClient; @@ -31,6 +34,27 @@ private SettingsBuilder buildCheckoutLockFeatureSettings(boolean checkoutFeature ); } + public static SettingsBuilder utcTimezoneConfiguration() { + return timezoneConfigurationFor("UTC"); + } + + public static SettingsBuilder newYorkTimezoneConfiguration() { + return timezoneConfigurationFor("America/New_York"); + } + + public static SettingsBuilder timezoneConfigurationFor(String timezone) { + return getLocaleAndTimeZoneConfiguration(timezone); + } + + private static SettingsBuilder getLocaleAndTimeZoneConfiguration(String timezone) { + return new SettingsBuilder(UUID.randomUUID(), DEFAULT_TIME_ZONE_SCOPE, DEFAULT_TIME_ZONE_KEY, + new JsonObject().put("locale", US_LOCALE) + .put("timezone", timezone) + .put("currency", "USD") + .encodePrettily() + ); + } + public void enableTlrFeature() { createGeneralTlrSettings(true, false); } diff --git a/src/test/java/org/folio/circulation/domain/ConfigurationServiceTest.java b/src/test/java/org/folio/circulation/domain/ConfigurationServiceTest.java index 1fdcfc695a..986c89e399 100644 --- a/src/test/java/org/folio/circulation/domain/ConfigurationServiceTest.java +++ b/src/test/java/org/folio/circulation/domain/ConfigurationServiceTest.java @@ -1,23 +1,16 @@ package org.folio.circulation.domain; -import static java.time.ZoneOffset.UTC; import static org.folio.circulation.support.json.JsonPropertyWriter.write; import static org.junit.jupiter.api.Assertions.assertEquals; -import java.time.ZoneId; import java.util.Collections; import java.util.List; - import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; - -import api.support.builders.ConfigRecordBuilder; -import api.support.builders.ConfigurationBuilder; import io.vertx.core.json.JsonObject; class ConfigurationServiceTest { - private static final String US_LOCALE = "en-US"; private static final String VALUE = "value"; private static final Integer DEFAULT_TIMEOUT_CONFIGURATION = 3; @@ -29,46 +22,6 @@ public static void before() { service = new ConfigurationService(); } - @Test - void testUtcTimeZone() { - String zone = "UTC"; - String timeZoneValue = getTimezoneValue(zone); - JsonObject jsonObject = getJsonObject(timeZoneValue); - - assertEquals(ZoneId.of(zone), service.findDateTimeZone(jsonObject)); - } - - @Test - void testEuropeTimeZone() { - String zone = "Europe/Kiev"; - String timeZoneValue = getTimezoneValue(zone); - JsonObject jsonObject = getJsonObject(timeZoneValue); - - assertEquals(ZoneId.of(zone), service.findDateTimeZone(jsonObject)); - } - - @Test - void testEmptyTimeZoneValue() { - String timeZoneValue = getTimezoneValue(""); - JsonObject jsonObject = getJsonObject(timeZoneValue); - - assertEquals(UTC, service.findDateTimeZone(jsonObject)); - } - - @Test - void testEmptyJsonValue() { - JsonObject jsonObject = getJsonObject(""); - - assertEquals(UTC, service.findDateTimeZone(jsonObject)); - } - - @Test - void testEmptyJson() { - JsonObject jsonObject = new JsonObject(); - - assertEquals(UTC, service.findDateTimeZone(jsonObject)); - } - @Test void shouldUseConfiguredCheckoutTimeoutDurationWhenAnInteger() { JsonObject jsonConfig = new JsonObject() @@ -102,18 +55,6 @@ void shouldUseDefaultCheckoutTimeoutDurationWhenConfiguredValueIsNotAnInteger() assertEquals(DEFAULT_TIMEOUT_CONFIGURATION, actualSessionTimeout); } - private JsonObject getJsonObject(String timeZoneValue) { - ConfigRecordBuilder config = new ConfigRecordBuilder(timeZoneValue); - return new ConfigurationBuilder(Collections.singletonList(config)).create(); - } - - private String getTimezoneValue(String timezone) { - final JsonObject encodedValue = new JsonObject(); - write(encodedValue, "locale", US_LOCALE); - write(encodedValue, "timezone", timezone); - return encodedValue.toString(); - } - private String getJsonConfigWithCheckoutTimeoutDurationAsString(String checkoutTimeoutDuration) { final JsonObject encodedValue = new JsonObject(); write(encodedValue, "checkoutTimeoutDuration", checkoutTimeoutDuration); diff --git a/src/test/java/org/folio/circulation/domain/InstanceTests.java b/src/test/java/org/folio/circulation/domain/InstanceTests.java index 29f3328029..d6d1443e01 100644 --- a/src/test/java/org/folio/circulation/domain/InstanceTests.java +++ b/src/test/java/org/folio/circulation/domain/InstanceTests.java @@ -10,21 +10,21 @@ class InstanceTests { @Test void cannotHaveANullCollectionOfIdentifiers() { - assertThrows(NullPointerException.class, () -> new Instance(UUID.randomUUID().toString(), "1234", "Title", null, emptyList(), emptyList(), emptyList(), emptyList())); + assertThrows(NullPointerException.class, () -> new Instance(UUID.randomUUID().toString(), "1234", "Title", null, emptyList(), emptyList(), emptyList(), emptyList(), emptyList())); } @Test void cannotHaveANullCollectionOfContributors() { - assertThrows(NullPointerException.class, () -> new Instance(UUID.randomUUID().toString(), "1234", "Title", emptyList(), null, emptyList(), emptyList(), emptyList())); + assertThrows(NullPointerException.class, () -> new Instance(UUID.randomUUID().toString(), "1234", "Title", emptyList(), null, emptyList(), emptyList(), emptyList(), emptyList())); } @Test void cannotHaveANullCollectionOfPublication() { - assertThrows(NullPointerException.class, () -> new Instance(UUID.randomUUID().toString(), "1234", "Title", emptyList(), emptyList(), null, emptyList(), emptyList())); + assertThrows(NullPointerException.class, () -> new Instance(UUID.randomUUID().toString(), "1234", "Title", emptyList(), emptyList(), null, emptyList(), emptyList(), emptyList())); } @Test void cannotHaveANullCollectionOfEditions() { - assertThrows(NullPointerException.class, () -> new Instance(UUID.randomUUID().toString(), "1234", "Title", emptyList(), emptyList(), emptyList(), null, emptyList())); + assertThrows(NullPointerException.class, () -> new Instance(UUID.randomUUID().toString(), "1234", "Title", emptyList(), emptyList(), emptyList(), null, emptyList(), emptyList())); } } diff --git a/src/test/java/org/folio/circulation/domain/OverdueFineServiceTest.java b/src/test/java/org/folio/circulation/domain/OverdueFineServiceTest.java index 69fe68b714..05142869ba 100644 --- a/src/test/java/org/folio/circulation/domain/OverdueFineServiceTest.java +++ b/src/test/java/org/folio/circulation/domain/OverdueFineServiceTest.java @@ -615,7 +615,7 @@ private Item createItem() { .withLocation(new Location(null, LOCATION_NAME, null, null, emptyList(), SERVICE_POINT_ID, false, Institution.unknown(), Campus.unknown(), Library.unknown(), ServicePoint.unknown())) - .withInstance(new Instance(UUID.randomUUID().toString(), INSTANCE_HRID, TITLE, emptyList(), contributors, emptyList(), emptyList(), emptyList())) + .withInstance(new Instance(UUID.randomUUID().toString(), INSTANCE_HRID, TITLE, emptyList(), contributors, emptyList(), emptyList(), emptyList(), emptyList())) .withMaterialType(new MaterialType(ITEM_MATERIAL_TYPE_ID.toString(), ITEM_MATERIAL_TYPE_NAME, null)); } diff --git a/src/test/java/org/folio/circulation/domain/RequestRepresentationTests.java b/src/test/java/org/folio/circulation/domain/RequestRepresentationTests.java index f3fca57fd1..286801e581 100644 --- a/src/test/java/org/folio/circulation/domain/RequestRepresentationTests.java +++ b/src/test/java/org/folio/circulation/domain/RequestRepresentationTests.java @@ -132,7 +132,8 @@ private Request createMockRequest() { emptyList(), List.of(new Publication("fake publisher", "fake place", "2016", null)), List.of("First American Edition"), - List.of("Hardback")); + List.of("Hardback"), + emptyList()); JsonObject itemJson = new JsonObject() .put("effectiveCallNumberComponents", new JsonObject() diff --git a/src/test/java/org/folio/circulation/infrastructure/storage/SettingsRepositoryTest.java b/src/test/java/org/folio/circulation/infrastructure/storage/SettingsRepositoryTest.java index ff17621af8..7679baeefa 100644 --- a/src/test/java/org/folio/circulation/infrastructure/storage/SettingsRepositoryTest.java +++ b/src/test/java/org/folio/circulation/infrastructure/storage/SettingsRepositoryTest.java @@ -12,6 +12,7 @@ import org.folio.circulation.support.results.Result; import org.junit.jupiter.api.Test; +import java.time.ZoneId; import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -102,6 +103,36 @@ void fetchTlrSettings() { verifyNoInteractions(configurationClient); } + @Test + @SneakyThrows + void fetchTenantLocaleSettings() { + Clients clients = mock(Clients.class); + CollectionResourceClient settingsClient = mock(CollectionResourceClient.class); + + JsonObject mockSettingsResponse = new JsonObject() + .put("items", new JsonArray() + .add(new JsonObject() + .put("id", UUID.randomUUID().toString()) + .put("scope", "stripes-core.prefs.manage") + .put("key", "tenantLocaleSettings") + .put("value", "{\"locale\":\"en-US\",\"timezone\":\"Europe/Berlin\",\"currency\":\"USD\"}"))) + .put("resultInfo", new JsonObject() + .put("totalRecords", 1) + .put("diagnostics", new JsonArray())); + + when(clients.settingsStorageClient()).thenReturn(settingsClient); + when(settingsClient.getMany(any(), any())) + .thenReturn(ofAsync(new Response(200, mockSettingsResponse.encode(), "application/json"))); + + ZoneId actualResult = new SettingsRepository(clients) + .lookupTimeZoneSettings() + .get(30, TimeUnit.SECONDS) + .value(); + + assertEquals(ZoneId.of("Europe/Berlin"), actualResult); + verify(settingsClient).getMany(any(), any()); + } + @Test @SneakyThrows void fallBackToLegacyConfigurationWhenTlrSettingsAreNotFound() {