From 8092d9730777bbeacabcf502323235fcbcd92b71 Mon Sep 17 00:00:00 2001 From: Boburbek Kadirkhodjaev Date: Fri, 6 Dec 2024 21:06:47 +0500 Subject: [PATCH] [EUREKA-561-4]. Add validateUserUnaffiliatedLocationUpdates method to 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 --- .../org/folio/config/ApplicationConfig.java | 20 +++---- .../org/folio/helper/PurchaseOrderHelper.java | 28 ++++++++- .../folio/helper/PurchaseOrderLineHelper.java | 50 +--------------- .../CompositePoLineValidationService.java | 51 +++++++++++++++- .../folio/helper/PurchaseOrderHelperTest.java | 7 +++ .../helper/PurchaseOrderLineHelperTest.java | 59 ------------------- .../CompositePoLineValidationServiceTest.java | 52 ++++++++++++++++ 7 files changed, 147 insertions(+), 120 deletions(-) diff --git a/src/main/java/org/folio/config/ApplicationConfig.java b/src/main/java/org/folio/config/ApplicationConfig.java index a5fd0af89..7ee11cb1f 100644 --- a/src/main/java/org/folio/config/ApplicationConfig.java +++ b/src/main/java/org/folio/config/ApplicationConfig.java @@ -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() { diff --git a/src/main/java/org/folio/helper/PurchaseOrderHelper.java b/src/main/java/org/folio/helper/PurchaseOrderHelper.java index 89b1e04cb..c44a558ff 100644 --- a/src/main/java/org/folio/helper/PurchaseOrderHelper.java +++ b/src/main/java/org/folio/helper/PurchaseOrderHelper.java @@ -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 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>(); + 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 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())); diff --git a/src/main/java/org/folio/helper/PurchaseOrderLineHelper.java b/src/main/java/org/folio/helper/PurchaseOrderLineHelper.java index ee2cb4824..9198653dd 100644 --- a/src/main/java/org/folio/helper/PurchaseOrderLineHelper.java +++ b/src/main/java/org/folio/helper/PurchaseOrderLineHelper.java @@ -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 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 validateAccessProviders(CompositePoLine compOrderLine, Requ .mapEmpty(); } - private Future 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> 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)); diff --git a/src/main/java/org/folio/service/orders/CompositePoLineValidationService.java b/src/main/java/org/folio/service/orders/CompositePoLineValidationService.java index 5e0159e98..e3134a5c8 100644 --- a/src/main/java/org/folio/service/orders/CompositePoLineValidationService.java +++ b/src/main/java/org/folio/service/orders/CompositePoLineValidationService.java @@ -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> validatePoLine(CompositePoLine compPOL, RequestContext requestContext) { @@ -329,4 +344,38 @@ private void validateReceivingWorkflowForBindary(CompositePoLine poLine, List validateUserUnaffiliatedLocationUpdates(String poLineId, List updatedPoLineLocations, + List 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> getUserTenantsIfNeeded(RequestContext requestContext) { + return consortiumConfigurationService.getConsortiumConfiguration(requestContext) + .compose(consortiumConfiguration -> + consortiumConfiguration + .map(configuration -> consortiumUserTenantsRetriever.getUserTenants(configuration.consortiumId(), configuration.centralTenantId(), requestContext)) + .orElse(Future.succeededFuture()) + ); + } } diff --git a/src/test/java/org/folio/helper/PurchaseOrderHelperTest.java b/src/test/java/org/folio/helper/PurchaseOrderHelperTest.java index eb26c8969..3b13e38c7 100644 --- a/src/test/java/org/folio/helper/PurchaseOrderHelperTest.java +++ b/src/test/java/org/folio/helper/PurchaseOrderHelperTest.java @@ -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 future = purchaseOrderHelper.putCompositeOrderById(compPO.getId(), deleteHoldings, compPO, requestContext); diff --git a/src/test/java/org/folio/helper/PurchaseOrderLineHelperTest.java b/src/test/java/org/folio/helper/PurchaseOrderLineHelperTest.java index 6e9827aa2..c3ee8fd37 100644 --- a/src/test/java/org/folio/helper/PurchaseOrderLineHelperTest.java +++ b/src/test/java/org/folio/helper/PurchaseOrderLineHelperTest.java @@ -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 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 }); - } - } diff --git a/src/test/java/org/folio/service/orders/CompositePoLineValidationServiceTest.java b/src/test/java/org/folio/service/orders/CompositePoLineValidationServiceTest.java index 9ca39d5d6..423ba2d09 100644 --- a/src/test/java/org/folio/service/orders/CompositePoLineValidationServiceTest.java +++ b/src/test/java/org/folio/service/orders/CompositePoLineValidationServiceTest.java @@ -1,6 +1,7 @@ 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; @@ -8,18 +9,25 @@ 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; @@ -27,6 +35,9 @@ 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 errorsToCodes(List 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()); + } }