Skip to content

Commit

Permalink
[MODORDERS-1120] - Support populating searchLocationId for correct te…
Browse files Browse the repository at this point in the history
…nant during order opening (#957)

[MODORDERS-1120] - Support populating searchLocationId for correct tenant during order opening
  • Loading branch information
imerabishvili authored Jun 5, 2024
1 parent fedc3a9 commit e9a95a5
Show file tree
Hide file tree
Showing 11 changed files with 316 additions and 152 deletions.
2 changes: 1 addition & 1 deletion ramls/acq-models
Submodule acq-models updated 0 files
11 changes: 2 additions & 9 deletions src/main/java/org/folio/helper/PurchaseOrderLineHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import static org.folio.helper.BaseHelper.EVENT_PAYLOAD;
import static org.folio.helper.BaseHelper.ORDER_ID;
import static org.folio.orders.utils.HelperUtils.calculateEstimatedPrice;
import static org.folio.orders.utils.HelperUtils.convertToPoLine;
import static org.folio.orders.utils.HelperUtils.getPoLineLimit;
import static org.folio.orders.utils.PoLineCommonUtil.verifyProtectedFieldsChanged;
import static org.folio.orders.utils.ProtectedOperationType.DELETE;
Expand Down Expand Up @@ -224,7 +223,7 @@ public Future<CompositePoLine> createPoLineWithOrder(CompositePoLine compPoLine,
return GenericCompositeFuture.join(new ArrayList<>(subObjFuts))
.compose(v -> generateLineNumber(compOrder, requestContext))
.map(lineNumber -> line.put(PO_LINE_NUMBER, lineNumber))
.compose(v -> updateSearchLocations(compPoLine, requestContext))
.compose(v -> purchaseOrderLineService.updateSearchLocations(compPoLine, requestContext))
.map(v -> line.put(SEARCH_LOCATION_IDS, compPoLine.getSearchLocationIds()))
.compose(v -> createPoLineSummary(compPoLine, line, requestContext));
}
Expand Down Expand Up @@ -355,7 +354,7 @@ private boolean isUnreleasedEncumbrances(CompositePoLine compOrderLine, PoLine p
public Future<Void> updateOrderLine(CompositePoLine compOrderLine, JsonObject lineFromStorage, RequestContext requestContext) {
Promise<Void> promise = Promise.promise();

updateSearchLocations(compOrderLine, requestContext)
purchaseOrderLineService.updateSearchLocations(compOrderLine, requestContext)
.compose(v -> purchaseOrderLineService.updatePoLineSubObjects(compOrderLine, lineFromStorage, requestContext))
.compose(poLine -> purchaseOrderLineService.updateOrderLineSummary(compOrderLine.getId(), poLine, requestContext))
.onSuccess(json -> promise.complete())
Expand Down Expand Up @@ -549,12 +548,6 @@ private List<Future<?>> processPoLinesUpdate(CompositePurchaseOrder compOrder, L
return futures;
}

private Future<Void> updateSearchLocations(CompositePoLine compositePoLine, RequestContext requestContext) {
return purchaseOrderLineService.retrieveSearchLocationIds(convertToPoLine(compositePoLine), requestContext)
.map(compositePoLine::withSearchLocationIds)
.mapEmpty();
}

private List<Future<CompositePoLine>> processPoLinesCreation(CompositePurchaseOrder compOrder, List<PoLine> poLinesFromStorage,
RequestContext requestContext) {
return getNewPoLines(compOrder, poLinesFromStorage)
Expand Down
128 changes: 73 additions & 55 deletions src/main/java/org/folio/orders/utils/HelperUtils.java
Original file line number Diff line number Diff line change
@@ -1,41 +1,14 @@
package org.folio.orders.utils;

import static io.vertx.core.Future.succeededFuture;
import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.folio.orders.utils.ResourcePathResolver.ALERTS;
import static org.folio.orders.utils.ResourcePathResolver.REPORTING_CODES;
import static org.folio.rest.RestConstants.EN;
import static org.folio.rest.core.exceptions.ErrorCodes.MULTIPLE_NONPACKAGE_TITLES;
import static org.folio.rest.core.exceptions.ErrorCodes.TITLE_NOT_FOUND;
import static org.folio.rest.jaxrs.model.PoLine.PaymentStatus.FULLY_PAID;
import static org.folio.rest.jaxrs.model.PoLine.PaymentStatus.PAYMENT_NOT_REQUIRED;
import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.FULLY_RECEIVED;
import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.RECEIPT_NOT_REQUIRED;
import static org.folio.service.exchange.ExchangeRateProviderResolver.RATE_KEY;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.money.CurrencyUnit;
import javax.money.Monetary;
import javax.money.MonetaryAmount;
import javax.money.convert.ConversionQuery;
import javax.money.convert.ConversionQueryBuilder;

import io.vertx.core.AsyncResult;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonObject;
import one.util.streamex.IntStreamEx;
import one.util.streamex.StreamEx;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.map.CaseInsensitiveMap;
import org.apache.commons.lang3.ArrayUtils;
Expand Down Expand Up @@ -65,15 +38,40 @@
import org.javamoney.moneta.function.MonetaryFunctions;
import org.javamoney.moneta.function.MonetaryOperators;

import io.vertx.core.AsyncResult;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonObject;
import one.util.streamex.IntStreamEx;
import one.util.streamex.StreamEx;
import javax.money.CurrencyUnit;
import javax.money.Monetary;
import javax.money.MonetaryAmount;
import javax.money.convert.ConversionQuery;
import javax.money.convert.ConversionQueryBuilder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static io.vertx.core.Future.succeededFuture;
import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.folio.orders.utils.ResourcePathResolver.ALERTS;
import static org.folio.orders.utils.ResourcePathResolver.REPORTING_CODES;
import static org.folio.rest.RestConstants.EN;
import static org.folio.rest.core.exceptions.ErrorCodes.MULTIPLE_NONPACKAGE_TITLES;
import static org.folio.rest.core.exceptions.ErrorCodes.TITLE_NOT_FOUND;
import static org.folio.rest.jaxrs.model.PoLine.PaymentStatus.FULLY_PAID;
import static org.folio.rest.jaxrs.model.PoLine.PaymentStatus.PAYMENT_NOT_REQUIRED;
import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.FULLY_RECEIVED;
import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.RECEIPT_NOT_REQUIRED;
import static org.folio.service.exchange.ExchangeRateProviderResolver.RATE_KEY;

public class HelperUtils {

Expand Down Expand Up @@ -189,7 +187,7 @@ public static int calculateInventoryItemsQuantity(CompositePoLine compPOL, List<
/**
* Calculates pieces quantity for list of locations and return map where piece format is a key and corresponding quantity of pieces as value.
*
* @param compPOL composite PO Line
* @param compPOL composite PO Line
* @param locations list of locations to calculate quantity for
* @return quantity of pieces per piece format either required Inventory item for PO Line
*/
Expand Down Expand Up @@ -231,7 +229,7 @@ public static Map<Piece.Format, Integer> calculatePiecesWithItemIdQuantity(Compo
/**
* Calculates pieces quantity for specified locations based on piece format.
*
* @param format piece format
* @param format piece format
* @param locations list of locations to calculate quantity for
* @return quantity of items expected in the inventory for PO Line
*/
Expand All @@ -244,6 +242,7 @@ public static int calculatePiecesQuantity(Piece.Format format, List<Location> lo

/**
* Calculates total estimated price. See MODORDERS-180 for more details.
*
* @param cost PO Line's cost
*/
public static MonetaryAmount calculateEstimatedPrice(Cost cost) {
Expand Down Expand Up @@ -318,19 +317,34 @@ public static int getElectronicLocationsQuantity(List<Location> locations) {
.sum();
}

/**
* Almost same as collectResultsOnSuccess with addition that each future itself returns a list and
* this implementation combines all elements of all lists into the single list.
* @param futures The list of futures to be combined
* @return Single list containing all elements returned from all futures
* @param <E> element type
* @param <T> list type
*/
public static <E, T extends List<E>> Future<List<E>> combineResultListsOnSuccess(Collection<Future<T>> futures) {
return collectResultsOnSuccess(futures)
.map(lists -> lists.stream().flatMap(List::stream).toList());
}

/**
* Wait for all requests completion and collect all resulting objects. In case any failed, complete resulting future with the exception
*
* @param futures list of futures and each produces resulting object on completion
* @param <T> resulting type
* @param <T> resulting type
* @return resulting objects
*/
public static <T> Future<List<T>> collectResultsOnSuccess(List<Future<T>> futures) {
public static <T> Future<List<T>> collectResultsOnSuccess(Collection<Future<T>> futures) {
return GenericCompositeFuture.join(new ArrayList<>(futures))
.map(CompositeFuture::list);
}

/**
* Transform list of id's to CQL query using 'or' operation
*
* @param ids list of id's
* @return String representing CQL query to get records by id's
*/
Expand All @@ -344,8 +358,9 @@ public static String convertIdsToCqlQuery(Collection<String> ids) {

/**
* Transform list of values for some property to CQL query using 'or' operation
* @param values list of field values
* @param fieldName the property name to search by
*
* @param values list of field values
* @param fieldName the property name to search by
* @param strictMatch indicates whether strict match mode (i.e. ==) should be used or not (i.e. =)
* @return String representing CQL query to get records by some property values
*/
Expand All @@ -365,6 +380,7 @@ public static int getPoLineLimit(JsonObject config) {
/**
* Convert {@link JsonObject} which actually represents org.folio.rest.acq.model.PurchaseOrder to {@link CompositePurchaseOrder}
* These objects are the same except PurchaseOrder doesn't contain poLines field.
*
* @param poJson {@link JsonObject} representing org.folio.rest.acq.model.PurchaseOrder
* @return {@link CompositePurchaseOrder}
*/
Expand Down Expand Up @@ -450,23 +466,24 @@ public static boolean isProductIdsExist(CompositePoLine compPOL) {
}

public static Void handleErrorResponse(Handler<AsyncResult<javax.ws.rs.core.Response>> asyncResultHandler, BaseHelper helper,
Throwable t) {
Throwable t) {
asyncResultHandler.handle(succeededFuture(helper.buildErrorResponse(t)));
return null;
}

/**
* Check the number of titles per po line.
*
* @param lineIdTitles Map po line id -> list of titles
* @param poLineById Map po line id -> composite po line
* @param poLineById Map po line id -> composite po line
*/
public static void verifyTitles(Map<String, List<Title>> lineIdTitles, Map<String, CompositePoLine> poLineById) {
verifyAllTitlesExist(lineIdTitles, poLineById);
verifyNonPackageLinesHaveSingleTitle(lineIdTitles, poLineById);
}

private static void verifyNonPackageLinesHaveSingleTitle(Map<String, List<Title>> titles,
Map<String, CompositePoLine> poLineById) {
Map<String, CompositePoLine> poLineById) {
if (titles.keySet().stream().anyMatch(lineId -> titles.get(lineId).size() > 1 && !poLineById.get(lineId).getIsPackage())) {
throw new HttpException(400, MULTIPLE_NONPACKAGE_TITLES);
}
Expand Down Expand Up @@ -508,6 +525,7 @@ public static ConversionQuery buildConversionQuery(PoLine poLine, String systemC
/**
* Accepts response with collection of the elements and tries to extract the first one.
* In case the response is incorrect or empty, the {@link CompletionException} will be thrown
*
* @param response {@link JsonObject} representing service response which should contain array of objects
* @param propertyName name of the property which holds array of objects
* @return the first element of the array
Expand All @@ -517,7 +535,7 @@ public static JsonObject getFirstObjectFromResponse(JsonObject response, String
.flatMap(items -> items.stream().findFirst())
.map(JsonObject.class::cast)
.orElseThrow(() -> new CompletionException(new NoInventoryRecordException(
String.format("No records of '%s' can be found", propertyName))));
String.format("No records of '%s' can be found", propertyName))));
}

public static String extractId(JsonObject json) {
Expand Down
64 changes: 64 additions & 0 deletions src/main/java/org/folio/orders/utils/StreamUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.folio.orders.utils;

import lombok.experimental.UtilityClass;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
* Utility class allowing to perform stream operations in a more convenient way
*/
@UtilityClass
public class StreamUtils {

public static <T> T find(Collection<T> collection, Predicate<T> predicate) {
for (T t : collection) {
if (predicate.test(t)) {
return t;
}
}
return null;
}

public static <T> List<T> filter(Collection<T> collection, Predicate<T> predicate) {
return collection.stream().filter(predicate).toList();
}

public static <K, V> List<V> map(Collection<K> collection, Function<K, V> mapper) {
return collection.stream().map(mapper).toList();
}

public static <K, V> Set<V> mapToSet(Collection<K> collection, Function<K, V> mapper) {
return collection.stream().map(mapper).collect(Collectors.toSet());
}

public static <T, K> Map<K, T> listToMap(Collection<T> collection, Function<T, K> toKey) {
return listToMap(collection, toKey, Function.identity());
}

public static <T, K, V> Map<K, V> listToMap(Collection<T> collection, Function<T, K> toKey, Function<T, V> toValue) {
return collection.stream().collect(Collectors.toMap(toKey, toValue, (a, b) -> a));
}

public static <T, K> Map<K, List<T>> groupBy(Collection<T> collection, Function<T, K> toKey) {
return groupBy(collection, toKey, Function.identity());
}

public static <T, K, V> Map<K, List<V>> groupBy(Collection<T> collection, Function<T, K> toKey, Function<T, V> toValue) {
Map<K, List<V>> map = new HashMap<>();
for (T t : collection) {
K key = toKey.apply(t);
List<V> sublist = map.computeIfAbsent(key, k -> new ArrayList<>());
sublist.add(toValue.apply(t));
}
return map;
}

}
Loading

0 comments on commit e9a95a5

Please sign in to comment.