diff --git a/src/main/java/org/folio/rest/core/exceptions/ErrorCodes.java b/src/main/java/org/folio/rest/core/exceptions/ErrorCodes.java index 527424401..766e54128 100644 --- a/src/main/java/org/folio/rest/core/exceptions/ErrorCodes.java +++ b/src/main/java/org/folio/rest/core/exceptions/ErrorCodes.java @@ -111,7 +111,7 @@ public enum ErrorCodes { PO_LINE_HAS_RELATED_APPROVED_INVOICE_ERROR("poLineHasRelatedApprovedInvoice", "Composite POL related invoice lines contains lines which has status APPROVED for the current fiscal year, invoice line ids: %s"), MULTIPLE_FISCAL_YEARS("multipleFiscalYears", "Order line fund distributions have active budgets in multiple fiscal years."), INSTANCE_INVALID_PRODUCT_ID_ERROR("instanceInvalidProductIdError", "Instance connection could not be changed, the chosen instance contains an invalid Product ID."), - FUND_LOCATION_RESTRICTION_VIOLATION("fundLocationRestrictionViolation", "One of the funds is restricted to be used for one of the locations."), + FUND_LOCATION_RESTRICTION_VIOLATION("fundLocationRestrictionViolation", "One of the locations is restricted to be used by all funds."), ENCUMBRANCES_FOR_RE_ENCUMBER_NOT_FOUND("encumbrancesForReEncumberNotFound", "The encumbrances were correctly created during the rollover or have already been updated."), CLAIMING_CONFIG_INVALID("claimingConfigInvalid", "Claiming interval should be set and greater than 0 if claiming is active"), TEMPLATE_NAME_ALREADY_EXISTS("templateNameNotUnique", "Template name already exists"); diff --git a/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderFlowValidator.java b/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderFlowValidator.java index 31a65b5a6..67668011a 100644 --- a/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderFlowValidator.java +++ b/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderFlowValidator.java @@ -4,7 +4,6 @@ import static org.folio.rest.jaxrs.model.CompositePurchaseOrder.WorkflowStatus.PENDING; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -129,24 +128,37 @@ public Future checkFundLocationRestrictions(List poLines, } private Future validateLocationRestrictions(CompositePoLine poLine, List funds) { - List polLocationIds = poLine.getLocations().stream().map(Location::getLocationId).toList(); - for (Fund fund : funds) { - if (Boolean.TRUE.equals(fund.getRestrictByLocations()) && !CollectionUtils.containsAll(fund.getLocationIds(), polLocationIds)) { - String poLineId = poLine.getId(); - String fundId = fund.getId(); - Collection restrictedLocations = CollectionUtils.subtract(polLocationIds, fund.getLocationIds()); - logger.error("For POL {} fund {} is restricted to be used for locations {}", poLineId, fundId, restrictedLocations); - List parameters = List.of( - new Parameter().withKey("poLineId").withValue(poLineId), - new Parameter().withKey("poLineNumber").withValue(poLine.getPoLineNumber()), - new Parameter().withKey("fundId").withValue(fundId), - new Parameter().withKey("fundCode").withValue(fund.getCode()), - new Parameter().withKey("restrictedLocations").withValue(restrictedLocations.toString()) - ); - return Future.failedFuture(new HttpException(422, ErrorCodes.FUND_LOCATION_RESTRICTION_VIOLATION, parameters)); - } + // TODO: will be removed in scope of https://folio-org.atlassian.net/browse/MODORDERS-981 + // as we'll obtain funds once before the processing and check on emptiness + if (CollectionUtils.isEmpty(funds)) { + logger.info("No funds found for PO Line {}, skipping fund-location restrictions check.", poLine.getId()); + return Future.succeededFuture(); + } + + List restrictedLocations = poLine.getLocations() + .stream() + .map(Location::getLocationId) + .filter(locId -> isRestrictedByAllFunds(funds, locId)) + .toList(); + + if (restrictedLocations.isEmpty()) { + return Future.succeededFuture(); } - return Future.succeededFuture(); + + String poLineId = poLine.getId(); + logger.error("For POL {} locations {} are restricted to be used by all funds", poLineId, restrictedLocations); + List parameters = List.of( + new Parameter().withKey("poLineId").withValue(poLineId), + new Parameter().withKey("poLineNumber").withValue(poLine.getPoLineNumber()), + new Parameter().withKey("restrictedLocations").withValue(restrictedLocations.toString()) + ); + return Future.failedFuture(new HttpException(422, ErrorCodes.FUND_LOCATION_RESTRICTION_VIOLATION, parameters)); + } + + private boolean isRestrictedByAllFunds(List funds, String locationId) { + return funds + .stream() + .allMatch(fund -> Boolean.TRUE.equals(fund.getRestrictByLocations()) && !fund.getLocationIds().contains(locationId)); } } diff --git a/src/test/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderFlowValidatorTest.java b/src/test/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderFlowValidatorTest.java index 7a70fa1bd..57ffd2883 100644 --- a/src/test/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderFlowValidatorTest.java +++ b/src/test/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderFlowValidatorTest.java @@ -47,7 +47,7 @@ public void testCheckFundLocationRestrictions(VertxTestContext vertxTestContext) // given List fundIds = List.of("F1", "F2"); - List locationIds = List.of("L1", "L2", "L3"); + List locationIds = List.of("L1", "L2", "L3", "L4", "L5", "L6"); CompositePoLine poLine = new CompositePoLine() .withId("ID") .withPoLineNumber("number") @@ -59,8 +59,8 @@ public void testCheckFundLocationRestrictions(VertxTestContext vertxTestContext) ); Mockito.when(fundService.getFunds(fundIds, requestContext)).thenReturn( Future.succeededFuture(List.of( - new Fund().withId("F1").withCode("FC").withRestrictByLocations(true).withLocationIds(List.of("L1", "L2", "L3", "L4")), - new Fund().withId("F2").withCode("FC").withRestrictByLocations(true).withLocationIds(List.of("L2")) + new Fund().withId("F1").withCode("FC").withRestrictByLocations(true).withLocationIds(List.of("L1", "L2", "L3")), + new Fund().withId("F2").withCode("FC").withRestrictByLocations(true).withLocationIds(List.of("L2", "L3", "L4")) )) ); @@ -76,9 +76,7 @@ public void testCheckFundLocationRestrictions(VertxTestContext vertxTestContext) List expectedParameters = List.of( new Parameter().withKey("poLineId").withValue(poLine.getId()), new Parameter().withKey("poLineNumber").withValue(poLine.getPoLineNumber()), - new Parameter().withKey("fundId").withValue("F2"), - new Parameter().withKey("fundCode").withValue("FC"), - new Parameter().withKey("restrictedLocations").withValue("[L1, L3]") + new Parameter().withKey("restrictedLocations").withValue("[L5, L6]") ); assertEquals(FUND_LOCATION_RESTRICTION_VIOLATION.toError().withParameters(expectedParameters), exception.getError()); vertxTestContext.completeNow();