Skip to content

Commit

Permalink
[MODORDERS-1046] - Retrieve holdings from member tenants for Restrict…
Browse files Browse the repository at this point in the history
… by Locations feature
  • Loading branch information
imerabishvili committed Apr 8, 2024
1 parent b25126d commit 1fa0360
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import io.vertx.core.Future;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.map.CaseInsensitiveMap;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.okapi.common.GenericCompositeFuture;
import org.folio.okapi.common.XOkapiHeaders;
import org.folio.orders.utils.FundDistributionUtils;
import org.folio.rest.acq.model.finance.Fund;
import org.folio.rest.core.exceptions.ErrorCodes;
Expand All @@ -30,6 +34,7 @@
import org.folio.rest.jaxrs.model.Location;
import org.folio.rest.jaxrs.model.Parameter;
import org.folio.rest.jaxrs.model.PieceCollection;
import org.folio.rest.tools.utils.TenantTool;
import org.folio.service.finance.FundService;
import org.folio.service.finance.expenceclass.ExpenseClassValidationService;
import org.folio.service.finance.transaction.EncumbranceWorkflowStrategyFactory;
Expand Down Expand Up @@ -146,14 +151,14 @@ private Future<Void> validateLocationRestrictions(CompositePoLine poLine, List<F
Set<Fund> restrictedFunds = funds.stream().filter(Fund::getRestrictByLocations).collect(Collectors.toSet());

if (restrictedFunds.isEmpty()) {
logger.info("validateLocationRestrictions:: Funds is not restricted by locations");
logger.info("validateLocationRestrictions:: no funds found restricted by locations");
return Future.succeededFuture();
}

return extractRestrictedLocationIds(poLine, restrictedFunds, requestContext)
.compose(restrictedLocations -> {
if (restrictedLocations.isEmpty()) {
logger.info("validateLocationRestrictions:: restricted locations is not found for poLineId '{}'", poLine.getId());
logger.info("validateLocationRestrictions:: no restricted locations found for poLineId '{}'", poLine.getId());
return Future.succeededFuture();
}

Expand All @@ -173,43 +178,78 @@ private Future<Void> validateLocationRestrictions(CompositePoLine poLine, List<F
* The method checking fund location against valid locations and holding locations in POL to identify restricted locations
* <br> if there is one, will be stored to put in error as parameter.
* <br> otherwise, order can be opened
* @param poLine poLine of order that requested to be open
*
* @param poLine poLine of order that requested to be open
* @param restrictedFunds restricted funds of poLine
* @param requestContext requestContext
* @param requestContext requestContext
* @return Restricted locations
*/
private Future<Set<String>> extractRestrictedLocationIds(CompositePoLine poLine, Set<Fund> restrictedFunds, RequestContext requestContext) {
Set<String> validLocationIds = poLine.getLocations().stream().map(Location::getLocationId).filter(Objects::nonNull).collect(Collectors.toSet());
List<String> holdingIds = poLine.getLocations().stream().map(Location::getHoldingId).filter(Objects::nonNull).toList();

return getPermanentLocationIdsFromHoldings(holdingIds, requestContext)
.map(permanentLocationIds -> {
logger.info("extractRestrictedLocations:: '{}' restrictedFund(s) is being checked against permanentLocationIds: {} and validLocations: {}",
restrictedFunds.size(), permanentLocationIds, validLocationIds);
validLocationIds.addAll(permanentLocationIds);
Set<String> restrictedLocationsIds = new HashSet<>();
for (var fund : restrictedFunds) {
List<String> restrictedFundLocationIds = fund.getLocationIds().stream().filter(locationId -> !validLocationIds.contains(locationId)).toList();
if (restrictedFundLocationIds.size() == fund.getLocationIds().size()) {

String currentTenantId = TenantTool.tenantId(requestContext.getHeaders());

// 1. locations for current tenant
var locations = poLine.getLocations()
.stream()
.map(Location::getLocationId)
.filter(Objects::nonNull)
.map(location -> getTenantLocation(currentTenantId, location))
.toList();

// 2. locations from holdings
Map<String, List<String>> holdingsByTenant = poLine.getLocations()
.stream()
.collect(Collectors.groupingBy(
location -> ObjectUtils.defaultIfNull(location.getTenantId(), currentTenantId),
Collectors.mapping(Location::getHoldingId, Collectors.filtering(Objects::nonNull, Collectors.toList()))
));

List<Future<List<String>>> futures = holdingsByTenant.entrySet()
.stream()
.map(entry -> getLocationsFromHoldings(entry.getKey(), entry.getValue(), requestContext))
.toList();

return GenericCompositeFuture.all(futures).map(ar -> {
// 3. join locations from holdings
var locationsFromHoldings = futures.stream()
.map(Future::result)
.flatMap(List::stream)
.toList();

logger.info("extractRestrictedLocations:: '{}' restrictedFund(s) is being checked against locations: {} and locationsFromHoldings: {}",
restrictedFunds.size(), locations, locationsFromHoldings);

// 4. join all locations and check every fund against allowed locations
var allowedLocations = CollectionUtils.union(locations, locationsFromHoldings);

Set<String> restrictedLocationsIds = new HashSet<>();
for (var fund : restrictedFunds) {
List<String> fundLocations = fund.getLocations()
.stream()
.map(location -> getTenantLocation(ObjectUtils.defaultIfNull(location.getTenantId(), currentTenantId), location.getLocationId()))
.toList();
if (fundLocations.stream().anyMatch(allowedLocations::contains)) {
logger.info("extractRestrictedLocationIds:: Fund '{}' has valid location (at least one)", fund.getId());
} else {
restrictedLocationsIds.addAll(fundLocations);
logger.info("extractRestrictedLocationIds:: restrictedFundLocationIds: {} for fund: {}", restrictedLocationsIds, fund.getId());
restrictedLocationsIds.addAll(restrictedFundLocationIds);
}
logger.info("extractRestrictedLocationIds:: Fund '{}' has valid location (at leas one)", fund.getId());
}
return restrictedLocationsIds;
});
}
return restrictedLocationsIds;
});
}

private Future<List<String>> getPermanentLocationIdsFromHoldings(List<String> holdingIds, RequestContext requestContext) {
List<String> permanentLocationIds = new ArrayList<>();
private Future<List<String>> getLocationsFromHoldings(String tenantId, List<String> holdingIds, RequestContext requestContext) {
List<String> result = new ArrayList<>();

if (holdingIds.isEmpty()) {
return Future.succeededFuture(permanentLocationIds);
return Future.succeededFuture(result);
}

return inventoryManager.getHoldingsByIds(holdingIds, requestContext)
.map(holdings -> holdings.stream()
.map(holding -> holding.getString(HOLDING_PERMANENT_LOCATION_ID))
.map(locationId -> getTenantLocation(tenantId, locationId))
.toList())
.onFailure(failure -> {
logger.error("Couldn't retrieve holdings", failure);
Expand All @@ -219,4 +259,14 @@ private Future<List<String>> getPermanentLocationIdsFromHoldings(List<String> ho
});
}

private static String getTenantLocation(String tenantId, String locationId) {
return tenantId + "." + locationId;
}

private static RequestContext createContextWithNewTenantId(RequestContext requestContext, String tenantId) {
var modifiedHeaders = new CaseInsensitiveMap<>(requestContext.getHeaders());
modifiedHeaders.put(XOkapiHeaders.TENANT, tenantId);
return new RequestContext(requestContext.getContext(), modifiedHeaders);
}

}
Loading

0 comments on commit 1fa0360

Please sign in to comment.