Skip to content

Commit

Permalink
[EUREKA-561-4]. Add validateUserUnaffiliatedLocationUpdates method to…
Browse files Browse the repository at this point in the history
… Order Open/unopen (#1061)

* [EUREKA-561-3]. Add validateUserUnaffiliatedLocationUpdates method to Order Open/unopen

* [EUREKA-561-4]. Change method args & remove risky conversions

* [EUREKA-561-4]. Replace filtering with StringUtils.equals(), change args names
BKadirkhodjaev authored Dec 6, 2024
1 parent 6f79419 commit 8092d97
Showing 7 changed files with 147 additions and 120 deletions.
20 changes: 10 additions & 10 deletions src/main/java/org/folio/config/ApplicationConfig.java
Original file line number Diff line number Diff line change
@@ -712,14 +712,14 @@ PurchaseOrderHelper purchaseOrderHelper(PurchaseOrderLineHelper purchaseOrderLin
ProtectionService protectionService, InventoryItemStatusSyncService itemStatusSyncService,
OpenCompositeOrderManager openCompositeOrderManager, PurchaseOrderStorageService purchaseOrderStorageService,
ConfigurationEntriesCache configurationEntriesCache, PoNumberHelper poNumberHelper,
OpenCompositeOrderFlowValidator openCompositeOrderFlowValidator,
ReOpenCompositeOrderManager reOpenCompositeOrderManager, OrderValidationService orderValidationService) {
OpenCompositeOrderFlowValidator openCompositeOrderFlowValidator, ReOpenCompositeOrderManager reOpenCompositeOrderManager,
OrderValidationService orderValidationService, CompositePoLineValidationService compositePoLineValidationService) {
return new PurchaseOrderHelper(purchaseOrderLineHelper, orderLinesSummaryPopulateService, encumbranceService,
combinedPopulateService, encumbranceWorkflowStrategyFactory, orderInvoiceRelationService, tagService,
purchaseOrderLineService, titlesService, protectionService, itemStatusSyncService,
openCompositeOrderManager, purchaseOrderStorageService, configurationEntriesCache,
poNumberHelper, openCompositeOrderFlowValidator, reOpenCompositeOrderManager,
orderValidationService);
orderValidationService, compositePoLineValidationService);
}

@Bean
@@ -746,18 +746,18 @@ PurchaseOrderLineHelper purchaseOrderLineHelper(InventoryItemStatusSyncService i
PurchaseOrderStorageService purchaseOrderStorageService,
RestClient restClient,
CompositePoLineValidationService compositePoLineValidationService,
POLInvoiceLineRelationService polInvoiceLineRelationService,
OrganizationService organizationService,
ConsortiumConfigurationService consortiumConfigurationService,
ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever) {
OrganizationService organizationService) {
return new PurchaseOrderLineHelper(itemStatusSyncService, inventoryInstanceManager, encumbranceService, expenseClassValidationService,
encumbranceWorkflowStrategyFactory, orderInvoiceRelationService, titlesService, protectionService,
purchaseOrderLineService, purchaseOrderStorageService, restClient, compositePoLineValidationService,
organizationService, consortiumConfigurationService, consortiumUserTenantsRetriever);
organizationService);
}

@Bean CompositePoLineValidationService compositePoLineValidationService(ExpenseClassValidationService expenseClassValidationService) {
return new CompositePoLineValidationService(expenseClassValidationService);
@Bean
CompositePoLineValidationService compositePoLineValidationService(ExpenseClassValidationService expenseClassValidationService,
ConsortiumConfigurationService consortiumConfigurationService,
ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever) {
return new CompositePoLineValidationService(expenseClassValidationService, consortiumConfigurationService, consortiumUserTenantsRetriever);
}

@Bean TitleValidationService titleValidationService() {
28 changes: 25 additions & 3 deletions src/main/java/org/folio/helper/PurchaseOrderHelper.java
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@
import static org.folio.service.UserService.getCurrentUserId;
import static org.folio.service.orders.utils.StatusUtils.changeOrderStatusForOrderUpdate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -63,6 +64,7 @@
import org.folio.service.finance.transaction.EncumbranceWorkflowStrategyFactory;
import org.folio.service.inventory.InventoryItemStatusSyncService;
import org.folio.service.orders.CompositeOrderDynamicDataPopulateService;
import org.folio.service.orders.CompositePoLineValidationService;
import org.folio.service.orders.OrderInvoiceRelationService;
import org.folio.service.orders.OrderValidationService;
import org.folio.service.orders.OrderWorkflowType;
@@ -78,6 +80,7 @@
import io.vertx.core.json.JsonObject;

public class PurchaseOrderHelper {

private static final Logger logger = LogManager.getLogger(PurchaseOrderHelper.class);

private final PurchaseOrderLineHelper purchaseOrderLineHelper;
@@ -98,6 +101,7 @@ public class PurchaseOrderHelper {
private final PoNumberHelper poNumberHelper;
private final ReOpenCompositeOrderManager reOpenCompositeOrderManager;
private final OrderValidationService orderValidationService;
private final CompositePoLineValidationService compositePoLineValidationService;

public PurchaseOrderHelper(PurchaseOrderLineHelper purchaseOrderLineHelper,
CompositeOrderDynamicDataPopulateService orderLinesSummaryPopulateService, EncumbranceService encumbranceService,
@@ -109,7 +113,8 @@ public PurchaseOrderHelper(PurchaseOrderLineHelper purchaseOrderLineHelper,
OpenCompositeOrderManager openCompositeOrderManager, PurchaseOrderStorageService purchaseOrderStorageService,
ConfigurationEntriesCache configurationEntriesCache, PoNumberHelper poNumberHelper,
OpenCompositeOrderFlowValidator openCompositeOrderFlowValidator,
ReOpenCompositeOrderManager reOpenCompositeOrderManager, OrderValidationService orderValidationService) {
ReOpenCompositeOrderManager reOpenCompositeOrderManager, OrderValidationService orderValidationService,
CompositePoLineValidationService compositePoLineValidationService) {
this.purchaseOrderLineHelper = purchaseOrderLineHelper;
this.orderLinesSummaryPopulateService = orderLinesSummaryPopulateService;
this.encumbranceService = encumbranceService;
@@ -128,6 +133,7 @@ public PurchaseOrderHelper(PurchaseOrderLineHelper purchaseOrderLineHelper,
this.openCompositeOrderFlowValidator = openCompositeOrderFlowValidator;
this.reOpenCompositeOrderManager = reOpenCompositeOrderManager;
this.orderValidationService = orderValidationService;
this.compositePoLineValidationService = compositePoLineValidationService;
}

/**
@@ -224,9 +230,26 @@ public Future<Void> updateOrder(CompositePurchaseOrder compPO, boolean deleteHol
.map(HelperUtils::convertToCompositePurchaseOrder)
.compose(lines -> purchaseOrderLineService.populateOrderLines(lines, requestContext))
.compose(poFromStorage -> {
CompositePurchaseOrder clonedPoFromStorage = JsonObject.mapFrom(poFromStorage).mapTo(CompositePurchaseOrder.class);
boolean isTransitionToOpen = isTransitionToOpen(poFromStorage, compPO);
return orderValidationService.validateOrderForUpdate(compPO, poFromStorage, deleteHoldings, requestContext)
.compose(ok -> {
.compose(v -> {
var poLineFutures = new ArrayList<Future<Void>>();
if (!compPO.getCompositePoLines().isEmpty()) {
compPO.getCompositePoLines().forEach(poLine -> {
var compPoLineFromStorage = clonedPoFromStorage.getCompositePoLines().stream()
.filter(entry -> StringUtils.equals(entry.getId(), poLine.getId()))
.findFirst().orElse(null);
if (Objects.nonNull(compPoLineFromStorage)) {
var updatedLocations = poLine.getLocations();
var storedLocations = compPoLineFromStorage.getLocations();
poLineFutures.add(compositePoLineValidationService.validateUserUnaffiliatedLocationUpdates(poLine.getId(), updatedLocations, storedLocations, requestContext));
}
});
}
return collectResultsOnSuccess(poLineFutures);
})
.compose(v -> {
if (isTransitionToClosed(poFromStorage, compPO)) {
return closeOrder(compPO, poFromStorage, requestContext);
}
@@ -235,7 +258,6 @@ public Future<Void> updateOrder(CompositePurchaseOrder compPO, boolean deleteHol
.compose(v -> {
if (isTransitionToOpen) {
if (CollectionUtils.isEmpty(compPO.getCompositePoLines())) {
CompositePurchaseOrder clonedPoFromStorage = JsonObject.mapFrom(poFromStorage).mapTo(CompositePurchaseOrder.class);
compPO.setCompositePoLines(clonedPoFromStorage.getCompositePoLines());
}
compPO.getCompositePoLines().forEach(poLine -> PoLineCommonUtil.updateLocationsQuantity(poLine.getLocations()));
50 changes: 3 additions & 47 deletions src/main/java/org/folio/helper/PurchaseOrderLineHelper.java
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@
import static org.folio.orders.utils.HelperUtils.getPoLineLimit;
import static org.folio.orders.utils.PoLineCommonUtil.convertToCompositePoLine;
import static org.folio.orders.utils.PoLineCommonUtil.convertToPoLine;
import static org.folio.orders.utils.PoLineCommonUtil.extractUnaffiliatedLocations;
import static org.folio.orders.utils.PoLineCommonUtil.updateLocationsQuantity;
import static org.folio.orders.utils.PoLineCommonUtil.verifyProtectedFieldsChanged;
import static org.folio.orders.utils.ProtectedOperationType.DELETE;
@@ -42,7 +41,6 @@

import io.vertx.core.json.JsonArray;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -70,14 +68,11 @@
import org.folio.rest.jaxrs.model.Error;
import org.folio.rest.jaxrs.model.Errors;
import org.folio.rest.jaxrs.model.FundDistribution;
import org.folio.rest.jaxrs.model.Location;
import org.folio.rest.jaxrs.model.Physical;
import org.folio.rest.jaxrs.model.PoLine;
import org.folio.rest.jaxrs.model.PoLineCollection;
import org.folio.rest.jaxrs.model.ReportingCode;
import org.folio.service.ProtectionService;
import org.folio.service.consortium.ConsortiumConfigurationService;
import org.folio.service.consortium.ConsortiumUserTenantsRetriever;
import org.folio.service.finance.expenceclass.ExpenseClassValidationService;
import org.folio.service.finance.transaction.EncumbranceService;
import org.folio.service.finance.transaction.EncumbranceWorkflowStrategy;
@@ -97,8 +92,8 @@
import io.vertx.core.json.JsonObject;

public class PurchaseOrderLineHelper {
private static final Logger logger = LogManager.getLogger(PurchaseOrderLineHelper.class);

private static final Logger logger = LogManager.getLogger(PurchaseOrderLineHelper.class);

private static final Pattern PO_LINE_NUMBER_PATTERN = Pattern.compile("([a-zA-Z0-9]{1,22}-)(\\d{1,3})");
private static final String PURCHASE_ORDER_ID = "purchaseOrderId";
@@ -121,8 +116,6 @@ public class PurchaseOrderLineHelper {
private final RestClient restClient;
private final CompositePoLineValidationService compositePoLineValidationService;
private final OrganizationService organizationService;
private final ConsortiumConfigurationService consortiumConfigurationService;
private final ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever;

public PurchaseOrderLineHelper(InventoryItemStatusSyncService inventoryItemStatusSyncService,
InventoryInstanceManager inventoryInstanceManager,
@@ -136,9 +129,7 @@ public PurchaseOrderLineHelper(InventoryItemStatusSyncService inventoryItemStatu
PurchaseOrderStorageService purchaseOrderStorageService,
RestClient restClient,
CompositePoLineValidationService compositePoLineValidationService,
OrganizationService organizationService,
ConsortiumConfigurationService consortiumConfigurationService,
ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever) {
OrganizationService organizationService) {

this.itemStatusSyncService = inventoryItemStatusSyncService;
this.inventoryInstanceManager = inventoryInstanceManager;
@@ -153,8 +144,6 @@ public PurchaseOrderLineHelper(InventoryItemStatusSyncService inventoryItemStatu
this.restClient = restClient;
this.compositePoLineValidationService = compositePoLineValidationService;
this.organizationService = organizationService;
this.consortiumConfigurationService = consortiumConfigurationService;
this.consortiumUserTenantsRetriever = consortiumUserTenantsRetriever;
}

/**
@@ -289,7 +278,7 @@ public Future<Void> updateOrderLine(CompositePoLine compOrderLine, RequestContex
.compose(compOrder -> protectionService.isOperationRestricted(compOrder.getAcqUnitIds(), UPDATE, requestContext)
.compose(v -> purchaseOrderLineService.validateAndNormalizeISBNAndProductType(Collections.singletonList(compOrderLine), requestContext))
.compose(v -> validateAccessProviders(compOrderLine, requestContext))
.compose(v -> validateUserUnaffiliatedLocationUpdates(compOrderLine, poLineFromStorage, requestContext))
.compose(v -> compositePoLineValidationService.validateUserUnaffiliatedLocationUpdates(compOrderLine.getId(), compOrderLine.getLocations(), poLineFromStorage.getLocations(), requestContext))
.compose(v -> expenseClassValidationService.validateExpenseClassesForOpenedOrder(compOrder, Collections.singletonList(compOrderLine), requestContext))
.compose(v -> processPoLineEncumbrances(compOrder, compOrderLine, poLineFromStorage, requestContext)))
.map(v -> compOrderLine.withPoLineNumber(poLineFromStorage.getPoLineNumber())) // PoLine number must not be modified during PoLine update, set original value
@@ -733,39 +722,6 @@ private Future<Void> validateAccessProviders(CompositePoLine compOrderLine, Requ
.mapEmpty();
}

private Future<Void> validateUserUnaffiliatedLocationUpdates(CompositePoLine updatedPoLine, PoLine storedPoLine, RequestContext requestContext) {
return getUserTenantsIfNeeded(requestContext)
.compose(userTenants -> {
if (CollectionUtils.isEmpty(userTenants)) {
logger.info("validateUserUnaffiliatedLocationUpdates:: User tenants is empty");
return Future.succeededFuture();
}
var storageUnaffiliatedLocations = extractUnaffiliatedLocations(storedPoLine.getLocations(), userTenants);
var updatedUnaffiliatedLocations = extractUnaffiliatedLocations(updatedPoLine.getLocations(), userTenants);
logger.info("validateUserUnaffiliatedLocationUpdates:: Found unaffiliated POL location tenant ids, poLineId: '{}', stored: '{}', updated: '{}'",
updatedPoLine.getId(),
storageUnaffiliatedLocations.stream().map(Location::getTenantId).distinct().toList(),
updatedUnaffiliatedLocations.stream().map(Location::getTenantId).distinct().toList());
if (!SetUtils.isEqualSet(storageUnaffiliatedLocations, updatedUnaffiliatedLocations)) {
logger.info("validateUserUnaffiliatedLocationUpdates:: User is not affiliated with all locations on the POL, poLineId: '{}'",
updatedPoLine.getId());
return Future.failedFuture(new HttpException(422, ErrorCodes.LOCATION_UPDATE_WITHOUT_AFFILIATION));
}
logger.info("validateUserUnaffiliatedLocationUpdates:: User is affiliated with all locations on the POL, poLineId: '{}'",
updatedPoLine.getId());
return Future.succeededFuture();
});
}

private Future<List<String>> getUserTenantsIfNeeded(RequestContext requestContext) {
return consortiumConfigurationService.getConsortiumConfiguration(requestContext)
.compose(consortiumConfiguration ->
consortiumConfiguration
.map(configuration -> consortiumUserTenantsRetriever.getUserTenants(configuration.consortiumId(), configuration.centralTenantId(), requestContext))
.orElse(Future.succeededFuture())
);
}

private void validatePOLineProtectedFieldsChanged(CompositePoLine compOrderLine, PoLine poLineFromStorage, CompositePurchaseOrder purchaseOrder) {
if (purchaseOrder.getWorkflowStatus() != PENDING) {
verifyProtectedFieldsChanged(POLineProtectedFieldsUtil.getFieldNames(compOrderLine.getOrderFormat().value()), JsonObject.mapFrom(poLineFromStorage), JsonObject.mapFrom(compOrderLine));
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.folio.orders.utils.HelperUtils.calculateEstimatedPrice;
import static org.folio.orders.utils.PoLineCommonUtil.extractUnaffiliatedLocations;
import static org.folio.orders.utils.PoLineCommonUtil.getElectronicCostQuantity;
import static org.folio.orders.utils.PoLineCommonUtil.getPhysicalCostQuantity;
import static org.folio.orders.utils.PoLineCommonUtil.isHoldingUpdateRequiredForEresource;
@@ -23,9 +24,13 @@

import io.vertx.core.Future;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.orders.utils.HelperUtils;
import org.folio.rest.core.exceptions.ErrorCodes;
import org.folio.rest.core.exceptions.HttpException;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.jaxrs.model.CompositePoLine;
import org.folio.rest.jaxrs.model.Cost;
@@ -35,15 +40,25 @@
import org.folio.rest.jaxrs.model.Parameter;
import org.folio.rest.jaxrs.model.Physical;
import org.folio.rest.jaxrs.model.PoLine;
import org.folio.service.consortium.ConsortiumConfigurationService;
import org.folio.service.consortium.ConsortiumUserTenantsRetriever;
import org.folio.service.finance.expenceclass.ExpenseClassValidationService;


public class CompositePoLineValidationService extends BaseValidationService {

private static final Logger logger = LogManager.getLogger(CompositePoLineValidationService.class);

private final ExpenseClassValidationService expenseClassValidationService;
private final ConsortiumConfigurationService consortiumConfigurationService;
private final ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever;

public CompositePoLineValidationService(ExpenseClassValidationService expenseClassValidationService) {
public CompositePoLineValidationService(ExpenseClassValidationService expenseClassValidationService,
ConsortiumConfigurationService consortiumConfigurationService,
ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever) {
this.expenseClassValidationService = expenseClassValidationService;
this.consortiumConfigurationService = consortiumConfigurationService;
this.consortiumUserTenantsRetriever = consortiumUserTenantsRetriever;
}

public Future<List<Error>> validatePoLine(CompositePoLine compPOL, RequestContext requestContext) {
@@ -329,4 +344,38 @@ private void validateReceivingWorkflowForBindary(CompositePoLine poLine, List<Er
errors.add(RECEIVING_WORKFLOW_INCORRECT_FOR_BINDARY_ACTIVE.toError());
}
}

public Future<Void> validateUserUnaffiliatedLocationUpdates(String poLineId, List<Location> updatedPoLineLocations,
List<Location> storedPoLineLocations, RequestContext requestContext) {
return getUserTenantsIfNeeded(requestContext)
.compose(userTenants -> {
if (CollectionUtils.isEmpty(userTenants)) {
logger.info("validateUserUnaffiliatedLocationUpdates:: User tenants is empty");
return Future.succeededFuture();
}
var storageUnaffiliatedLocations = extractUnaffiliatedLocations(storedPoLineLocations, userTenants);
var updatedUnaffiliatedLocations = extractUnaffiliatedLocations(updatedPoLineLocations, userTenants);
logger.info("validateUserUnaffiliatedLocationUpdates:: Found unaffiliated POL location tenant ids, poLineId: '{}', stored: '{}', updated: '{}'",
poLineId,
storageUnaffiliatedLocations.stream().map(Location::getTenantId).distinct().toList(),
updatedUnaffiliatedLocations.stream().map(Location::getTenantId).distinct().toList());
if (!SetUtils.isEqualSet(storageUnaffiliatedLocations, updatedUnaffiliatedLocations)) {
logger.info("validateUserUnaffiliatedLocationUpdates:: User is not affiliated with all locations on the POL, poLineId: '{}'",
poLineId);
return Future.failedFuture(new HttpException(422, ErrorCodes.LOCATION_UPDATE_WITHOUT_AFFILIATION));
}
logger.info("validateUserUnaffiliatedLocationUpdates:: User is affiliated with all locations on the POL, poLineId: '{}'",
poLineId);
return Future.succeededFuture();
});
}

private Future<List<String>> getUserTenantsIfNeeded(RequestContext requestContext) {
return consortiumConfigurationService.getConsortiumConfiguration(requestContext)
.compose(consortiumConfiguration ->
consortiumConfiguration
.map(configuration -> consortiumUserTenantsRetriever.getUserTenants(configuration.consortiumId(), configuration.centralTenantId(), requestContext))
.orElse(Future.succeededFuture())
);
}
}
7 changes: 7 additions & 0 deletions src/test/java/org/folio/helper/PurchaseOrderHelperTest.java
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doAnswer;
@@ -46,6 +47,7 @@
import org.folio.service.inventory.InventoryItemStatusSyncService;
import org.folio.service.invoice.InvoiceLineService;
import org.folio.service.orders.CompositeOrderDynamicDataPopulateService;
import org.folio.service.orders.CompositePoLineValidationService;
import org.folio.service.orders.OrderInvoiceRelationService;
import org.folio.service.orders.OrderValidationService;
import org.folio.service.orders.PurchaseOrderLineService;
@@ -100,6 +102,8 @@ public class PurchaseOrderHelperTest {
ConfigurationEntriesCache configurationEntriesCache;
@Mock
OrderValidationService orderValidationService;
@Mock
CompositePoLineValidationService compositePoLineValidationService;

@BeforeEach
void beforeEach() {
@@ -175,6 +179,7 @@ void testPutPendingCompositeOrder() throws IOException {
CompositePurchaseOrder compPO = order.mapTo(CompositePurchaseOrder.class);
prepareOrderForPostRequest(compPO);
compPO.setId(UUID.randomUUID().toString());
compPO.getCompositePoLines().forEach(line -> line.withId(UUID.randomUUID().toString()));
CompositePurchaseOrder poFromStorage = JsonObject.mapFrom(compPO).mapTo(CompositePurchaseOrder.class);

boolean deleteHoldings = false;
@@ -195,6 +200,8 @@ void testPutPendingCompositeOrder() throws IOException {
.when(purchaseOrderStorageService).saveOrder(any(PurchaseOrder.class), eq(requestContext));
doReturn(succeededFuture(null))
.when(encumbranceService).updateEncumbrancesOrderStatusAndReleaseIfClosed(any(CompositePurchaseOrder.class), eq(requestContext));
doReturn(succeededFuture(null))
.when(compositePoLineValidationService).validateUserUnaffiliatedLocationUpdates(anyString(), any(), any(), eq(requestContext));

// When
Future<Void> future = purchaseOrderHelper.putCompositeOrderById(compPO.getId(), deleteHoldings, compPO, requestContext);
59 changes: 0 additions & 59 deletions src/test/java/org/folio/helper/PurchaseOrderLineHelperTest.java
Original file line number Diff line number Diff line change
@@ -3,11 +3,7 @@
import io.vertx.core.Future;
import io.vertx.core.json.JsonObject;

import java.lang.reflect.InvocationTargetException;
import java.util.Optional;
import java.util.stream.Stream;

import org.folio.models.consortium.ConsortiumConfiguration;
import org.folio.rest.acq.model.SequenceNumbers;
import org.folio.rest.core.RestClient;
import org.folio.rest.core.exceptions.HttpException;
@@ -19,8 +15,6 @@
import org.folio.rest.jaxrs.model.Cost;
import org.folio.rest.jaxrs.model.PoLine;
import org.folio.rest.jaxrs.model.ReportingCode;
import org.folio.service.consortium.ConsortiumConfigurationService;
import org.folio.service.consortium.ConsortiumUserTenantsRetriever;
import org.folio.service.orders.PurchaseOrderLineService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -39,13 +33,9 @@

import static io.vertx.core.Future.failedFuture;
import static io.vertx.core.Future.succeededFuture;
import static org.folio.TestUtils.callPrivateMethod;
import static org.folio.TestUtils.getLocationsForTenants;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -57,10 +47,6 @@ public class PurchaseOrderLineHelperTest {
@Mock
private PurchaseOrderLineService purchaseOrderLineService;
@Mock
private ConsortiumConfigurationService consortiumConfigurationService;
@Mock
private ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever;
@Mock
private RequestContext requestContext;
@Mock
private RestClient restClient;
@@ -192,49 +178,4 @@ void testHasAlteredExchangeRate(boolean expected, Double oldExchangeRate, Double
var newCost = new Cost().withCurrency(currencyCode).withExchangeRate(oldExchangeRate);
assertEquals(expected, PurchaseOrderLineHelper.hasAlteredExchangeRate(oldCost, newCost));
}

@Test
void testValidateUserUnaffiliatedLocationUpdates() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
var locationsStored = getLocationsForTenants(List.of("tenant1", "tenant2", "tenant3"));
var locationsUpdated = getLocationsForTenants(List.of("tenant1", "tenant3"));
var storagePoLine = new PoLine().withLocations(locationsStored);
var updatedPoLine = new CompositePoLine().withLocations(locationsUpdated);

doReturn(succeededFuture(Optional.of(new ConsortiumConfiguration("tenant1", "consortiumId"))))
.when(consortiumConfigurationService).getConsortiumConfiguration(any(RequestContext.class));
doReturn(succeededFuture(List.of("tenant1", "tenant2")))
.when(consortiumUserTenantsRetriever).getUserTenants(eq("consortiumId"), eq("centralTenantId"), any(RequestContext.class));

var future = callValidateUserUnaffiliatedLocationUpdates(updatedPoLine, storagePoLine, requestContext, purchaseOrderLineHelper);

assertTrue(future.succeeded());
}

@Test
void testValidateUserUnaffiliatedLocationUpdatesInvalid() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
var locationsStored = getLocationsForTenants(List.of("tenant1", "tenant2", "tenant3"));
var locationsUpdated = getLocationsForTenants(List.of("tenant1", "tenant3"));
locationsUpdated.get(1).withQuantityPhysical(10);
var storagePoLine = new PoLine().withLocations(locationsStored);
var updatedPoLine = new CompositePoLine().withLocations(locationsUpdated);

doReturn(succeededFuture(Optional.of(new ConsortiumConfiguration("tenant1", "consortiumId"))))
.when(consortiumConfigurationService).getConsortiumConfiguration(any(RequestContext.class));
doReturn(succeededFuture(List.of("tenant1")))
.when(consortiumUserTenantsRetriever).getUserTenants(eq("consortiumId"), anyString(), any(RequestContext.class));

var future = callValidateUserUnaffiliatedLocationUpdates(updatedPoLine, storagePoLine, requestContext, purchaseOrderLineHelper);

assertTrue(future.failed());
assertInstanceOf(HttpException.class, future.cause());
}

private Future<Void> callValidateUserUnaffiliatedLocationUpdates(CompositePoLine updatedPoLine, PoLine storagePoLine,
RequestContext requestContext, PurchaseOrderLineHelper purchaseOrderLineHelper)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

return callPrivateMethod(purchaseOrderLineHelper, "validateUserUnaffiliatedLocationUpdates", Future.class,
new Class[]{ CompositePoLine.class, PoLine.class, RequestContext.class }, new Object[] { updatedPoLine, storagePoLine, requestContext });
}

}
Original file line number Diff line number Diff line change
@@ -1,32 +1,43 @@
package org.folio.service.orders;

import static io.vertx.core.Future.succeededFuture;
import static org.folio.TestUtils.getLocationsForTenants;
import static org.folio.rest.core.exceptions.ErrorCodes.*;
import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.OTHER;
import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.P_E_MIX;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

import io.vertx.core.Future;
import org.folio.models.consortium.ConsortiumConfiguration;
import org.folio.rest.core.exceptions.ErrorCodes;
import org.folio.rest.core.exceptions.HttpException;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.jaxrs.model.CompositePoLine;
import org.folio.rest.jaxrs.model.Cost;
import org.folio.rest.jaxrs.model.Details;
import org.folio.rest.jaxrs.model.Error;
import org.folio.rest.jaxrs.model.Location;
import org.folio.rest.jaxrs.model.Physical;
import org.folio.rest.jaxrs.model.PoLine;
import org.folio.service.consortium.ConsortiumConfigurationService;
import org.folio.service.consortium.ConsortiumUserTenantsRetriever;
import org.folio.service.finance.expenceclass.ExpenseClassValidationService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -40,6 +51,10 @@ public class CompositePoLineValidationServiceTest {
private AutoCloseable mockitoMocks;
@Mock
private ExpenseClassValidationService expenseClassValidationService;
@Mock
private ConsortiumConfigurationService consortiumConfigurationService;
@Mock
private ConsortiumUserTenantsRetriever consortiumUserTenantsRetriever;
@InjectMocks
private CompositePoLineValidationService compositePoLineValidationService;
@Mock
@@ -367,10 +382,47 @@ void testValidatePoLineWithZeroQuantitiesWithoutLocations() {
assertThat(errors, hasSize(0));
}


private Set<String> errorsToCodes(List<Error> errors) {
return errors
.stream()
.map(Error::getCode)
.collect(Collectors.toSet());
}

@Test
void testValidateUserUnaffiliatedLocationUpdates() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
var locationsStored = getLocationsForTenants(List.of("tenant1", "tenant2", "tenant3"));
var locationsUpdated = getLocationsForTenants(List.of("tenant1", "tenant3"));
var storagePoLine = new PoLine().withLocations(locationsStored);
var updatedPoLine = new CompositePoLine().withLocations(locationsUpdated);

doReturn(succeededFuture(Optional.of(new ConsortiumConfiguration("tenant1", "consortiumId"))))
.when(consortiumConfigurationService).getConsortiumConfiguration(any(RequestContext.class));
doReturn(succeededFuture(List.of("tenant1", "tenant2")))
.when(consortiumUserTenantsRetriever).getUserTenants(eq("consortiumId"), eq("centralTenantId"), any(RequestContext.class));

var future = compositePoLineValidationService.validateUserUnaffiliatedLocationUpdates(updatedPoLine.getId(), updatedPoLine.getLocations(), storagePoLine.getLocations(), requestContext);

assertTrue(future.succeeded());
}

@Test
void testValidateUserUnaffiliatedLocationUpdatesInvalid() {
var locationsStored = getLocationsForTenants(List.of("tenant1", "tenant2", "tenant3"));
var locationsUpdated = getLocationsForTenants(List.of("tenant1", "tenant3"));
locationsUpdated.get(1).withQuantityPhysical(10);
var storagePoLine = new PoLine().withLocations(locationsStored);
var updatedPoLine = new CompositePoLine().withLocations(locationsUpdated);

doReturn(succeededFuture(Optional.of(new ConsortiumConfiguration("tenant1", "consortiumId"))))
.when(consortiumConfigurationService).getConsortiumConfiguration(any(RequestContext.class));
doReturn(succeededFuture(List.of("tenant1")))
.when(consortiumUserTenantsRetriever).getUserTenants(eq("consortiumId"), anyString(), any(RequestContext.class));

var future = compositePoLineValidationService.validateUserUnaffiliatedLocationUpdates(updatedPoLine.getId(), updatedPoLine.getLocations(), storagePoLine.getLocations(), requestContext);

assertTrue(future.failed());
assertInstanceOf(HttpException.class, future.cause());
}
}

0 comments on commit 8092d97

Please sign in to comment.