From 631d8399b0c7f4e4fad2fa2225b0244a0dde1c5d Mon Sep 17 00:00:00 2001 From: BKadirkhodjaev Date: Wed, 18 Dec 2024 17:49:33 +0500 Subject: [PATCH 1/8] [MODORDERS-1209-2]. Update Send Claims and Piece Batch Update models with new properties --- ramls/acq-models | 2 +- .../pieces/PieceBatchStatusUpdateHolder.java | 6 +++++ .../java/org/folio/rest/impl/PiecesAPI.java | 5 +++- .../org/folio/service/pieces/PieceUtil.java | 13 ++++++++++ .../service/pieces/PiecesClaimingService.java | 21 ++++++++-------- .../flows/update/PieceUpdateFlowManager.java | 7 +++--- .../rest/impl/PiecesClaimingApiTest.java | 2 ++ .../pieces/PiecesClaimingServiceTest.java | 24 +++++++++---------- .../update/PieceUpdateFlowManagerTest.java | 2 +- .../send-claims-1-piece-1-vendor-1-job.json | 5 +++- 10 files changed, 58 insertions(+), 29 deletions(-) diff --git a/ramls/acq-models b/ramls/acq-models index c0c72775d..a8f2fef0b 160000 --- a/ramls/acq-models +++ b/ramls/acq-models @@ -1 +1 @@ -Subproject commit c0c72775d08c28a77d1475df435497e8b0020667 +Subproject commit a8f2fef0b41fbe99f917a39857821b93c417b1a9 diff --git a/src/main/java/org/folio/models/pieces/PieceBatchStatusUpdateHolder.java b/src/main/java/org/folio/models/pieces/PieceBatchStatusUpdateHolder.java index b65c1663f..23570a86d 100644 --- a/src/main/java/org/folio/models/pieces/PieceBatchStatusUpdateHolder.java +++ b/src/main/java/org/folio/models/pieces/PieceBatchStatusUpdateHolder.java @@ -12,6 +12,12 @@ public class PieceBatchStatusUpdateHolder extends BasePieceFlowHolder { @Getter private Piece.ReceivingStatus receivingStatus; @Getter + private Integer claimingInterval; + @Getter + private String internalNote; + @Getter + private String externalNote; + @Getter private List pieces; private String poLineId; diff --git a/src/main/java/org/folio/rest/impl/PiecesAPI.java b/src/main/java/org/folio/rest/impl/PiecesAPI.java index d79527686..91a9028fe 100644 --- a/src/main/java/org/folio/rest/impl/PiecesAPI.java +++ b/src/main/java/org/folio/rest/impl/PiecesAPI.java @@ -114,7 +114,10 @@ public void getOrdersPiecesRequests(List pieceIds, String status, Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) { - pieceUpdateFlowManager.updatePiecesStatuses(pieceBatchStatusCollection.getPieceIds(), pieceBatchStatusCollection.getReceivingStatus(), new RequestContext(vertxContext, okapiHeaders)) + var pieceIds = pieceBatchStatusCollection.getPieceIds(); + var receivingStatus = pieceBatchStatusCollection.getReceivingStatus(); + var claimingInterval = pieceBatchStatusCollection.getClaimingInterval(); + pieceUpdateFlowManager.updatePiecesStatuses(pieceIds, receivingStatus, claimingInterval, null, null, new RequestContext(vertxContext, okapiHeaders)) .onSuccess(v -> asyncResultHandler.handle(succeededFuture(buildNoContentResponse()))) .onFailure(t -> handleErrorResponse(asyncResultHandler, t)); } diff --git a/src/main/java/org/folio/service/pieces/PieceUtil.java b/src/main/java/org/folio/service/pieces/PieceUtil.java index e97d67103..5ab3fe20f 100644 --- a/src/main/java/org/folio/service/pieces/PieceUtil.java +++ b/src/main/java/org/folio/service/pieces/PieceUtil.java @@ -10,6 +10,7 @@ import java.util.Optional; import java.util.stream.Collectors; +import org.folio.models.pieces.PieceBatchStatusUpdateHolder; import org.folio.orders.utils.PoLineCommonUtil; import org.folio.rest.jaxrs.model.CompositePoLine; import org.folio.rest.jaxrs.model.Eresource; @@ -82,4 +83,16 @@ public static boolean updatePieceStatus(Piece piece, Piece.ReceivingStatus oldSt return isStatusChanged; } + public static boolean updatePieceStatus(Piece piece, PieceBatchStatusUpdateHolder holder) { + var isStatusChanged = !piece.getReceivingStatus().equals(holder.getReceivingStatus()); + if (isStatusChanged) { + piece.setStatusUpdatedDate(new Date()); + } + piece.setReceivingStatus(holder.getReceivingStatus()); + piece.setClaimingInterval(holder.getClaimingInterval()); + piece.setInternalNote(holder.getInternalNote()); + piece.setExternalNote(holder.getExternalNote()); + return isStatusChanged; + } + } diff --git a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java index a2fdcf561..46771689b 100644 --- a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java +++ b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java @@ -90,7 +90,7 @@ public Future sendClaims(ClaimingCollection claimingCollection, return Future.succeededFuture(createEmptyClaimingResults(CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS.getValue())); } log.info("sendClaims:: Using pieces by vendor id map, map: {}", pieceIdsByVendorIds); - return createJobsByVendor(config, pieceIdsByVendorIds, requestContext); + return createJobsByVendor(claimingCollection, config, pieceIdsByVendorIds, requestContext); }); }) .onFailure(t -> log.error("sendClaims:: Failed send claims: {}", JsonObject.mapFrom(claimingCollection).encodePrettily(), t)); @@ -151,15 +151,15 @@ private static Map> transformAndGroupPieceIdsByVendorId(Lis .groupingBy(Pair::getKey, mapping(Pair::getValue, toList())); } - private Future createJobsByVendor(JsonObject config, Map> pieceIdsByVendorId, - RequestContext requestContext) { + private Future createJobsByVendor(ClaimingCollection claimingCollection, JsonObject config, + Map> pieceIdsByVendorId, RequestContext requestContext) { log.info("createJobsByVendor:: Creating jobs by vendor, vendors by pieces count: {}", pieceIdsByVendorId.size()); if (CollectionUtils.isEmpty(pieceIdsByVendorId)) { log.info("createJobsByVendor:: No jobs are created, pieceIdsByVendorId is empty"); return Future.succeededFuture(new ClaimingResults() .withClaimingPieceResults(createErrorClaimingResults(pieceIdsByVendorId, CANNOT_GROUP_PIECES_BY_VENDOR_MESSAGE.getValue()))); } - return collectResultsOnSuccess(createUpdatePiecesAndJobFutures(config, pieceIdsByVendorId, requestContext)) + return collectResultsOnSuccess(createUpdatePiecesAndJobFutures(claimingCollection, config, pieceIdsByVendorId, requestContext)) .map(updatedPieceLists -> { if (CollectionUtils.isEmpty(updatedPieceLists)) { log.info("createJobsByVendor:: No pieces were processed for claiming"); @@ -173,8 +173,8 @@ private Future createJobsByVendor(JsonObject config, Map>> createUpdatePiecesAndJobFutures(JsonObject config, Map> pieceIdsByVendorId, - RequestContext requestContext) { + private List>> createUpdatePiecesAndJobFutures(ClaimingCollection claimingCollection, JsonObject config, + Map> pieceIdsByVendorId, RequestContext requestContext) { var updatePiecesAndJobFutures = new ArrayList>>(); pieceIdsByVendorId.forEach((vendorId, pieceIds) -> config.stream() .filter(pieceIdsByVendorIdEntry -> isExportTypeClaimsAndCorrectVendorId(vendorId, pieceIdsByVendorIdEntry) @@ -182,7 +182,7 @@ private List>> createUpdatePiecesAndJobFutures(JsonObject co .forEach(pieceIdsByVendorIdEntry -> { log.info("createJobsByVendor:: Preparing job integration detail for vendor, vendor id: {}, pieces: {}, job key: {}", vendorId, pieceIds.size(), pieceIdsByVendorIdEntry.getKey()); - updatePiecesAndJobFutures.add(updatePiecesAndCreateJob(pieceIds, pieceIdsByVendorIdEntry, requestContext)); + updatePiecesAndJobFutures.add(updatePiecesAndCreateJob(claimingCollection, pieceIds, pieceIdsByVendorIdEntry, requestContext)); })); return updatePiecesAndJobFutures; } @@ -208,10 +208,11 @@ private static boolean isExportTypeClaimsAndCorrectVendorId(String vendorId, Map return pieceIdsByVendorIdEntry.getKey().startsWith(String.format("%s_%s", EXPORT_TYPE_CLAIMS, vendorId)); } - private Future> updatePiecesAndCreateJob(List pieceIds, Map.Entry pieceIdsByVendorIdEntry, - RequestContext requestContext) { + private Future> updatePiecesAndCreateJob(ClaimingCollection claimingCollection, List pieceIds, + Map.Entry pieceIdsByVendorIdEntry, RequestContext requestContext) { log.info("updatePiecesAndCreateJob:: Updating pieces and creating a job, job key: {}, count: {}", pieceIdsByVendorIdEntry.getKey(), pieceIds.size()); - return pieceUpdateFlowManager.updatePiecesStatuses(pieceIds, PieceBatchStatusCollection.ReceivingStatus.CLAIM_SENT, requestContext) + return pieceUpdateFlowManager.updatePiecesStatuses(pieceIds, PieceBatchStatusCollection.ReceivingStatus.CLAIM_SENT, + claimingCollection.getClaimingInterval(), claimingCollection.getInternalNote(), claimingCollection.getExternalNote(), requestContext) .compose(v -> createJob(pieceIdsByVendorIdEntry.getKey(), pieceIdsByVendorIdEntry.getValue(), pieceIds, requestContext).map(pieceIds)); } diff --git a/src/main/java/org/folio/service/pieces/flows/update/PieceUpdateFlowManager.java b/src/main/java/org/folio/service/pieces/flows/update/PieceUpdateFlowManager.java index 5eed324f9..ec1298fc2 100644 --- a/src/main/java/org/folio/service/pieces/flows/update/PieceUpdateFlowManager.java +++ b/src/main/java/org/folio/service/pieces/flows/update/PieceUpdateFlowManager.java @@ -97,14 +97,15 @@ public Future updatePiece(Piece pieceToUpdate, boolean createItem, boolean .mapEmpty(); } - public Future updatePiecesStatuses(List pieceIds, PieceBatchStatusCollection.ReceivingStatus receivingStatus, RequestContext requestContext) { + public Future updatePiecesStatuses(List pieceIds, PieceBatchStatusCollection.ReceivingStatus receivingStatus, + Integer claimingInterval, String internalNote, String externalNote, RequestContext requestContext) { var newStatus = Piece.ReceivingStatus.fromValue(receivingStatus.value()); return isOperationRestricted(pieceIds, requestContext) .compose(v -> pieceStorageService.getPiecesByIds(pieceIds, requestContext)) .compose(pieces -> validateFetchedPiecesQuantity(pieces, pieceIds)) .map(pieces -> pieces.stream().collect(Collectors.groupingBy(Piece::getPoLineId))) .map(piecesByPoLineId -> piecesByPoLineId.entrySet().stream() - .map(entry -> new PieceBatchStatusUpdateHolder(newStatus, entry.getValue(), entry.getKey())) + .map(entry -> new PieceBatchStatusUpdateHolder(newStatus, claimingInterval, internalNote, externalNote, entry.getValue(), entry.getKey())) .map(holder -> basePieceFlowHolderBuilder.updateHolderWithOrderInformation(holder, requestContext) .compose(v -> updatePoLine(holder, requestContext)) .compose(v -> updatePiecesStatusesByPoLine(holder, requestContext))) @@ -171,7 +172,7 @@ private Future> validateFetchedPiecesQuantity(List pieces, Li private Future updatePiecesStatusesByPoLine(PieceBatchStatusUpdateHolder holder, RequestContext requestContext) { var isAnyPiecesUpdated = holder.getPieces().stream() - .map(piece -> updatePieceStatus(piece, piece.getReceivingStatus(), holder.getReceivingStatus())) + .map(piece -> updatePieceStatus(piece, holder)) .reduce(Boolean.FALSE, Boolean::logicalOr); // Don't replace .map() with .anyMatch(), as it needs to iterate over all elements if (!Boolean.TRUE.equals(isAnyPiecesUpdated)) { return Future.succeededFuture(); diff --git a/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java b/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java index c72ef5d9b..89bec2300 100644 --- a/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java +++ b/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java @@ -204,6 +204,8 @@ void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx assertThat(jobCreations, hasSize(dto.jobCreations)); assertThat(jobExecutions, hasSize(dto.jobExecutions)); assertThat(response.getClaimingPieceResults().size(), equalTo(dto.claimingResults)); + pieceUpdates.forEach(pieceUpdate -> logger.info("Updated piece: {}", pieceUpdate.encodePrettily())); + var claimedPieceIds = jobCreations.stream() .peek(job -> logger.info("Created job: {}", JsonObject.mapFrom(job).encodePrettily())) .map(job -> job.getJsonObject(EXPORT_TYPE_SPECIFIC_PARAMETERS.getValue()) diff --git a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java index d74651a3c..81475bbd3 100644 --- a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java +++ b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java @@ -82,7 +82,7 @@ void testSendClaims_emptyClaimingPieceIds(VertxTestContext testContext) { @Test void testSendClaims_noConfigEntries(VertxTestContext testContext) { - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")).withClaimingInterval(1); var requestContext = mock(RequestContext.class); when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject())); @@ -97,7 +97,7 @@ void testSendClaims_noConfigEntries(VertxTestContext testContext) { @Test void testSendClaims_noPiecesFound(VertxTestContext testContext) { - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")).withClaimingInterval(1); var requestContext = mock(RequestContext.class); when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("key", "value"))); @@ -113,7 +113,7 @@ void testSendClaims_noPiecesFound(VertxTestContext testContext) { @Test void testSendClaims_pieceStatusNotLate(VertxTestContext testContext) { - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")).withClaimingInterval(1); var requestContext = mock(RequestContext.class); when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("CLAIMS_vendorId", createIntegrationDetail()))); @@ -121,7 +121,7 @@ void testSendClaims_pieceStatusNotLate(VertxTestContext testContext) { when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId1"))); when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId"))); when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withIsVendor(true))); - when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any())).thenReturn(Future.succeededFuture()); + when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(restClient.post(eq(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)), any(), any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("status", "CREATED"))); when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); @@ -135,7 +135,7 @@ void testSendClaims_pieceStatusNotLate(VertxTestContext testContext) { @Test void testSendClaims_success(VertxTestContext testContext) { - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")).withClaimingInterval(1); var requestContext = mock(RequestContext.class); when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("CLAIMS_vendorId", createIntegrationDetail()))); @@ -143,7 +143,7 @@ void testSendClaims_success(VertxTestContext testContext) { when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId1"))); when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId"))); when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withIsVendor(true))); - when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any())).thenReturn(Future.succeededFuture()); + when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(restClient.post(eq(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)), any(), any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("status", "CREATED"))); when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); @@ -158,7 +158,7 @@ void testSendClaims_success(VertxTestContext testContext) { @Test void testSendClaims_successWithTwoPieces(VertxTestContext testContext) { - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1", "pieceId2")); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1", "pieceId2")).withClaimingInterval(1); var requestContext = mock(RequestContext.class); when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("CLAIMS_vendorId", createIntegrationDetail()))); @@ -169,7 +169,7 @@ void testSendClaims_successWithTwoPieces(VertxTestContext testContext) { when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId1"))); when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId"))); when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withIsVendor(true))); - when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any())).thenReturn(Future.succeededFuture()); + when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(restClient.post(eq(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)), any(), any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("status", "CREATED"))); when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); @@ -186,7 +186,7 @@ void testSendClaims_successWithTwoPieces(VertxTestContext testContext) { @Test void testSendClaims_successWithMultipleOrganizations(VertxTestContext testContext) { - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1", "pieceId2", "pieceId3", "pieceId4", "pieceId5")); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1", "pieceId2", "pieceId3", "pieceId4", "pieceId5")).withClaimingInterval(1); var requestContext = mock(RequestContext.class); when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject() @@ -216,7 +216,7 @@ void testSendClaims_successWithMultipleOrganizations(VertxTestContext testContex return Future.succeededFuture(new Organization().withId(vendorId).withIsVendor(true)); }); - when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any())).thenReturn(Future.succeededFuture()); + when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(restClient.post(eq(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)), any(), any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("status", "CREATED"))); when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); @@ -241,7 +241,7 @@ void testSendClaims_successWithMultipleOrganizations(VertxTestContext testContex @Test void testSendClaims_successWithMultipleOrganizationsAndOneIntegrationDetail(VertxTestContext testContext) { - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1", "pieceId2", "pieceId3", "pieceId4", "pieceId5")); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1", "pieceId2", "pieceId3", "pieceId4", "pieceId5")).withClaimingInterval(1); var requestContext = mock(RequestContext.class); when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject() @@ -270,7 +270,7 @@ void testSendClaims_successWithMultipleOrganizationsAndOneIntegrationDetail(Vert return Future.succeededFuture(new Organization().withId(vendorId).withIsVendor(true)); }); - when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any())).thenReturn(Future.succeededFuture()); + when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(restClient.post(eq(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)), any(), any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("status", "CREATED"))); when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); diff --git a/src/test/java/org/folio/service/pieces/flows/update/PieceUpdateFlowManagerTest.java b/src/test/java/org/folio/service/pieces/flows/update/PieceUpdateFlowManagerTest.java index 1fbb37eeb..96331a062 100644 --- a/src/test/java/org/folio/service/pieces/flows/update/PieceUpdateFlowManagerTest.java +++ b/src/test/java/org/folio/service/pieces/flows/update/PieceUpdateFlowManagerTest.java @@ -350,7 +350,7 @@ void shouldUpdatePiecesStatusesSuccessfully() { doReturn(succeededFuture()).when(pieceStorageService).updatePiece(any(), eq(requestContext)); doNothing().when(pieceService).receiptConsistencyPiecePoLine(anyString(), eq(requestContext)); - Future result = pieceUpdateFlowManager.updatePiecesStatuses(pieceIds, receivingStatus, requestContext); + Future result = pieceUpdateFlowManager.updatePiecesStatuses(pieceIds, receivingStatus, null, null, null, requestContext); assertTrue(result.succeeded()); } diff --git a/src/test/resources/mockdata/claiming/send-claims-1-piece-1-vendor-1-job.json b/src/test/resources/mockdata/claiming/send-claims-1-piece-1-vendor-1-job.json index 090aad6b5..a9685606a 100644 --- a/src/test/resources/mockdata/claiming/send-claims-1-piece-1-vendor-1-job.json +++ b/src/test/resources/mockdata/claiming/send-claims-1-piece-1-vendor-1-job.json @@ -1,5 +1,8 @@ { "claimingPieceIds": [ "dcd0ba36-b660-4751-b9fe-c8ac61ff6f99" - ] + ], + "claimingInterval": 1, + "internalNote": "internal", + "externalNote": "external" } From 1df97c23fe4153111a1a75323a011eade51c7140 Mon Sep 17 00:00:00 2001 From: BKadirkhodjaev Date: Thu, 19 Dec 2024 13:59:50 +0500 Subject: [PATCH 2/8] [MODORDERS-1209-2]. Add test coverage to PieceUtil --- .../folio/service/pieces/PieceUtilTest.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/test/java/org/folio/service/pieces/PieceUtilTest.java diff --git a/src/test/java/org/folio/service/pieces/PieceUtilTest.java b/src/test/java/org/folio/service/pieces/PieceUtilTest.java new file mode 100644 index 000000000..c73dd6578 --- /dev/null +++ b/src/test/java/org/folio/service/pieces/PieceUtilTest.java @@ -0,0 +1,69 @@ +package org.folio.service.pieces; + +import org.folio.CopilotGenerated; +import org.folio.models.pieces.PieceBatchStatusUpdateHolder; +import org.folio.rest.jaxrs.model.Piece; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +@CopilotGenerated(partiallyGenerated = true) +class PieceUtilTest { + + @Test + void testUpdatePieceStatusWithOldAndNewStatus() { + Piece piece = new Piece().withReceivingStatus(Piece.ReceivingStatus.EXPECTED); + Piece.ReceivingStatus newStatus = Piece.ReceivingStatus.RECEIVED; + + boolean statusChanged = PieceUtil.updatePieceStatus(piece, piece.getReceivingStatus(), newStatus); + + assertTrue(statusChanged); + assertSame(newStatus, piece.getReceivingStatus()); + assertNotNull(piece.getStatusUpdatedDate()); + } + + @Test + void testUpdatePieceStatusWithSameOldAndNewStatus() { + Piece piece = new Piece().withReceivingStatus(Piece.ReceivingStatus.EXPECTED); + Piece.ReceivingStatus newStatus = Piece.ReceivingStatus.EXPECTED; + + boolean statusChanged = PieceUtil.updatePieceStatus(piece, piece.getReceivingStatus(), newStatus); + + assertFalse(statusChanged); + assertSame(newStatus, piece.getReceivingStatus()); + } + + @Test + void testUpdatePieceStatusWithHolder() { + Piece piece = new Piece().withReceivingStatus(Piece.ReceivingStatus.EXPECTED); + PieceBatchStatusUpdateHolder holder = new PieceBatchStatusUpdateHolder(Piece.ReceivingStatus.RECEIVED, + 30, "internal", "external", List.of(piece), UUID.randomUUID().toString()); + + boolean statusChanged = PieceUtil.updatePieceStatus(piece, holder); + + assertTrue(statusChanged); + assertSame(piece.getReceivingStatus(), holder.getReceivingStatus()); + assertSame(piece.getClaimingInterval(), holder.getClaimingInterval()); + assertEquals(piece.getInternalNote(), holder.getInternalNote()); + assertEquals(piece.getExternalNote(), holder.getExternalNote()); + assertNotNull(piece.getStatusUpdatedDate()); + } + + @Test + void testUpdatePieceStatusWithHolderSameStatus() { + Piece piece = new Piece().withReceivingStatus(Piece.ReceivingStatus.EXPECTED); + PieceBatchStatusUpdateHolder holder = new PieceBatchStatusUpdateHolder(Piece.ReceivingStatus.EXPECTED, + 30, "internal", "external", List.of(piece), UUID.randomUUID().toString()); + + boolean statusChanged = PieceUtil.updatePieceStatus(piece, holder); + + assertFalse(statusChanged); + assertSame(piece.getReceivingStatus(), holder.getReceivingStatus()); + assertSame(piece.getClaimingInterval(), holder.getClaimingInterval()); + assertEquals(piece.getInternalNote(), holder.getInternalNote()); + assertEquals(piece.getExternalNote(), holder.getExternalNote()); + } +} From 037711d902754031248e27d9b5bb8bf78678a0d9 Mon Sep 17 00:00:00 2001 From: BKadirkhodjaev Date: Thu, 19 Dec 2024 15:54:25 +0500 Subject: [PATCH 3/8] [MODORDERS-1209-2]. Add missing PieceUtilTestNested --- src/test/java/org/folio/ApiTestSuite.java | 5 +++++ src/test/java/org/folio/CopilotGenerated.java | 2 +- src/test/java/org/folio/service/pieces/PieceUtilTest.java | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/folio/ApiTestSuite.java b/src/test/java/org/folio/ApiTestSuite.java index 8f1d9f8da..62a9acd20 100644 --- a/src/test/java/org/folio/ApiTestSuite.java +++ b/src/test/java/org/folio/ApiTestSuite.java @@ -97,6 +97,7 @@ import org.folio.service.pieces.PieceServiceTest; import org.folio.service.pieces.PieceStorageServiceTest; import org.folio.service.pieces.PieceUpdateInventoryServiceTest; +import org.folio.service.pieces.PieceUtilTest; import org.folio.service.pieces.PiecesClaimingServiceTest; import org.folio.service.pieces.flows.BasePieceFlowHolderBuilderTest; import org.folio.service.pieces.flows.DefaultPieceFlowsValidatorTest; @@ -555,4 +556,8 @@ class PiecesClaimingApiTestNested extends PiecesClaimingApiTest { @Nested class PiecesClaimingServiceNested extends PiecesClaimingServiceTest { } + + @Nested + class PieceUtilTestNested extends PieceUtilTest { + } } diff --git a/src/test/java/org/folio/CopilotGenerated.java b/src/test/java/org/folio/CopilotGenerated.java index e9dc2e86e..fe9bcc860 100644 --- a/src/test/java/org/folio/CopilotGenerated.java +++ b/src/test/java/org/folio/CopilotGenerated.java @@ -13,7 +13,7 @@ * Set value or partiallyGenerated attribute to true * if the generated test(s) were significantly modified/altered by the developer. */ -@Target(ElementType.TYPE) +@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface CopilotGenerated { diff --git a/src/test/java/org/folio/service/pieces/PieceUtilTest.java b/src/test/java/org/folio/service/pieces/PieceUtilTest.java index c73dd6578..1dbe27b0a 100644 --- a/src/test/java/org/folio/service/pieces/PieceUtilTest.java +++ b/src/test/java/org/folio/service/pieces/PieceUtilTest.java @@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.*; @CopilotGenerated(partiallyGenerated = true) -class PieceUtilTest { +public class PieceUtilTest { @Test void testUpdatePieceStatusWithOldAndNewStatus() { From ba3fd6267d7018f4155395bc7d9d4f8992633e8c Mon Sep 17 00:00:00 2001 From: BKadirkhodjaev Date: Thu, 19 Dec 2024 19:01:37 +0500 Subject: [PATCH 4/8] [MODORDERS-1209-2]. Add integration details validation to Send Claims --- .../folio/models/claiming/ClaimingError.java | 3 +- .../service/pieces/PiecesClaimingService.java | 125 ++++++++++-------- .../rest/impl/PiecesClaimingApiTest.java | 5 +- .../pieces/PiecesClaimingServiceTest.java | 87 +++++++++++- 4 files changed, 158 insertions(+), 62 deletions(-) diff --git a/src/main/java/org/folio/models/claiming/ClaimingError.java b/src/main/java/org/folio/models/claiming/ClaimingError.java index 4f5c8ab5e..9b8520bd3 100644 --- a/src/main/java/org/folio/models/claiming/ClaimingError.java +++ b/src/main/java/org/folio/models/claiming/ClaimingError.java @@ -11,7 +11,8 @@ public enum ClaimingError { CANNOT_RETRIEVE_CONFIG_ENTRIES("Cannot retrieve config entries"), CANNOT_GROUP_PIECES_BY_VENDOR_MESSAGE("Cannot group pieces by vendor"), CANNOT_CREATE_JOBS_AND_UPDATE_PIECES("Cannot create jobs and update pieces"), - CANNOT_FIND_A_PIECE_BY_ID("Cannot find a piece by '%s' id"); + CANNOT_FIND_A_PIECE_BY_ID("Cannot find a piece by '%s' id"), + UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS("Unable to generate claims for %s because no claim integrations exist"); private final String value; } diff --git a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java index 46771689b..dd47f300c 100644 --- a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java +++ b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java @@ -6,6 +6,7 @@ import lombok.extern.log4j.Log4j2; import one.util.streamex.StreamEx; import org.apache.commons.lang3.tuple.Pair; +import org.folio.rest.acq.model.Organization; import org.folio.rest.core.RestClient; import org.folio.rest.core.models.RequestContext; import org.folio.rest.jaxrs.model.ClaimingCollection; @@ -37,6 +38,7 @@ import static org.folio.models.claiming.ClaimingError.CANNOT_GROUP_PIECES_BY_VENDOR_MESSAGE; import static org.folio.models.claiming.ClaimingError.CANNOT_RETRIEVE_CONFIG_ENTRIES; import static org.folio.models.claiming.ClaimingError.CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY; +import static org.folio.models.claiming.ClaimingError.UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS; import static org.folio.models.claiming.IntegrationDetailField.CLAIM_PIECE_IDS; import static org.folio.models.claiming.IntegrationDetailField.EXPORT_TYPE_SPECIFIC_PARAMETERS; import static org.folio.models.claiming.IntegrationDetailField.VENDOR_EDI_ORDERS_EXPORT_CONFIG; @@ -67,8 +69,9 @@ public class PiecesClaimingService { /** * Sends claims by receiving pieces to be claimed, groups them by vendor, * updates piece statuses and finally creates jobs per vendor and associated integration details + * * @param claimingCollection An array of pieces ids - * @param requestContext Headers to make HTTP or Kafka requests + * @param requestContext Headers to make HTTP or Kafka requests * @return Future of an array of claimingResults */ public Future sendClaims(ClaimingCollection claimingCollection, RequestContext requestContext) { @@ -84,86 +87,109 @@ public Future sendClaims(ClaimingCollection claimingCollection, } var pieceIds = claimingCollection.getClaimingPieceIds().stream().toList(); log.info("sendClaims:: Received pieces to be claimed, pieceIds: {}", pieceIds); - return groupPieceIdsByVendorId(pieceIds, requestContext) - .compose(pieceIdsByVendorIds -> { - if (CollectionUtils.isEmpty(pieceIdsByVendorIds)) { + return groupPieceIdsByVendor(pieceIds, requestContext) + .compose(pieceIdsByVendors -> { + if (CollectionUtils.isEmpty(pieceIdsByVendors)) { return Future.succeededFuture(createEmptyClaimingResults(CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS.getValue())); } - log.info("sendClaims:: Using pieces by vendor id map, map: {}", pieceIdsByVendorIds); - return createJobsByVendor(claimingCollection, config, pieceIdsByVendorIds, requestContext); + pieceIdsByVendors.forEach((key, value) -> + log.info("createVendorPiecePair:: Using pieces by vendor map, vendorId: {}, piecesByVendor: {}", key.getId(), value)); + var vendorWithoutIntegrationDetails = checkVendorIntegrationDetails(config, pieceIdsByVendors); + if (Objects.nonNull(vendorWithoutIntegrationDetails)) { + return Future.succeededFuture(new ClaimingResults() + .withClaimingPieceResults(List.of(new ClaimingPieceResult().withStatus(FAILURE) + .withError(new Error().withMessage(String.format(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getValue(), vendorWithoutIntegrationDetails.getCode())))))); + } + return createJobsByVendor(claimingCollection, config, pieceIdsByVendors, requestContext); }); }) .onFailure(t -> log.error("sendClaims:: Failed send claims: {}", JsonObject.mapFrom(claimingCollection).encodePrettily(), t)); } - private Future>> groupPieceIdsByVendorId(List pieceIds, RequestContext requestContext) { - log.info("groupPieceIdsByVendorId:: Grouping pieces by vendor, pieceIds count: {}", pieceIds.size()); + private Future>> groupPieceIdsByVendor(List pieceIds, RequestContext requestContext) { + log.info("groupPieceIdsByVendor:: Grouping pieces by vendor, pieceIds count: {}", pieceIds.size()); return pieceStorageService.getPiecesByIds(pieceIds, requestContext) .compose(pieces -> { if (CollectionUtils.isEmpty(pieces)) { - log.info("groupPieceIdsByVendorId:: No pieces are found by piece ids, pieceIds: {}", pieceIds); + log.info("groupPieceIdsByVendor:: No pieces are found by piece ids, pieceIds: {}", pieceIds); return Future.succeededFuture(); } var uniquePiecePoLinePairs = pieces.stream() .filter(Objects::nonNull).filter(piece -> Objects.nonNull(piece.getId()) && Objects.nonNull(piece.getPoLineId())) .map(piece -> Pair.of(piece.getPoLineId(), piece.getId())).distinct() .toList(); - log.info("groupPieceIdsByVendorId:: Prepared unique piece-poLine pairs, pairs: {}", uniquePiecePoLinePairs); + log.info("groupPieceIdsByVendor:: Prepared unique piece-poLine pairs, pairs: {}", uniquePiecePoLinePairs); return collectResultsOnSuccess(createPieceIdByVendorFutures(pieces, uniquePiecePoLinePairs, requestContext)) - .map(PiecesClaimingService::transformAndGroupPieceIdsByVendorId); + .map(PiecesClaimingService::transformAndGroupPieceIdsByVendor); }); } - private List>> createPieceIdByVendorFutures(List pieces, List> uniquePiecePoLinePairs, - RequestContext requestContext) { - var pieceIdByVendorIdFutures = new ArrayList>>(); + private List>> createPieceIdByVendorFutures(List pieces, List> uniquePiecePoLinePairs, + RequestContext requestContext) { + var pieceIdByVendorFutures = new ArrayList>>(); uniquePiecePoLinePairs.forEach(piecePoLinePairs -> { var foundPiece = pieces.stream() .filter(Objects::nonNull).filter(piece -> Objects.nonNull(piece.getId())).filter(piece -> piece.getId().equals(piecePoLinePairs.getRight())) .findFirst().orElseThrow(() -> new NoSuchElementException(String.format(CANNOT_FIND_A_PIECE_BY_ID.getValue(), piecePoLinePairs.getRight()))); - var pieceIdByVendorIdFuture = createVendorPiecePair(piecePoLinePairs, foundPiece, requestContext); - if (Objects.nonNull(pieceIdByVendorIdFuture)) { - pieceIdByVendorIdFutures.add(pieceIdByVendorIdFuture); + var pieceIdByVendorFuture = createVendorPiecePair(piecePoLinePairs, foundPiece, requestContext); + if (Objects.nonNull(pieceIdByVendorFuture)) { + pieceIdByVendorFutures.add(pieceIdByVendorFuture); } }); - return pieceIdByVendorIdFutures; + return pieceIdByVendorFutures; } - private Future> createVendorPiecePair(Pair piecePoLinePairs, - Piece piece, RequestContext requestContext) { + private Future> createVendorPiecePair(Pair piecePoLinePairs, + Piece piece, RequestContext requestContext) { if (!piece.getReceivingStatus().equals(Piece.ReceivingStatus.LATE)) { log.info("createVendorPiecePair:: Ignoring processing of a piece not in LATE state, piece id: {}", piece.getId()); return Future.succeededFuture(); } return purchaseOrderLineService.getOrderLineById(piecePoLinePairs.getLeft(), requestContext) .compose(poLine -> purchaseOrderStorageService.getPurchaseOrderById(poLine.getPurchaseOrderId(), requestContext) - .compose(purchaseOrder -> organizationService.getVendorById(purchaseOrder.getVendor(), requestContext))) + .compose(purchaseOrder -> organizationService.getVendorById(purchaseOrder.getVendor(), requestContext))) .map(vendor -> { if (Objects.nonNull(vendor) && Boolean.TRUE.equals(vendor.getIsVendor())) { - return Pair.of(vendor.getId(), piecePoLinePairs.getRight()); + return Pair.of(vendor, piecePoLinePairs.getRight()); } return null; }); } - private static Map> transformAndGroupPieceIdsByVendorId(List> piecesByVendorList) { + private Organization checkVendorIntegrationDetails(JsonObject config, Map> pieceIdsByVendors) { + return pieceIdsByVendors.keySet().stream() + .filter(vendor -> { + var vendorIntegrationDetails = config.stream() + .filter(configEntry -> isExportTypeClaimsAndCorrectVendorId(vendor.getId(), configEntry)) + .toList(); + log.info("checkVendorIntegrationDetails:: Found vendor integration details, vendorId: {}, integrationDetails: {}", vendor.getId(), vendorIntegrationDetails); + return vendorIntegrationDetails.isEmpty(); + }) + .findFirst().orElse(null); + } + + private boolean isExportTypeClaimsAndCorrectVendorId(String vendorId, Map.Entry configEntry) { + return configEntry.getKey().startsWith(String.format("%s_%s", EXPORT_TYPE_CLAIMS, vendorId)) && Objects.nonNull(configEntry.getValue()); + } + + private static Map> transformAndGroupPieceIdsByVendor(List> piecesByVendorList) { return StreamEx.of(piecesByVendorList).distinct().filter(Objects::nonNull) .groupingBy(Pair::getKey, mapping(Pair::getValue, toList())); } private Future createJobsByVendor(ClaimingCollection claimingCollection, JsonObject config, - Map> pieceIdsByVendorId, RequestContext requestContext) { - log.info("createJobsByVendor:: Creating jobs by vendor, vendors by pieces count: {}", pieceIdsByVendorId.size()); - if (CollectionUtils.isEmpty(pieceIdsByVendorId)) { - log.info("createJobsByVendor:: No jobs are created, pieceIdsByVendorId is empty"); + Map> pieceIdsByVendor, RequestContext requestContext) { + log.info("createJobsByVendor:: Creating jobs by vendor, vendors by pieces count: {}", pieceIdsByVendor.size()); + if (CollectionUtils.isEmpty(pieceIdsByVendor)) { + log.info("createJobsByVendor:: No jobs are created, pieceIdsByVendor is empty"); return Future.succeededFuture(new ClaimingResults() - .withClaimingPieceResults(createErrorClaimingResults(pieceIdsByVendorId, CANNOT_GROUP_PIECES_BY_VENDOR_MESSAGE.getValue()))); + .withClaimingPieceResults(createErrorClaimingResults(pieceIdsByVendor, CANNOT_GROUP_PIECES_BY_VENDOR_MESSAGE.getValue()))); } - return collectResultsOnSuccess(createUpdatePiecesAndJobFutures(claimingCollection, config, pieceIdsByVendorId, requestContext)) + return collectResultsOnSuccess(createUpdatePiecesAndJobFutures(claimingCollection, config, pieceIdsByVendor, requestContext)) .map(updatedPieceLists -> { if (CollectionUtils.isEmpty(updatedPieceLists)) { log.info("createJobsByVendor:: No pieces were processed for claiming"); - return new ClaimingResults().withClaimingPieceResults(createErrorClaimingResults(pieceIdsByVendorId, CANNOT_CREATE_JOBS_AND_UPDATE_PIECES.getValue())); + return new ClaimingResults().withClaimingPieceResults(createErrorClaimingResults(pieceIdsByVendor, CANNOT_CREATE_JOBS_AND_UPDATE_PIECES.getValue())); } var successClaimingPieceResults = createSuccessClaimingResults(updatedPieceLists); log.info("createJobsByVendor:: Successfully processed pieces for claiming, count: {}", successClaimingPieceResults.size()); @@ -174,15 +200,14 @@ private Future createJobsByVendor(ClaimingCollection claimingCo } private List>> createUpdatePiecesAndJobFutures(ClaimingCollection claimingCollection, JsonObject config, - Map> pieceIdsByVendorId, RequestContext requestContext) { + Map> pieceIdsByVendor, RequestContext requestContext) { var updatePiecesAndJobFutures = new ArrayList>>(); - pieceIdsByVendorId.forEach((vendorId, pieceIds) -> config.stream() - .filter(pieceIdsByVendorIdEntry -> isExportTypeClaimsAndCorrectVendorId(vendorId, pieceIdsByVendorIdEntry) - && Objects.nonNull(pieceIdsByVendorIdEntry.getValue())) - .forEach(pieceIdsByVendorIdEntry -> { - log.info("createJobsByVendor:: Preparing job integration detail for vendor, vendor id: {}, pieces: {}, job key: {}", - vendorId, pieceIds.size(), pieceIdsByVendorIdEntry.getKey()); - updatePiecesAndJobFutures.add(updatePiecesAndCreateJob(claimingCollection, pieceIds, pieceIdsByVendorIdEntry, requestContext)); + pieceIdsByVendor.forEach((vendor, pieceIds) -> config.stream() + .filter(pieceIdsByVendorIdEntry -> isExportTypeClaimsAndCorrectVendorId(vendor.getId(), pieceIdsByVendorIdEntry)) + .forEach(configEntry -> { + log.info("createUpdatePiecesAndJobFutures:: Preparing job integration detail for vendor, vendor id: {}, pieces: {}, job key: {}", + vendor.getId(), pieceIds.size(), configEntry.getKey()); + updatePiecesAndJobFutures.add(updatePiecesAndCreateJob(claimingCollection, pieceIds, configEntry, requestContext)); })); return updatePiecesAndJobFutures; } @@ -193,27 +218,23 @@ private static ClaimingResults createEmptyClaimingResults(String message) { private List createSuccessClaimingResults(List> updatedPieceLists) { return updatedPieceLists.stream().flatMap(Collection::stream).distinct() - .map(pieceId -> new ClaimingPieceResult().withPieceId(pieceId).withStatus(SUCCESS)) - .toList(); - } - - private List createErrorClaimingResults(Map> pieceIdsByVendorId, String message) { - return pieceIdsByVendorId.values().stream() - .flatMap(Collection::stream) - .map(pieceId -> new ClaimingPieceResult().withPieceId(pieceId).withStatus(FAILURE).withError(new Error().withMessage(message))) - .toList(); + .map(pieceId -> new ClaimingPieceResult().withPieceId(pieceId).withStatus(SUCCESS)) + .toList(); } - private static boolean isExportTypeClaimsAndCorrectVendorId(String vendorId, Map.Entry pieceIdsByVendorIdEntry) { - return pieceIdsByVendorIdEntry.getKey().startsWith(String.format("%s_%s", EXPORT_TYPE_CLAIMS, vendorId)); + private List createErrorClaimingResults(Map> pieceIdsByVendor, String message) { + return pieceIdsByVendor.values().stream() + .flatMap(Collection::stream) + .map(pieceId -> new ClaimingPieceResult().withPieceId(pieceId).withStatus(FAILURE).withError(new Error().withMessage(message))) + .toList(); } private Future> updatePiecesAndCreateJob(ClaimingCollection claimingCollection, List pieceIds, - Map.Entry pieceIdsByVendorIdEntry, RequestContext requestContext) { - log.info("updatePiecesAndCreateJob:: Updating pieces and creating a job, job key: {}, count: {}", pieceIdsByVendorIdEntry.getKey(), pieceIds.size()); + Map.Entry configEntry, RequestContext requestContext) { + log.info("updatePiecesAndCreateJob:: Updating pieces and creating a job, job key: {}, count: {}", configEntry.getKey(), pieceIds.size()); return pieceUpdateFlowManager.updatePiecesStatuses(pieceIds, PieceBatchStatusCollection.ReceivingStatus.CLAIM_SENT, claimingCollection.getClaimingInterval(), claimingCollection.getInternalNote(), claimingCollection.getExternalNote(), requestContext) - .compose(v -> createJob(pieceIdsByVendorIdEntry.getKey(), pieceIdsByVendorIdEntry.getValue(), pieceIds, requestContext).map(pieceIds)); + .compose(v -> createJob(configEntry.getKey(), configEntry.getValue(), pieceIds, requestContext).map(pieceIds)); } private Future createJob(String configKey, Object configValue, List pieceIds, RequestContext requestContext) { diff --git a/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java b/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java index 89bec2300..2a59dd47e 100644 --- a/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java +++ b/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java @@ -205,7 +205,6 @@ void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx assertThat(jobExecutions, hasSize(dto.jobExecutions)); assertThat(response.getClaimingPieceResults().size(), equalTo(dto.claimingResults)); pieceUpdates.forEach(pieceUpdate -> logger.info("Updated piece: {}", pieceUpdate.encodePrettily())); - var claimedPieceIds = jobCreations.stream() .peek(job -> logger.info("Created job: {}", JsonObject.mapFrom(job).encodePrettily())) .map(job -> job.getJsonObject(EXPORT_TYPE_SPECIFIC_PARAMETERS.getValue()) @@ -217,13 +216,13 @@ void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx response.getClaimingPieceResults() .forEach(result -> { - assertThat(result.getPieceId(), not(nullValue())); - assertThat(result.getStatus(), is(expectedStatus)); if (expectedStatus == SUCCESS) { + assertThat(result.getPieceId(), not(nullValue())); assertThat(result.getError(), is(nullValue())); } else { assertThat(result.getError(), is(notNullValue())); } + assertThat(result.getStatus(), is(expectedStatus)); }); } } diff --git a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java index 81475bbd3..a9c29ca32 100644 --- a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java +++ b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.UUID; import static org.folio.models.claiming.ClaimingError.CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS; import static org.folio.models.claiming.ClaimingError.CANNOT_RETRIEVE_CONFIG_ENTRIES; @@ -39,6 +40,7 @@ import static org.folio.orders.utils.ResourcePathResolver.DATA_EXPORT_SPRING_CREATE_JOB; import static org.folio.orders.utils.ResourcePathResolver.resourcesPath; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -142,7 +144,7 @@ void testSendClaims_success(VertxTestContext testContext) { when(pieceStorageService.getPiecesByIds(any(), any())).thenReturn(Future.succeededFuture(List.of(new Piece().withId("pieceId1").withPoLineId("poLineId1").withReceivingStatus(Piece.ReceivingStatus.LATE)))); when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId1"))); when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId"))); - when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withIsVendor(true))); + when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withCode("VENDOR").withIsVendor(true))); when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(restClient.post(eq(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)), any(), any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("status", "CREATED"))); when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); @@ -168,7 +170,7 @@ void testSendClaims_successWithTwoPieces(VertxTestContext testContext) { ))); when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId1"))); when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId"))); - when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withIsVendor(true))); + when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withCode("VENDOR").withIsVendor(true))); when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(restClient.post(eq(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)), any(), any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("status", "CREATED"))); when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); @@ -184,6 +186,71 @@ void testSendClaims_successWithTwoPieces(VertxTestContext testContext) { }))); } + @Test + void testSendClaims_missingOrganizationIntegrationDetailsForTwoOrganizations(VertxTestContext testContext) { + var pieceId = UUID.randomUUID().toString(); + var piece = new Piece().withId(pieceId).withPoLineId("poLineId").withReceivingStatus(Piece.ReceivingStatus.LATE); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of(pieceId)); + var requestContext = mock(RequestContext.class); + + when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject() + .put("CLAIMS_vendorId1", createIntegrationDetail()))); + when(pieceStorageService.getPiecesByIds(any(), any())).thenReturn(Future.succeededFuture(List.of(piece))); + when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId"))); + when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId2"))); + when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId2").withCode("VENDOR2").withIsVendor(true))); + + piecesClaimingService.sendClaims(claimingCollection, requestContext) + .onComplete(testContext.succeeding(result -> testContext.verify(() -> { + assertEquals(1, result.getClaimingPieceResults().size()); + var claimingPieceResult = result.getClaimingPieceResults().get(0); + assertNull(claimingPieceResult.getPieceId()); + assertEquals(ClaimingPieceResult.Status.FAILURE, claimingPieceResult.getStatus()); + assertEquals("Unable to generate claims for VENDOR2 because no claim integrations exist", claimingPieceResult.getError().getMessage()); + testContext.completeNow(); + }))); + } + + @Test + void testSendClaims_missingOrganizationIntegrationDetailsForThreeOrganizations(VertxTestContext testContext) { + var pieceId1 = UUID.randomUUID().toString(); + var pieceId2 = UUID.randomUUID().toString(); + var pieceId3 = UUID.randomUUID().toString(); + var piece1 = new Piece().withId(pieceId1).withPoLineId("poLineId1").withReceivingStatus(Piece.ReceivingStatus.LATE); + var piece2 = new Piece().withId(pieceId2).withPoLineId("poLineId2").withReceivingStatus(Piece.ReceivingStatus.LATE); + var piece3 = new Piece().withId(pieceId3).withPoLineId("poLineId3").withReceivingStatus(Piece.ReceivingStatus.LATE); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of(pieceId1, pieceId2, pieceId3)); + var requestContext = mock(RequestContext.class); + + when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject() + .put("CLAIMS_vendorId1", createIntegrationDetail()))); + when(pieceStorageService.getPiecesByIds(any(), any())).thenReturn(Future.succeededFuture(List.of(piece1, piece2, piece3))); + when(purchaseOrderLineService.getOrderLineById(any(), any())).thenAnswer(invocation -> { + String poLineId = invocation.getArgument(0); + return Future.succeededFuture(new PoLine().withPurchaseOrderId(poLineId.equals("poLineId1") ? "orderId1" : poLineId.equals("poLineId2") ? "orderId2" : "orderId3")); + }); + when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenAnswer(invocation -> { + String orderId = invocation.getArgument(0); + return Future.succeededFuture(new PurchaseOrder().withVendor(orderId.equals("orderId1") ? "vendorId1" : orderId.equals("orderId2") ? "vendorId2" : "vendorId3")); + }); + when(organizationService.getVendorById(any(), any())).thenAnswer(invocation -> { + String vendorId = invocation.getArgument(0); + return Future.succeededFuture(new Organization().withId(vendorId).withCode(vendorId.equals("vendorId1") ? "VENDOR1" : vendorId.equals("vendorId2") ? "VENDOR2" : "VENDOR3").withIsVendor(true)); + }); + + piecesClaimingService.sendClaims(claimingCollection, requestContext) + .onComplete(testContext.succeeding(result -> testContext.verify(() -> { + assertEquals(1, result.getClaimingPieceResults().size()); + var claimingPieceResults = result.getClaimingPieceResults(); + claimingPieceResults.forEach(claimingPieceResult -> { + assertNull(claimingPieceResult.getPieceId()); + assertEquals(ClaimingPieceResult.Status.FAILURE, claimingPieceResult.getStatus()); + assertEquals("Unable to generate claims for VENDOR2 because no claim integrations exist", claimingPieceResult.getError().getMessage()); + }); + testContext.completeNow(); + }))); + } + @Test void testSendClaims_successWithMultipleOrganizations(VertxTestContext testContext) { var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1", "pieceId2", "pieceId3", "pieceId4", "pieceId5")).withClaimingInterval(1); @@ -213,7 +280,7 @@ void testSendClaims_successWithMultipleOrganizations(VertxTestContext testContex when(organizationService.getVendorById(any(), any())).thenAnswer(invocation -> { String vendorId = invocation.getArgument(0); - return Future.succeededFuture(new Organization().withId(vendorId).withIsVendor(true)); + return Future.succeededFuture(new Organization().withId(vendorId).withCode(vendorId.equals("vendorId1") ? "VENDOR1" : "VENDOR2").withIsVendor(true)); }); when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); @@ -245,7 +312,9 @@ void testSendClaims_successWithMultipleOrganizationsAndOneIntegrationDetail(Vert var requestContext = mock(RequestContext.class); when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject() - .put("CLAIMS_vendorId1", createIntegrationDetail()))); + .put("CLAIMS_vendorId1", createIntegrationDetail()) + .put("CLAIMS_vendorId2", createIntegrationDetail()) + )); when(pieceStorageService.getPiecesByIds(any(), any())).thenReturn(Future.succeededFuture(List.of( new Piece().withId("pieceId1").withPoLineId("poLineId1").withReceivingStatus(Piece.ReceivingStatus.LATE), @@ -267,7 +336,7 @@ void testSendClaims_successWithMultipleOrganizationsAndOneIntegrationDetail(Vert when(organizationService.getVendorById(any(), any())).thenAnswer(invocation -> { String vendorId = invocation.getArgument(0); - return Future.succeededFuture(new Organization().withId(vendorId).withIsVendor(true)); + return Future.succeededFuture(new Organization().withId(vendorId).withCode(vendorId.equals("vendorId1") ? "VENDOR1" : "VENDOR2").withIsVendor(true)); }); when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); @@ -276,13 +345,19 @@ void testSendClaims_successWithMultipleOrganizationsAndOneIntegrationDetail(Vert piecesClaimingService.sendClaims(claimingCollection, requestContext) .onComplete(testContext.succeeding(result -> testContext.verify(() -> { - assertEquals(2, result.getClaimingPieceResults().size()); + assertEquals(5, result.getClaimingPieceResults().size()); var copiedSortedResults = new ArrayList<>(result.getClaimingPieceResults()); copiedSortedResults.sort(Comparator.comparing(ClaimingPieceResult::getPieceId)); assertEquals("pieceId1", copiedSortedResults.get(0).getPieceId()); assertEquals(ClaimingPieceResult.Status.SUCCESS, copiedSortedResults.get(0).getStatus()); assertEquals("pieceId2", copiedSortedResults.get(1).getPieceId()); assertEquals(ClaimingPieceResult.Status.SUCCESS, copiedSortedResults.get(1).getStatus()); + assertEquals("pieceId3", copiedSortedResults.get(2).getPieceId()); + assertEquals(ClaimingPieceResult.Status.SUCCESS, copiedSortedResults.get(1).getStatus()); + assertEquals("pieceId4", copiedSortedResults.get(3).getPieceId()); + assertEquals(ClaimingPieceResult.Status.SUCCESS, copiedSortedResults.get(1).getStatus()); + assertEquals("pieceId5", copiedSortedResults.get(4).getPieceId()); + assertEquals(ClaimingPieceResult.Status.SUCCESS, copiedSortedResults.get(1).getStatus()); testContext.completeNow(); }))); } From e06264f7b31ca3c0837b56d7bf3ab4c60729f3cb Mon Sep 17 00:00:00 2001 From: BKadirkhodjaev Date: Thu, 19 Dec 2024 19:26:50 +0500 Subject: [PATCH 5/8] [MODORDERS-1209-2]. Change validation to throw an exception --- .../service/pieces/PiecesClaimingService.java | 8 +- .../rest/impl/PiecesClaimingApiTest.java | 94 +++++++++++-------- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java index dd47f300c..2b725342a 100644 --- a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java +++ b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java @@ -6,13 +6,16 @@ import lombok.extern.log4j.Log4j2; import one.util.streamex.StreamEx; import org.apache.commons.lang3.tuple.Pair; +import org.folio.HttpStatus; import org.folio.rest.acq.model.Organization; import org.folio.rest.core.RestClient; +import org.folio.rest.core.exceptions.HttpException; import org.folio.rest.core.models.RequestContext; import org.folio.rest.jaxrs.model.ClaimingCollection; import org.folio.rest.jaxrs.model.ClaimingPieceResult; import org.folio.rest.jaxrs.model.ClaimingResults; import org.folio.rest.jaxrs.model.Error; +import org.folio.rest.jaxrs.model.Errors; import org.folio.rest.jaxrs.model.Piece; import org.folio.rest.jaxrs.model.PieceBatchStatusCollection; import org.folio.service.caches.ConfigurationEntriesCache; @@ -96,9 +99,8 @@ public Future sendClaims(ClaimingCollection claimingCollection, log.info("createVendorPiecePair:: Using pieces by vendor map, vendorId: {}, piecesByVendor: {}", key.getId(), value)); var vendorWithoutIntegrationDetails = checkVendorIntegrationDetails(config, pieceIdsByVendors); if (Objects.nonNull(vendorWithoutIntegrationDetails)) { - return Future.succeededFuture(new ClaimingResults() - .withClaimingPieceResults(List.of(new ClaimingPieceResult().withStatus(FAILURE) - .withError(new Error().withMessage(String.format(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getValue(), vendorWithoutIntegrationDetails.getCode())))))); + var errors = List.of(new Error().withMessage(String.format(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getValue(), vendorWithoutIntegrationDetails.getCode()))); + throw new HttpException(HttpStatus.HTTP_UNPROCESSABLE_ENTITY.toInt(), new Errors().withErrors(errors).withTotalRecords(errors.size())); } return createJobsByVendor(claimingCollection, config, pieceIdsByVendors, requestContext); }); diff --git a/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java b/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java index 2a59dd47e..0e747c885 100644 --- a/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java +++ b/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java @@ -1,5 +1,6 @@ package org.folio.rest.impl; +import io.netty.handler.codec.http.HttpResponseStatus; import io.restassured.http.Header; import io.vertx.core.json.JsonObject; import io.vertx.junit5.VertxExtension; @@ -8,6 +9,7 @@ import org.folio.ApiTestSuite; import org.folio.Organization; import org.folio.config.ApplicationConfig; +import org.folio.rest.core.exceptions.HttpException; import org.folio.rest.jaxrs.model.ClaimingCollection; import org.folio.rest.jaxrs.model.ClaimingPieceResult; import org.folio.rest.jaxrs.model.ClaimingResults; @@ -29,6 +31,7 @@ import java.util.stream.Stream; import static io.netty.handler.codec.http.HttpResponseStatus.CREATED; +import static io.netty.handler.codec.http.HttpResponseStatus.UNPROCESSABLE_ENTITY; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.folio.RestTestUtils.prepareHeaders; import static org.folio.RestTestUtils.verifyPostResponse; @@ -106,8 +109,8 @@ private static Stream testPostPiecesClaimArgs() { var payloadFile = "send-claims-1-piece-1-vendor-1-job.json"; var mockHitDto = new MockHitDto(3, 2, 2, 1, 1, 1, 1, 1); return Stream.of( - Arguments.of("One piece One vendor One Job", 0, 17, 69, mockHitDto, payloadFile, EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10_CLAIMS, SUCCESS), - Arguments.of("One piece One vendor No Job", 0, 17, 69, null, payloadFile, EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10, FAILURE) + Arguments.of("One piece One vendor One Job", 0, 17, 69, mockHitDto, payloadFile, EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10_CLAIMS, SUCCESS, CREATED), + Arguments.of("One piece One vendor No Job", 0, 17, 69, null, payloadFile, EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10, FAILURE, UNPROCESSABLE_ENTITY) ); } @@ -139,7 +142,8 @@ public MockHitDto(int pieceSearches, int polSearches, int purchaseOrderRetrieval @ParameterizedTest @MethodSource("testPostPiecesClaimArgs") void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx, MockHitDto dto, - String payloadFile, Header header, ClaimingPieceResult.Status expectedStatus) { + String payloadFile, Header header, ClaimingPieceResult.Status expectedStatus, + HttpResponseStatus expectedResponseStatus) { logger.info("Testing postPiecesClaim, name: {}", name); var organization = getMockAsJson(ORGANIZATION_COLLECTION) @@ -162,9 +166,8 @@ void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx addMockEntry(PIECES_STORAGE, piece); var mockDataPath = BASE_MOCK_DATA_PATH + CLAIMING_MOCK_DATA_FOLDER + payloadFile; - var request = getMockAsJson(mockDataPath).mapTo(ClaimingCollection.class); - var response = verifyPostResponse(PIECES_CLAIMING_ENDPOINT, JsonObject.mapFrom(request).encode(), prepareHeaders(header), APPLICATION_JSON, CREATED.code()) - .as(ClaimingResults.class); + var request = getMockAsJson(mockDataPath).mapTo(ClaimingCollection.class); + var response = verifyPostResponse(PIECES_CLAIMING_ENDPOINT, JsonObject.mapFrom(request).encode(), prepareHeaders(header), APPLICATION_JSON, expectedResponseStatus.code()); // Filter out any dummy pieces without ids that are loaded from other tests var pieceSearches = getPieceSearches().stream() @@ -182,47 +185,56 @@ void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx .map(JsonObject::mapFrom).map(json -> json.getString(ENTRY_ID)) .filter(Objects::nonNull).filter(poLineId -> poLineId.equals(purchaseOrder.getId())) .toList(); + purchaseOrderRetrievals.forEach(entry -> logger.info("PurchaseOrders: {}", entry)); + var organizationSearches = getOrganizationSearches(); var pieceUpdates = getPieceUpdates(); var jobCreations = getDataExportSpringJobCreations(); var jobExecutions = getDataExportSpringJobExecutions(); - if (Objects.nonNull(dto)) { - assertThat(pieceSearches, not(nullValue())); - assertThat(polSearches, not(nullValue())); - assertThat(purchaseOrderRetrievals, not(nullValue())); - assertThat(organizationSearches, not(nullValue())); - assertThat(pieceUpdates, not(nullValue())); - assertThat(jobCreations, not(nullValue())); - assertThat(jobExecutions, not(nullValue())); - assertThat(pieceSearches, hasSize(dto.pieceSearches)); - assertThat(polSearches, hasSize(dto.polSearches)); - assertThat(purchaseOrderRetrievals, hasSize(dto.purchaseOrderRetrievals)); - assertThat(organizationSearches, hasSize(dto.organizationSearches)); - assertThat(pieceUpdates, hasSize(dto.pieceUpdates)); - assertThat(jobCreations, hasSize(dto.jobCreations)); - assertThat(jobExecutions, hasSize(dto.jobExecutions)); - assertThat(response.getClaimingPieceResults().size(), equalTo(dto.claimingResults)); - pieceUpdates.forEach(pieceUpdate -> logger.info("Updated piece: {}", pieceUpdate.encodePrettily())); - var claimedPieceIds = jobCreations.stream() - .peek(job -> logger.info("Created job: {}", JsonObject.mapFrom(job).encodePrettily())) - .map(job -> job.getJsonObject(EXPORT_TYPE_SPECIFIC_PARAMETERS.getValue()) - .getJsonObject(VENDOR_EDI_ORDERS_EXPORT_CONFIG.getValue()) - .getJsonArray(CLAIM_PIECE_IDS.getValue()).size()) - .mapToInt(value -> value).sum(); - assertThat(claimedPieceIds, equalTo(request.getClaimingPieceIds().size())); + pieceUpdates.forEach(pieceUpdate -> logger.info("Updated piece: {}", pieceUpdate.encodePrettily())); + + if (response instanceof ClaimingResults claimingResults) { + if (Objects.nonNull(dto)) { + assertThat(pieceSearches, not(nullValue())); + assertThat(polSearches, not(nullValue())); + assertThat(purchaseOrderRetrievals, not(nullValue())); + assertThat(organizationSearches, not(nullValue())); + assertThat(pieceUpdates, not(nullValue())); + assertThat(jobCreations, not(nullValue())); + assertThat(jobExecutions, not(nullValue())); + assertThat(pieceSearches, hasSize(dto.pieceSearches)); + assertThat(polSearches, hasSize(dto.polSearches)); + assertThat(purchaseOrderRetrievals, hasSize(dto.purchaseOrderRetrievals)); + assertThat(organizationSearches, hasSize(dto.organizationSearches)); + assertThat(pieceUpdates, hasSize(dto.pieceUpdates)); + assertThat(jobCreations, hasSize(dto.jobCreations)); + assertThat(jobExecutions, hasSize(dto.jobExecutions)); + assertThat(claimingResults.getClaimingPieceResults().size(), equalTo(dto.claimingResults)); + + var claimedPieceIds = jobCreations.stream() + .peek(job -> logger.info("Created job: {}", JsonObject.mapFrom(job).encodePrettily())) + .map(job -> job.getJsonObject(EXPORT_TYPE_SPECIFIC_PARAMETERS.getValue()) + .getJsonObject(VENDOR_EDI_ORDERS_EXPORT_CONFIG.getValue()) + .getJsonArray(CLAIM_PIECE_IDS.getValue()).size()) + .mapToInt(value -> value).sum(); + assertThat(claimedPieceIds, equalTo(request.getClaimingPieceIds().size())); + + claimingResults.getClaimingPieceResults() + .forEach(result -> { + if (expectedStatus == SUCCESS) { + assertThat(result.getPieceId(), not(nullValue())); + assertThat(result.getError(), is(nullValue())); + } else { + assertThat(result.getError(), is(notNullValue())); + } + assertThat(result.getStatus(), is(expectedStatus)); + }); + } + } else if (response instanceof HttpException exception) { + assertThat(exception.getErrors().getErrors().size(), is(1)); + assertThat(exception.getError().getMessage(), is("Unable to generate claims for AMAZ because no claim integrations exist")); } - - response.getClaimingPieceResults() - .forEach(result -> { - if (expectedStatus == SUCCESS) { - assertThat(result.getPieceId(), not(nullValue())); - assertThat(result.getError(), is(nullValue())); - } else { - assertThat(result.getError(), is(notNullValue())); - } - assertThat(result.getStatus(), is(expectedStatus)); - }); } } From 812dd4539f94a5c795f528e045b0385e82e92d7c Mon Sep 17 00:00:00 2001 From: BKadirkhodjaev Date: Thu, 19 Dec 2024 21:44:08 +0500 Subject: [PATCH 6/8] [MODORDERS-1209-2]. Throw http exception for all handled exception cases --- .../folio/models/claiming/ClaimingError.java | 18 -- .../rest/core/exceptions/ErrorCodes.java | 9 +- .../service/pieces/PiecesClaimingService.java | 95 ++++---- .../rest/impl/PiecesClaimingApiTest.java | 9 +- .../pieces/PiecesClaimingServiceTest.java | 216 +++++++++++------- 5 files changed, 200 insertions(+), 147 deletions(-) delete mode 100644 src/main/java/org/folio/models/claiming/ClaimingError.java diff --git a/src/main/java/org/folio/models/claiming/ClaimingError.java b/src/main/java/org/folio/models/claiming/ClaimingError.java deleted file mode 100644 index 9b8520bd3..000000000 --- a/src/main/java/org/folio/models/claiming/ClaimingError.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.folio.models.claiming; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public enum ClaimingError { - CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY("Cannot send claims, piece ids are empty"), - CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS("Cannot find pieces with LATE status to process"), - CANNOT_RETRIEVE_CONFIG_ENTRIES("Cannot retrieve config entries"), - CANNOT_GROUP_PIECES_BY_VENDOR_MESSAGE("Cannot group pieces by vendor"), - CANNOT_CREATE_JOBS_AND_UPDATE_PIECES("Cannot create jobs and update pieces"), - CANNOT_FIND_A_PIECE_BY_ID("Cannot find a piece by '%s' id"), - UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS("Unable to generate claims for %s because no claim integrations exist"); - - private final String value; -} 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 8515cd7c6..18dbfde9a 100644 --- a/src/main/java/org/folio/rest/core/exceptions/ErrorCodes.java +++ b/src/main/java/org/folio/rest/core/exceptions/ErrorCodes.java @@ -132,7 +132,14 @@ public enum ErrorCodes { RECEIVING_WORKFLOW_INCORRECT_FOR_BINDARY_ACTIVE("receivingWorkflowIncorrectForBindaryActive", "When PoLine is bindery active, its receiving workflow must be set to 'Independent order and receipt quantity'"), BIND_ITEM_MUST_INCLUDE_EITHER_HOLDING_ID_OR_LOCATION_ID("bindItemMustIncludeEitherHoldingIdOrLocationId", "During binding pieces, the bindItem object must have either holdingId or locationId field populated"), BUDGET_NOT_FOUND_FOR_FISCAL_YEAR("budgetNotFoundForFiscalYear", "Could not find an active budget for a fund with the current fiscal year of another fund in the fund distribution"), - LAST_PIECE("lastPiece", "The piece cannot be deleted because it is the last piece for the poLine with Receiving Workflow 'Synchronized order and receipt quantity' and cost quantity '1'"),; + LAST_PIECE("lastPiece", "The piece cannot be deleted because it is the last piece for the poLine with Receiving Workflow 'Synchronized order and receipt quantity' and cost quantity '1'"), + CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY("cannotSendClaimsPieceIdsAreEmpty", "Cannot send claims, piece ids are empty"), + CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS("cannotFindPiecesWithLatestStatusToProcess", "Cannot find pieces with LATE status to process"), + CANNOT_RETRIEVE_CONFIG_ENTRIES("cannotRetrieveConfigEntries", "Cannot retrieve config entries"), + CANNOT_GROUP_PIECES_BY_VENDOR("cannotGroupPiecesByVendorMessage", "Cannot group pieces by vendor"), + CANNOT_CREATE_JOBS_AND_UPDATE_PIECES("cannotCreateJobsAndUpdatePieces", "Cannot create jobs and update pieces"), + CANNOT_FIND_PIECE_BY_ID("cannotFindPieceById", "Cannot find a piece by '%s' id"), + UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS("unableToGenerateClaimsForOrgNoIntegrationDetails", "Unable to generate claims for %s because no claim integrations exist"); private final String code; private final String description; diff --git a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java index 2b725342a..5175f5a0f 100644 --- a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java +++ b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java @@ -9,6 +9,7 @@ import org.folio.HttpStatus; import org.folio.rest.acq.model.Organization; import org.folio.rest.core.RestClient; +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.ClaimingCollection; @@ -16,6 +17,7 @@ import org.folio.rest.jaxrs.model.ClaimingResults; import org.folio.rest.jaxrs.model.Error; import org.folio.rest.jaxrs.model.Errors; +import org.folio.rest.jaxrs.model.Parameter; import org.folio.rest.jaxrs.model.Piece; import org.folio.rest.jaxrs.model.PieceBatchStatusCollection; import org.folio.service.caches.ConfigurationEntriesCache; @@ -32,16 +34,10 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.stream.Collectors; import static java.util.stream.Collectors.mapping; import static java.util.stream.Collectors.toList; -import static org.folio.models.claiming.ClaimingError.CANNOT_CREATE_JOBS_AND_UPDATE_PIECES; -import static org.folio.models.claiming.ClaimingError.CANNOT_FIND_A_PIECE_BY_ID; -import static org.folio.models.claiming.ClaimingError.CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS; -import static org.folio.models.claiming.ClaimingError.CANNOT_GROUP_PIECES_BY_VENDOR_MESSAGE; -import static org.folio.models.claiming.ClaimingError.CANNOT_RETRIEVE_CONFIG_ENTRIES; -import static org.folio.models.claiming.ClaimingError.CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY; -import static org.folio.models.claiming.ClaimingError.UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS; import static org.folio.models.claiming.IntegrationDetailField.CLAIM_PIECE_IDS; import static org.folio.models.claiming.IntegrationDetailField.EXPORT_TYPE_SPECIFIC_PARAMETERS; import static org.folio.models.claiming.IntegrationDetailField.VENDOR_EDI_ORDERS_EXPORT_CONFIG; @@ -50,7 +46,13 @@ import static org.folio.orders.utils.ResourcePathResolver.DATA_EXPORT_SPRING_CREATE_JOB; import static org.folio.orders.utils.ResourcePathResolver.DATA_EXPORT_SPRING_EXECUTE_JOB; import static org.folio.orders.utils.ResourcePathResolver.resourcesPath; -import static org.folio.rest.jaxrs.model.ClaimingPieceResult.Status.FAILURE; +import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_CREATE_JOBS_AND_UPDATE_PIECES; +import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS; +import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_FIND_PIECE_BY_ID; +import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_GROUP_PIECES_BY_VENDOR; +import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_RETRIEVE_CONFIG_ENTRIES; +import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY; +import static org.folio.rest.core.exceptions.ErrorCodes.UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS; import static org.folio.rest.jaxrs.model.ClaimingPieceResult.Status.SUCCESS; @Log4j2 @@ -60,6 +62,8 @@ public class PiecesClaimingService { private static final String JOB_STATUS = "status"; private static final String EXPORT_TYPE_CLAIMS = "CLAIMS"; + private static final String VENDOR_CODE_PARAMETER = "vendorCode"; + private static final String PIECE_ID_PARAMETER = "pieceId"; private final ConfigurationEntriesCache configurationEntriesCache; private final PieceStorageService pieceStorageService; @@ -79,28 +83,29 @@ public class PiecesClaimingService { */ public Future sendClaims(ClaimingCollection claimingCollection, RequestContext requestContext) { if (CollectionUtils.isEmpty(claimingCollection.getClaimingPieceIds())) { - log.info("sendClaims:: No claims are sent, claiming piece ids are empty"); - return Future.succeededFuture(createEmptyClaimingResults(CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY.getValue())); + log.info("sendClaims:: Cannot send claims piece ids are empty - No claims are sent"); + throwHttpException(CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY, claimingCollection, HttpStatus.HTTP_BAD_REQUEST); } return configurationEntriesCache.loadConfiguration(DATA_EXPORT_SPRING_CONFIG_MODULE_NAME, requestContext) .compose(config -> { if (CollectionUtils.isEmpty(config.getMap())) { - log.info("sendClaims:: No claims are sent, config has no entries"); - return Future.succeededFuture(createEmptyClaimingResults(CANNOT_RETRIEVE_CONFIG_ENTRIES.getValue())); + log.info("sendClaims:: Cannot retrieve config entries - No claims are sent"); + throwHttpException(CANNOT_RETRIEVE_CONFIG_ENTRIES, claimingCollection, HttpStatus.HTTP_BAD_REQUEST); } var pieceIds = claimingCollection.getClaimingPieceIds().stream().toList(); log.info("sendClaims:: Received pieces to be claimed, pieceIds: {}", pieceIds); return groupPieceIdsByVendor(pieceIds, requestContext) .compose(pieceIdsByVendors -> { if (CollectionUtils.isEmpty(pieceIdsByVendors)) { - return Future.succeededFuture(createEmptyClaimingResults(CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS.getValue())); + log.info("sendClaims:: Cannot find pieces with late status to process - No claims are sent"); + throwHttpException(CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS, claimingCollection, HttpStatus.HTTP_BAD_REQUEST); } - pieceIdsByVendors.forEach((key, value) -> - log.info("createVendorPiecePair:: Using pieces by vendor map, vendorId: {}, piecesByVendor: {}", key.getId(), value)); + pieceIdsByVendors.forEach((vendor, piecesByVendor) -> + log.info("createVendorPiecePair:: Using pieces by vendor map, vendorId: {}, piecesByVendor: {}", vendor.getId(), piecesByVendor)); var vendorWithoutIntegrationDetails = checkVendorIntegrationDetails(config, pieceIdsByVendors); if (Objects.nonNull(vendorWithoutIntegrationDetails)) { - var errors = List.of(new Error().withMessage(String.format(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getValue(), vendorWithoutIntegrationDetails.getCode()))); - throw new HttpException(HttpStatus.HTTP_UNPROCESSABLE_ENTITY.toInt(), new Errors().withErrors(errors).withTotalRecords(errors.size())); + log.info("sendClaims:: Unable to generate claims because no claim integrations exist - No claims are sent"); + throwHttpExceptionOnMissingVendorIntegrationDetails(claimingCollection, vendorWithoutIntegrationDetails); } return createJobsByVendor(claimingCollection, config, pieceIdsByVendors, requestContext); }); @@ -132,7 +137,7 @@ private List>> createPieceIdByVendorFutures(Li uniquePiecePoLinePairs.forEach(piecePoLinePairs -> { var foundPiece = pieces.stream() .filter(Objects::nonNull).filter(piece -> Objects.nonNull(piece.getId())).filter(piece -> piece.getId().equals(piecePoLinePairs.getRight())) - .findFirst().orElseThrow(() -> new NoSuchElementException(String.format(CANNOT_FIND_A_PIECE_BY_ID.getValue(), piecePoLinePairs.getRight()))); + .findFirst().orElseThrow(() -> new NoSuchElementException(String.format(CANNOT_FIND_PIECE_BY_ID.getDescription(), piecePoLinePairs.getRight()))); var pieceIdByVendorFuture = createVendorPiecePair(piecePoLinePairs, foundPiece, requestContext); if (Objects.nonNull(pieceIdByVendorFuture)) { pieceIdByVendorFutures.add(pieceIdByVendorFuture); @@ -183,15 +188,14 @@ private Future createJobsByVendor(ClaimingCollection claimingCo Map> pieceIdsByVendor, RequestContext requestContext) { log.info("createJobsByVendor:: Creating jobs by vendor, vendors by pieces count: {}", pieceIdsByVendor.size()); if (CollectionUtils.isEmpty(pieceIdsByVendor)) { - log.info("createJobsByVendor:: No jobs are created, pieceIdsByVendor is empty"); - return Future.succeededFuture(new ClaimingResults() - .withClaimingPieceResults(createErrorClaimingResults(pieceIdsByVendor, CANNOT_GROUP_PIECES_BY_VENDOR_MESSAGE.getValue()))); + log.info("createJobsByVendor:: Cannot group pieces by vendor - No jobs were create or pieces processed"); + throwHttpException(CANNOT_GROUP_PIECES_BY_VENDOR, claimingCollection, HttpStatus.HTTP_NOT_FOUND); } return collectResultsOnSuccess(createUpdatePiecesAndJobFutures(claimingCollection, config, pieceIdsByVendor, requestContext)) .map(updatedPieceLists -> { if (CollectionUtils.isEmpty(updatedPieceLists)) { - log.info("createJobsByVendor:: No pieces were processed for claiming"); - return new ClaimingResults().withClaimingPieceResults(createErrorClaimingResults(pieceIdsByVendor, CANNOT_CREATE_JOBS_AND_UPDATE_PIECES.getValue())); + log.info("createJobsByVendor:: Cannot create jobs and update pieces - No jobs were create or pieces processed"); + throwHttpException(CANNOT_CREATE_JOBS_AND_UPDATE_PIECES, claimingCollection, HttpStatus.HTTP_NOT_FOUND); } var successClaimingPieceResults = createSuccessClaimingResults(updatedPieceLists); log.info("createJobsByVendor:: Successfully processed pieces for claiming, count: {}", successClaimingPieceResults.size()); @@ -209,34 +213,23 @@ private List>> createUpdatePiecesAndJobFutures(ClaimingColle .forEach(configEntry -> { log.info("createUpdatePiecesAndJobFutures:: Preparing job integration detail for vendor, vendor id: {}, pieces: {}, job key: {}", vendor.getId(), pieceIds.size(), configEntry.getKey()); - updatePiecesAndJobFutures.add(updatePiecesAndCreateJob(claimingCollection, pieceIds, configEntry, requestContext)); + updatePiecesAndJobFutures.add(updatePiecesAndCreateJob(claimingCollection, pieceIds, configEntry, requestContext).map(pieceIds)); })); return updatePiecesAndJobFutures; } - private static ClaimingResults createEmptyClaimingResults(String message) { - return new ClaimingResults().withClaimingPieceResults(List.of(new ClaimingPieceResult().withError(new Error().withMessage(message)))); - } - private List createSuccessClaimingResults(List> updatedPieceLists) { return updatedPieceLists.stream().flatMap(Collection::stream).distinct() .map(pieceId -> new ClaimingPieceResult().withPieceId(pieceId).withStatus(SUCCESS)) .toList(); } - private List createErrorClaimingResults(Map> pieceIdsByVendor, String message) { - return pieceIdsByVendor.values().stream() - .flatMap(Collection::stream) - .map(pieceId -> new ClaimingPieceResult().withPieceId(pieceId).withStatus(FAILURE).withError(new Error().withMessage(message))) - .toList(); - } - - private Future> updatePiecesAndCreateJob(ClaimingCollection claimingCollection, List pieceIds, - Map.Entry configEntry, RequestContext requestContext) { + private Future updatePiecesAndCreateJob(ClaimingCollection claimingCollection, List pieceIds, + Map.Entry configEntry, RequestContext requestContext) { log.info("updatePiecesAndCreateJob:: Updating pieces and creating a job, job key: {}, count: {}", configEntry.getKey(), pieceIds.size()); - return pieceUpdateFlowManager.updatePiecesStatuses(pieceIds, PieceBatchStatusCollection.ReceivingStatus.CLAIM_SENT, - claimingCollection.getClaimingInterval(), claimingCollection.getInternalNote(), claimingCollection.getExternalNote(), requestContext) - .compose(v -> createJob(configEntry.getKey(), configEntry.getValue(), pieceIds, requestContext).map(pieceIds)); + return createJob(configEntry.getKey(), configEntry.getValue(), pieceIds, requestContext).map(pieceIds) + .compose(v -> pieceUpdateFlowManager.updatePiecesStatuses(pieceIds, PieceBatchStatusCollection.ReceivingStatus.CLAIM_SENT, + claimingCollection.getClaimingInterval(), claimingCollection.getInternalNote(), claimingCollection.getExternalNote(), requestContext)); } private Future createJob(String configKey, Object configValue, List pieceIds, RequestContext requestContext) { @@ -253,4 +246,26 @@ private Future createJob(String configKey, Object configValue, List createPieceIdParameters(ClaimingCollection claimingCollection) { + return claimingCollection.getClaimingPieceIds().stream() + .map(pieceId -> new Parameter().withKey(PIECE_ID_PARAMETER).withValue(pieceId)) + .collect(Collectors.toCollection(ArrayList::new)); + } } diff --git a/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java b/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java index 0e747c885..1e80e8b58 100644 --- a/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java +++ b/src/test/java/org/folio/rest/impl/PiecesClaimingApiTest.java @@ -186,15 +186,13 @@ void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx .filter(Objects::nonNull).filter(poLineId -> poLineId.equals(purchaseOrder.getId())) .toList(); - purchaseOrderRetrievals.forEach(entry -> logger.info("PurchaseOrders: {}", entry)); + purchaseOrderRetrievals.forEach(entry -> logger.info("Retrieved PurchaseOrder: {}", entry)); var organizationSearches = getOrganizationSearches(); var pieceUpdates = getPieceUpdates(); var jobCreations = getDataExportSpringJobCreations(); var jobExecutions = getDataExportSpringJobExecutions(); - pieceUpdates.forEach(pieceUpdate -> logger.info("Updated piece: {}", pieceUpdate.encodePrettily())); - if (response instanceof ClaimingResults claimingResults) { if (Objects.nonNull(dto)) { assertThat(pieceSearches, not(nullValue())); @@ -213,6 +211,8 @@ void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx assertThat(jobExecutions, hasSize(dto.jobExecutions)); assertThat(claimingResults.getClaimingPieceResults().size(), equalTo(dto.claimingResults)); + pieceUpdates.forEach(pieceUpdate -> logger.info("Updated Piece: {}", pieceUpdate.encodePrettily())); + var claimedPieceIds = jobCreations.stream() .peek(job -> logger.info("Created job: {}", JsonObject.mapFrom(job).encodePrettily())) .map(job -> job.getJsonObject(EXPORT_TYPE_SPECIFIC_PARAMETERS.getValue()) @@ -234,7 +234,10 @@ void testPostPiecesClaim(String name, int vendorIdx, int poLineIdx, int pieceIdx } } else if (response instanceof HttpException exception) { assertThat(exception.getErrors().getErrors().size(), is(1)); + assertThat(exception.getError().getCode(), is("unableToGenerateClaimsForOrgNoIntegrationDetails")); assertThat(exception.getError().getMessage(), is("Unable to generate claims for AMAZ because no claim integrations exist")); + assertThat(exception.getError().getParameters().get(0).getValue(), is(piece.getId())); + assertThat(exception.getError().getParameters().get(1).getValue(), is("AMAZ")); } } } diff --git a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java index a9c29ca32..421546376 100644 --- a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java +++ b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java @@ -4,8 +4,10 @@ import io.vertx.core.json.JsonObject; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; +import lombok.extern.slf4j.Slf4j; import org.folio.CopilotGenerated; import org.folio.rest.acq.model.Organization; +import org.folio.rest.core.exceptions.HttpException; import org.folio.rest.core.models.RequestContext; import org.folio.rest.jaxrs.model.ClaimingCollection; import org.folio.rest.jaxrs.model.ClaimingPieceResult; @@ -19,6 +21,7 @@ import org.folio.service.pieces.flows.update.PieceUpdateFlowManager; import org.folio.rest.core.RestClient; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -31,19 +34,20 @@ import java.util.List; import java.util.UUID; -import static org.folio.models.claiming.ClaimingError.CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS; -import static org.folio.models.claiming.ClaimingError.CANNOT_RETRIEVE_CONFIG_ENTRIES; -import static org.folio.models.claiming.ClaimingError.CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY; import static org.folio.models.claiming.IntegrationDetailField.CLAIM_PIECE_IDS; -import static org.folio.models.claiming.IntegrationDetailField.EXPORT_TYPE_SPECIFIC_PARAMETERS; import static org.folio.models.claiming.IntegrationDetailField.VENDOR_EDI_ORDERS_EXPORT_CONFIG; +import static org.folio.models.claiming.IntegrationDetailField.EXPORT_TYPE_SPECIFIC_PARAMETERS; import static org.folio.orders.utils.ResourcePathResolver.DATA_EXPORT_SPRING_CREATE_JOB; import static org.folio.orders.utils.ResourcePathResolver.resourcesPath; +import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS; +import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_RETRIEVE_CONFIG_ENTRIES; +import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY; +import static org.folio.rest.core.exceptions.ErrorCodes.UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; +@Slf4j @ExtendWith(VertxExtension.class) @CopilotGenerated(partiallyGenerated = true) public class PiecesClaimingServiceTest { @@ -74,41 +78,58 @@ void testSendClaims_emptyClaimingPieceIds(VertxTestContext testContext) { var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of()); var requestContext = mock(RequestContext.class); - piecesClaimingService.sendClaims(claimingCollection, requestContext) - .onComplete(testContext.succeeding(result -> testContext.verify(() -> { - assertEquals(1, result.getClaimingPieceResults().size()); - assertEquals(CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY.getValue(), result.getClaimingPieceResults().get(0).getError().getMessage()); - testContext.completeNow(); - }))); + var throwable = Assertions.assertThrows(HttpException.class, () -> piecesClaimingService.sendClaims(claimingCollection, requestContext)); + Assertions.assertInstanceOf(HttpException.class, throwable); + var error = throwable.getErrors().getErrors().get(0); + assertEquals(CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY.getCode(), error.getCode()); + assertEquals(CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY.getDescription(), error.getMessage()); + Assertions.assertTrue(error.getParameters().isEmpty()); + testContext.completeNow(); } @Test void testSendClaims_noConfigEntries(VertxTestContext testContext) { - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")).withClaimingInterval(1); + var pieceId1 = UUID.randomUUID().toString(); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of(pieceId1)).withClaimingInterval(1); var requestContext = mock(RequestContext.class); when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject())); piecesClaimingService.sendClaims(claimingCollection, requestContext) - .onComplete(testContext.succeeding(result -> testContext.verify(() -> { - assertEquals(1, result.getClaimingPieceResults().size()); - assertEquals(CANNOT_RETRIEVE_CONFIG_ENTRIES.getValue(), result.getClaimingPieceResults().get(0).getError().getMessage()); + .onComplete(testContext.failing(throwable -> testContext.verify(() -> { + Assertions.assertInstanceOf(HttpException.class, throwable); + var httpException = (HttpException) throwable; + var error = httpException.getErrors().getErrors().get(0); + assertEquals(CANNOT_RETRIEVE_CONFIG_ENTRIES.getCode(), error.getCode()); + assertEquals(CANNOT_RETRIEVE_CONFIG_ENTRIES.getDescription(), error.getMessage()); + Assertions.assertEquals(1, error.getParameters().size()); + var parameter = error.getParameters().get(0); + Assertions.assertEquals("pieceId", parameter.getKey()); + Assertions.assertEquals(pieceId1, parameter.getValue()); testContext.completeNow(); }))); } @Test void testSendClaims_noPiecesFound(VertxTestContext testContext) { - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")).withClaimingInterval(1); + var pieceId1 = UUID.randomUUID().toString(); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of(pieceId1)).withClaimingInterval(1); var requestContext = mock(RequestContext.class); when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("key", "value"))); when(pieceStorageService.getPiecesByIds(any(), any())).thenReturn(Future.succeededFuture(List.of())); piecesClaimingService.sendClaims(claimingCollection, requestContext) - .onComplete(testContext.succeeding(result -> testContext.verify(() -> { - assertEquals(1, result.getClaimingPieceResults().size()); - assertEquals(CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS.getValue(), result.getClaimingPieceResults().get(0).getError().getMessage()); + .onComplete(testContext.failing(throwable -> testContext.verify(() -> { + Assertions.assertInstanceOf(HttpException.class, throwable); + var httpException = (HttpException) throwable; + var error = httpException.getErrors().getErrors().get(0); + assertEquals(CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS.getCode(), error.getCode()); + assertEquals(CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS.getDescription(), error.getMessage()); + Assertions.assertEquals(1, error.getParameters().size()); + var parameter = error.getParameters().get(0); + Assertions.assertEquals("pieceId", parameter.getKey()); + Assertions.assertEquals(pieceId1, parameter.getValue()); testContext.completeNow(); }))); } @@ -128,60 +149,16 @@ void testSendClaims_pieceStatusNotLate(VertxTestContext testContext) { when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); piecesClaimingService.sendClaims(claimingCollection, requestContext) - .onComplete(testContext.succeeding(result -> testContext.verify(() -> { - assertEquals(1, result.getClaimingPieceResults().size()); - assertEquals(CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS.getValue(), result.getClaimingPieceResults().get(0).getError().getMessage()); - testContext.completeNow(); - }))); - } - - @Test - void testSendClaims_success(VertxTestContext testContext) { - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")).withClaimingInterval(1); - var requestContext = mock(RequestContext.class); - - when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("CLAIMS_vendorId", createIntegrationDetail()))); - when(pieceStorageService.getPiecesByIds(any(), any())).thenReturn(Future.succeededFuture(List.of(new Piece().withId("pieceId1").withPoLineId("poLineId1").withReceivingStatus(Piece.ReceivingStatus.LATE)))); - when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId1"))); - when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId"))); - when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withCode("VENDOR").withIsVendor(true))); - when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); - when(restClient.post(eq(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)), any(), any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("status", "CREATED"))); - when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); - - piecesClaimingService.sendClaims(claimingCollection, requestContext) - .onComplete(testContext.succeeding(result -> testContext.verify(() -> { - assertEquals(1, result.getClaimingPieceResults().size()); - assertEquals("pieceId1", result.getClaimingPieceResults().get(0).getPieceId()); - assertEquals(ClaimingPieceResult.Status.SUCCESS, result.getClaimingPieceResults().get(0).getStatus()); - testContext.completeNow(); - }))); - } - - @Test - void testSendClaims_successWithTwoPieces(VertxTestContext testContext) { - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1", "pieceId2")).withClaimingInterval(1); - var requestContext = mock(RequestContext.class); - - when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("CLAIMS_vendorId", createIntegrationDetail()))); - when(pieceStorageService.getPiecesByIds(any(), any())).thenReturn(Future.succeededFuture(List.of( - new Piece().withId("pieceId1").withPoLineId("poLineId1").withReceivingStatus(Piece.ReceivingStatus.LATE), - new Piece().withId("pieceId2").withPoLineId("poLineId2").withReceivingStatus(Piece.ReceivingStatus.LATE) - ))); - when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId1"))); - when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId"))); - when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withCode("VENDOR").withIsVendor(true))); - when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); - when(restClient.post(eq(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)), any(), any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("status", "CREATED"))); - when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); - - piecesClaimingService.sendClaims(claimingCollection, requestContext) - .onComplete(testContext.succeeding(result -> testContext.verify(() -> { - assertEquals(2, result.getClaimingPieceResults().size()); - assertEquals("pieceId1", result.getClaimingPieceResults().get(0).getPieceId()); - assertEquals(ClaimingPieceResult.Status.SUCCESS, result.getClaimingPieceResults().get(0).getStatus()); - assertEquals("pieceId2", result.getClaimingPieceResults().get(1).getPieceId()); - assertEquals(ClaimingPieceResult.Status.SUCCESS, result.getClaimingPieceResults().get(1).getStatus()); + .onComplete(testContext.failing(throwable -> testContext.verify(() -> { + Assertions.assertInstanceOf(HttpException.class, throwable); + var httpException = (HttpException) throwable; + var error = httpException.getErrors().getErrors().get(0); + assertEquals(CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS.getCode(), error.getCode()); + assertEquals(CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS.getDescription(), error.getMessage()); + Assertions.assertEquals(1, error.getParameters().size()); + var parameter = error.getParameters().get(0); + Assertions.assertEquals("pieceId", parameter.getKey()); + Assertions.assertEquals("pieceId1", parameter.getValue()); testContext.completeNow(); }))); } @@ -201,12 +178,19 @@ void testSendClaims_missingOrganizationIntegrationDetailsForTwoOrganizations(Ver when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId2").withCode("VENDOR2").withIsVendor(true))); piecesClaimingService.sendClaims(claimingCollection, requestContext) - .onComplete(testContext.succeeding(result -> testContext.verify(() -> { - assertEquals(1, result.getClaimingPieceResults().size()); - var claimingPieceResult = result.getClaimingPieceResults().get(0); - assertNull(claimingPieceResult.getPieceId()); - assertEquals(ClaimingPieceResult.Status.FAILURE, claimingPieceResult.getStatus()); - assertEquals("Unable to generate claims for VENDOR2 because no claim integrations exist", claimingPieceResult.getError().getMessage()); + .onComplete(testContext.failing(throwable -> testContext.verify(() -> { + Assertions.assertInstanceOf(HttpException.class, throwable); + var httpException = (HttpException) throwable; + var error = httpException.getErrors().getErrors().get(0); + assertEquals(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getCode(), error.getCode()); + assertEquals(String.format(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getDescription(), "VENDOR2"), error.getMessage()); + Assertions.assertEquals(2, error.getParameters().size()); + var parameter1 = error.getParameters().get(0); + Assertions.assertEquals("pieceId", parameter1.getKey()); + Assertions.assertEquals(pieceId, parameter1.getValue()); + var parameter2 = error.getParameters().get(1); + Assertions.assertEquals("vendorCode", parameter2.getKey()); + Assertions.assertEquals("VENDOR2", parameter2.getValue()); testContext.completeNow(); }))); } @@ -238,15 +222,77 @@ void testSendClaims_missingOrganizationIntegrationDetailsForThreeOrganizations(V return Future.succeededFuture(new Organization().withId(vendorId).withCode(vendorId.equals("vendorId1") ? "VENDOR1" : vendorId.equals("vendorId2") ? "VENDOR2" : "VENDOR3").withIsVendor(true)); }); + piecesClaimingService.sendClaims(claimingCollection, requestContext) + .onComplete(testContext.failing(throwable -> testContext.verify(() -> { + Assertions.assertInstanceOf(HttpException.class, throwable); + var httpException = (HttpException) throwable; + var error = httpException.getErrors().getErrors().get(0); + assertEquals(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getCode(), error.getCode()); + assertEquals(String.format(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getDescription(), "VENDOR2"), error.getMessage()); + Assertions.assertEquals(4, error.getParameters().size()); + var parameter1 = error.getParameters().get(0); + Assertions.assertEquals("pieceId", parameter1.getKey()); + Assertions.assertEquals(pieceId1, parameter1.getValue()); + var parameter2 = error.getParameters().get(1); + Assertions.assertEquals("pieceId", parameter2.getKey()); + Assertions.assertEquals(pieceId2, parameter2.getValue()); + var parameter3 = error.getParameters().get(2); + Assertions.assertEquals("pieceId", parameter3.getKey()); + Assertions.assertEquals(pieceId3, parameter3.getValue()); + var parameter4 = error.getParameters().get(3); + Assertions.assertEquals("vendorCode", parameter4.getKey()); + Assertions.assertEquals("VENDOR2", parameter4.getValue()); + testContext.completeNow(); + }))); + } + + @Test + void testSendClaims_success(VertxTestContext testContext) { + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")).withClaimingInterval(1); + var requestContext = mock(RequestContext.class); + + when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("CLAIMS_vendorId", createIntegrationDetail()))); + when(pieceStorageService.getPiecesByIds(any(), any())).thenReturn(Future.succeededFuture(List.of(new Piece().withId("pieceId1").withPoLineId("poLineId1").withReceivingStatus(Piece.ReceivingStatus.LATE)))); + when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId1"))); + when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId"))); + when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withCode("VENDOR").withIsVendor(true))); + when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); + when(restClient.post(eq(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)), any(), any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("status", "CREATED"))); + when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); + piecesClaimingService.sendClaims(claimingCollection, requestContext) .onComplete(testContext.succeeding(result -> testContext.verify(() -> { assertEquals(1, result.getClaimingPieceResults().size()); - var claimingPieceResults = result.getClaimingPieceResults(); - claimingPieceResults.forEach(claimingPieceResult -> { - assertNull(claimingPieceResult.getPieceId()); - assertEquals(ClaimingPieceResult.Status.FAILURE, claimingPieceResult.getStatus()); - assertEquals("Unable to generate claims for VENDOR2 because no claim integrations exist", claimingPieceResult.getError().getMessage()); - }); + assertEquals("pieceId1", result.getClaimingPieceResults().get(0).getPieceId()); + assertEquals(ClaimingPieceResult.Status.SUCCESS, result.getClaimingPieceResults().get(0).getStatus()); + testContext.completeNow(); + }))); + } + + @Test + void testSendClaims_successWithTwoPieces(VertxTestContext testContext) { + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1", "pieceId2")).withClaimingInterval(1); + var requestContext = mock(RequestContext.class); + + when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("CLAIMS_vendorId", createIntegrationDetail()))); + when(pieceStorageService.getPiecesByIds(any(), any())).thenReturn(Future.succeededFuture(List.of( + new Piece().withId("pieceId1").withPoLineId("poLineId1").withReceivingStatus(Piece.ReceivingStatus.LATE), + new Piece().withId("pieceId2").withPoLineId("poLineId2").withReceivingStatus(Piece.ReceivingStatus.LATE) + ))); + when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId1"))); + when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId"))); + when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withCode("VENDOR").withIsVendor(true))); + when(pieceUpdateFlowManager.updatePiecesStatuses(any(), any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture()); + when(restClient.post(eq(resourcesPath(DATA_EXPORT_SPRING_CREATE_JOB)), any(), any(), any())).thenReturn(Future.succeededFuture(new JsonObject().put("status", "CREATED"))); + when(restClient.postEmptyResponse(any(), any(), any())).thenReturn(Future.succeededFuture()); + + piecesClaimingService.sendClaims(claimingCollection, requestContext) + .onComplete(testContext.succeeding(result -> testContext.verify(() -> { + assertEquals(2, result.getClaimingPieceResults().size()); + assertEquals("pieceId1", result.getClaimingPieceResults().get(0).getPieceId()); + assertEquals(ClaimingPieceResult.Status.SUCCESS, result.getClaimingPieceResults().get(0).getStatus()); + assertEquals("pieceId2", result.getClaimingPieceResults().get(1).getPieceId()); + assertEquals(ClaimingPieceResult.Status.SUCCESS, result.getClaimingPieceResults().get(1).getStatus()); testContext.completeNow(); }))); } From a45584d3cc71cd9f879f3af68c4558540109a488 Mon Sep 17 00:00:00 2001 From: BKadirkhodjaev Date: Fri, 20 Dec 2024 14:02:26 +0500 Subject: [PATCH 7/8] [MODORDERS-1209-2]. Add tenant validation to integration details --- .../claiming/IntegrationDetailField.java | 1 + .../service/pieces/PiecesClaimingService.java | 48 +++++++++++-------- .../pieces/PiecesClaimingServiceTest.java | 38 ++++++++++++++- .../test_diku_limit_10_claims.json | 2 +- 4 files changed, 68 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/folio/models/claiming/IntegrationDetailField.java b/src/main/java/org/folio/models/claiming/IntegrationDetailField.java index bf75daa7d..f7c5110e6 100644 --- a/src/main/java/org/folio/models/claiming/IntegrationDetailField.java +++ b/src/main/java/org/folio/models/claiming/IntegrationDetailField.java @@ -6,6 +6,7 @@ @Getter @AllArgsConstructor public enum IntegrationDetailField { + TENANT("tenant"), EXPORT_TYPE_SPECIFIC_PARAMETERS("exportTypeSpecificParameters"), VENDOR_EDI_ORDERS_EXPORT_CONFIG("vendorEdiOrdersExportConfig"), CLAIM_PIECE_IDS("claimPieceIds"); diff --git a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java index 5175f5a0f..63203401a 100644 --- a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java +++ b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java @@ -20,6 +20,7 @@ import org.folio.rest.jaxrs.model.Parameter; import org.folio.rest.jaxrs.model.Piece; import org.folio.rest.jaxrs.model.PieceBatchStatusCollection; +import org.folio.rest.tools.utils.TenantTool; import org.folio.service.caches.ConfigurationEntriesCache; import org.folio.service.orders.PurchaseOrderLineService; import org.folio.service.orders.PurchaseOrderStorageService; @@ -40,6 +41,7 @@ import static java.util.stream.Collectors.toList; import static org.folio.models.claiming.IntegrationDetailField.CLAIM_PIECE_IDS; import static org.folio.models.claiming.IntegrationDetailField.EXPORT_TYPE_SPECIFIC_PARAMETERS; +import static org.folio.models.claiming.IntegrationDetailField.TENANT; import static org.folio.models.claiming.IntegrationDetailField.VENDOR_EDI_ORDERS_EXPORT_CONFIG; import static org.folio.orders.utils.HelperUtils.DATA_EXPORT_SPRING_CONFIG_MODULE_NAME; import static org.folio.orders.utils.HelperUtils.collectResultsOnSuccess; @@ -102,7 +104,7 @@ public Future sendClaims(ClaimingCollection claimingCollection, } pieceIdsByVendors.forEach((vendor, piecesByVendor) -> log.info("createVendorPiecePair:: Using pieces by vendor map, vendorId: {}, piecesByVendor: {}", vendor.getId(), piecesByVendor)); - var vendorWithoutIntegrationDetails = checkVendorIntegrationDetails(config, pieceIdsByVendors); + var vendorWithoutIntegrationDetails = findFirstMissingVendorWithoutIntegrationDetail(config, pieceIdsByVendors, requestContext); if (Objects.nonNull(vendorWithoutIntegrationDetails)) { log.info("sendClaims:: Unable to generate claims because no claim integrations exist - No claims are sent"); throwHttpExceptionOnMissingVendorIntegrationDetails(claimingCollection, vendorWithoutIntegrationDetails); @@ -163,11 +165,13 @@ private Future> createVendorPiecePair(Pair> pieceIdsByVendors) { + private Organization findFirstMissingVendorWithoutIntegrationDetail(JsonObject config, Map> pieceIdsByVendors, + RequestContext requestContext) { return pieceIdsByVendors.keySet().stream() .filter(vendor -> { var vendorIntegrationDetails = config.stream() .filter(configEntry -> isExportTypeClaimsAndCorrectVendorId(vendor.getId(), configEntry)) + .filter(configEntry -> isCorrectTenant(TenantTool.tenantId(requestContext.getHeaders()), configEntry)) .toList(); log.info("checkVendorIntegrationDetails:: Found vendor integration details, vendorId: {}, integrationDetails: {}", vendor.getId(), vendorIntegrationDetails); return vendorIntegrationDetails.isEmpty(); @@ -175,15 +179,6 @@ private Organization checkVendorIntegrationDetails(JsonObject config, Map configEntry) { - return configEntry.getKey().startsWith(String.format("%s_%s", EXPORT_TYPE_CLAIMS, vendorId)) && Objects.nonNull(configEntry.getValue()); - } - - private static Map> transformAndGroupPieceIdsByVendor(List> piecesByVendorList) { - return StreamEx.of(piecesByVendorList).distinct().filter(Objects::nonNull) - .groupingBy(Pair::getKey, mapping(Pair::getValue, toList())); - } - private Future createJobsByVendor(ClaimingCollection claimingCollection, JsonObject config, Map> pieceIdsByVendor, RequestContext requestContext) { log.info("createJobsByVendor:: Creating jobs by vendor, vendors by pieces count: {}", pieceIdsByVendor.size()); @@ -209,7 +204,8 @@ private List>> createUpdatePiecesAndJobFutures(ClaimingColle Map> pieceIdsByVendor, RequestContext requestContext) { var updatePiecesAndJobFutures = new ArrayList>>(); pieceIdsByVendor.forEach((vendor, pieceIds) -> config.stream() - .filter(pieceIdsByVendorIdEntry -> isExportTypeClaimsAndCorrectVendorId(vendor.getId(), pieceIdsByVendorIdEntry)) + .filter(configEntry -> isExportTypeClaimsAndCorrectVendorId(vendor.getId(), configEntry)) + .filter(configEntry -> isCorrectTenant(TenantTool.tenantId(requestContext.getHeaders()), configEntry)) .forEach(configEntry -> { log.info("createUpdatePiecesAndJobFutures:: Preparing job integration detail for vendor, vendor id: {}, pieces: {}, job key: {}", vendor.getId(), pieceIds.size(), configEntry.getKey()); @@ -218,12 +214,6 @@ private List>> createUpdatePiecesAndJobFutures(ClaimingColle return updatePiecesAndJobFutures; } - private List createSuccessClaimingResults(List> updatedPieceLists) { - return updatedPieceLists.stream().flatMap(Collection::stream).distinct() - .map(pieceId -> new ClaimingPieceResult().withPieceId(pieceId).withStatus(SUCCESS)) - .toList(); - } - private Future updatePiecesAndCreateJob(ClaimingCollection claimingCollection, List pieceIds, Map.Entry configEntry, RequestContext requestContext) { log.info("updatePiecesAndCreateJob:: Updating pieces and creating a job, job key: {}, count: {}", configEntry.getKey(), pieceIds.size()); @@ -247,6 +237,26 @@ private Future createJob(String configKey, Object configValue, List configEntry) { + return configEntry.getKey().startsWith(String.format("%s_%s", EXPORT_TYPE_CLAIMS, vendorId)) && Objects.nonNull(configEntry.getValue()); + } + + private boolean isCorrectTenant(String tenantId, Map.Entry configEntry) { + var integrationDetail = new JsonObject(String.valueOf(configEntry.getValue())); + return integrationDetail.getString(TENANT.getValue()).equals(tenantId); + } + + private static Map> transformAndGroupPieceIdsByVendor(List> piecesByVendorList) { + return StreamEx.of(piecesByVendorList).distinct().filter(Objects::nonNull) + .groupingBy(Pair::getKey, mapping(Pair::getValue, toList())); + } + + private List createSuccessClaimingResults(List> updatedPieceLists) { + return updatedPieceLists.stream().flatMap(Collection::stream).distinct() + .map(pieceId -> new ClaimingPieceResult().withPieceId(pieceId).withStatus(SUCCESS)) + .toList(); + } + private void throwHttpExceptionOnMissingVendorIntegrationDetails(ClaimingCollection claimingCollection, Organization vendorWithoutIntegrationDetails) { var parameters = createPieceIdParameters(claimingCollection); parameters.add(new Parameter().withKey(VENDOR_CODE_PARAMETER).withValue(vendorWithoutIntegrationDetails.getCode())); @@ -263,7 +273,7 @@ private void throwHttpException(ErrorCodes errorCode, ClaimingCollection claimin throw new HttpException(httpStatus.toInt(), new Errors().withErrors(errors).withTotalRecords(errors.size())); } - private ArrayList createPieceIdParameters(ClaimingCollection claimingCollection) { + private List createPieceIdParameters(ClaimingCollection claimingCollection) { return claimingCollection.getClaimingPieceIds().stream() .map(pieceId -> new Parameter().withKey(PIECE_ID_PARAMETER).withValue(pieceId)) .collect(Collectors.toCollection(ArrayList::new)); diff --git a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java index 421546376..afdb76c1a 100644 --- a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java +++ b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java @@ -1,6 +1,7 @@ package org.folio.service.pieces; import io.vertx.core.Future; +import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; @@ -32,13 +33,16 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.UUID; import static org.folio.models.claiming.IntegrationDetailField.CLAIM_PIECE_IDS; +import static org.folio.models.claiming.IntegrationDetailField.TENANT; import static org.folio.models.claiming.IntegrationDetailField.VENDOR_EDI_ORDERS_EXPORT_CONFIG; import static org.folio.models.claiming.IntegrationDetailField.EXPORT_TYPE_SPECIFIC_PARAMETERS; import static org.folio.orders.utils.ResourcePathResolver.DATA_EXPORT_SPRING_CREATE_JOB; import static org.folio.orders.utils.ResourcePathResolver.resourcesPath; +import static org.folio.rest.RestVerticle.OKAPI_HEADER_TENANT; import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS; import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_RETRIEVE_CONFIG_ENTRIES; import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY; @@ -246,6 +250,38 @@ void testSendClaims_missingOrganizationIntegrationDetailsForThreeOrganizations(V }))); } + @Test + void testSendClaims_incorrectRequestTenant(VertxTestContext testContext) { + var pieceId = UUID.randomUUID().toString(); + var piece = new Piece().withId(pieceId).withPoLineId("poLineId").withReceivingStatus(Piece.ReceivingStatus.LATE); + var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of(pieceId)); + var requestContext = new RequestContext(Vertx.vertx().getOrCreateContext(), Map.of(OKAPI_HEADER_TENANT, "folio")); + + when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject() + .put("CLAIMS_vendorId", createIntegrationDetail()))); + when(pieceStorageService.getPiecesByIds(any(), any())).thenReturn(Future.succeededFuture(List.of(piece))); + when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId"))); + when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId"))); + when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withCode("VENDOR").withIsVendor(true))); + + piecesClaimingService.sendClaims(claimingCollection, requestContext) + .onComplete(testContext.failing(throwable -> testContext.verify(() -> { + Assertions.assertInstanceOf(HttpException.class, throwable); + var httpException = (HttpException) throwable; + var error = httpException.getErrors().getErrors().get(0); + assertEquals(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getCode(), error.getCode()); + assertEquals(String.format(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getDescription(), "VENDOR"), error.getMessage()); + Assertions.assertEquals(2, error.getParameters().size()); + var parameter1 = error.getParameters().get(0); + Assertions.assertEquals("pieceId", parameter1.getKey()); + Assertions.assertEquals(pieceId, parameter1.getValue()); + var parameter2 = error.getParameters().get(1); + Assertions.assertEquals("vendorCode", parameter2.getKey()); + Assertions.assertEquals("VENDOR", parameter2.getValue()); + testContext.completeNow(); + }))); + } + @Test void testSendClaims_success(VertxTestContext testContext) { var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")).withClaimingInterval(1); @@ -411,6 +447,6 @@ void testSendClaims_successWithMultipleOrganizationsAndOneIntegrationDetail(Vert private JsonObject createIntegrationDetail() { var vendorEditOrdersExportConfig = new JsonObject().put(CLAIM_PIECE_IDS.getValue(), List.of()); var exportTypeSpecificParameters = new JsonObject().put(VENDOR_EDI_ORDERS_EXPORT_CONFIG.getValue(), vendorEditOrdersExportConfig); - return new JsonObject().put(EXPORT_TYPE_SPECIFIC_PARAMETERS.getValue(), exportTypeSpecificParameters); + return new JsonObject().put(EXPORT_TYPE_SPECIFIC_PARAMETERS.getValue(), exportTypeSpecificParameters).put(TENANT.getValue(), "folio_shared"); } } diff --git a/src/test/resources/mockdata/configurations.entries/test_diku_limit_10_claims.json b/src/test/resources/mockdata/configurations.entries/test_diku_limit_10_claims.json index 833dd24b7..b06c817d4 100644 --- a/src/test/resources/mockdata/configurations.entries/test_diku_limit_10_claims.json +++ b/src/test/resources/mockdata/configurations.entries/test_diku_limit_10_claims.json @@ -77,7 +77,7 @@ }, { "id": "245bc212-4e56-4875-8c84-4401018d4242", - "value": "{\"id\":\"245bc212-4e56-4875-8c84-4401018d4242\",\"type\":\"CLAIMS\",\"tenant\":\"diku\",\"exportTypeSpecificParameters\":{\"vendorEdiOrdersExportConfig\":{\"exportConfigId\":\"245bc212-4e56-4875-8c84-4401018d4242\",\"vendorId\":\"e0fb5df2-cdf1-11e8-a8d5-f2801f1b9fd1\",\"integrationType\":\"Claiming\",\"transmissionMethod\":\"File download\",\"fileFormat\":\"CSV\",\"configName\":\"Test 1\",\"ediConfig\":{\"accountNoList\":[\"1\"],\"ediNamingConvention\":\"{organizationCode}-{integrationName}-{exportJobEndDate}\",\"libEdiCode\":\"Test 1\",\"libEdiType\":\"31B/US-SAN\",\"vendorEdiCode\":\"Test 1\",\"vendorEdiType\":\"31B/US-SAN\",\"sendAccountNumber\":false,\"supportOrder\":false,\"supportInvoice\":false},\"ediFtp\":{\"ftpConnMode\":\"Active\",\"ftpFormat\":\"SFTP\",\"ftpMode\":\"ASCII\",\"ftpPort\":1,\"serverAddress\":\"ftp://test1.org\"},\"isDefaultConfig\":false}},\"schedulePeriod\":\"NONE\"}", + "value": "{\"id\":\"245bc212-4e56-4875-8c84-4401018d4242\",\"type\":\"CLAIMS\",\"tenant\":\"test_diku_limit_10_claims\",\"exportTypeSpecificParameters\":{\"vendorEdiOrdersExportConfig\":{\"exportConfigId\":\"245bc212-4e56-4875-8c84-4401018d4242\",\"vendorId\":\"e0fb5df2-cdf1-11e8-a8d5-f2801f1b9fd1\",\"integrationType\":\"Claiming\",\"transmissionMethod\":\"File download\",\"fileFormat\":\"CSV\",\"configName\":\"Test 1\",\"ediConfig\":{\"accountNoList\":[\"1\"],\"ediNamingConvention\":\"{organizationCode}-{integrationName}-{exportJobEndDate}\",\"libEdiCode\":\"Test 1\",\"libEdiType\":\"31B/US-SAN\",\"vendorEdiCode\":\"Test 1\",\"vendorEdiType\":\"31B/US-SAN\",\"sendAccountNumber\":false,\"supportOrder\":false,\"supportInvoice\":false},\"ediFtp\":{\"ftpConnMode\":\"Active\",\"ftpFormat\":\"SFTP\",\"ftpMode\":\"ASCII\",\"ftpPort\":1,\"serverAddress\":\"ftp://test1.org\"},\"isDefaultConfig\":false}},\"schedulePeriod\":\"NONE\"}", "module": "mod-data-export-spring", "default": true, "enabled": true, From e971d1521b9edb3ce1ef562e5cbcb4daf3ee9058 Mon Sep 17 00:00:00 2001 From: BKadirkhodjaev Date: Fri, 20 Dec 2024 15:32:48 +0500 Subject: [PATCH 8/8] [MODORDERS-1209-2]. Remove unnecessary tenant validation --- .../service/pieces/PiecesClaimingService.java | 14 ++------ .../pieces/PiecesClaimingServiceTest.java | 35 ------------------- 2 files changed, 2 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java index 63203401a..7ccea6ef0 100644 --- a/src/main/java/org/folio/service/pieces/PiecesClaimingService.java +++ b/src/main/java/org/folio/service/pieces/PiecesClaimingService.java @@ -20,7 +20,6 @@ import org.folio.rest.jaxrs.model.Parameter; import org.folio.rest.jaxrs.model.Piece; import org.folio.rest.jaxrs.model.PieceBatchStatusCollection; -import org.folio.rest.tools.utils.TenantTool; import org.folio.service.caches.ConfigurationEntriesCache; import org.folio.service.orders.PurchaseOrderLineService; import org.folio.service.orders.PurchaseOrderStorageService; @@ -41,7 +40,6 @@ import static java.util.stream.Collectors.toList; import static org.folio.models.claiming.IntegrationDetailField.CLAIM_PIECE_IDS; import static org.folio.models.claiming.IntegrationDetailField.EXPORT_TYPE_SPECIFIC_PARAMETERS; -import static org.folio.models.claiming.IntegrationDetailField.TENANT; import static org.folio.models.claiming.IntegrationDetailField.VENDOR_EDI_ORDERS_EXPORT_CONFIG; import static org.folio.orders.utils.HelperUtils.DATA_EXPORT_SPRING_CONFIG_MODULE_NAME; import static org.folio.orders.utils.HelperUtils.collectResultsOnSuccess; @@ -104,7 +102,7 @@ public Future sendClaims(ClaimingCollection claimingCollection, } pieceIdsByVendors.forEach((vendor, piecesByVendor) -> log.info("createVendorPiecePair:: Using pieces by vendor map, vendorId: {}, piecesByVendor: {}", vendor.getId(), piecesByVendor)); - var vendorWithoutIntegrationDetails = findFirstMissingVendorWithoutIntegrationDetail(config, pieceIdsByVendors, requestContext); + var vendorWithoutIntegrationDetails = findFirstMissingVendorWithoutIntegrationDetail(config, pieceIdsByVendors); if (Objects.nonNull(vendorWithoutIntegrationDetails)) { log.info("sendClaims:: Unable to generate claims because no claim integrations exist - No claims are sent"); throwHttpExceptionOnMissingVendorIntegrationDetails(claimingCollection, vendorWithoutIntegrationDetails); @@ -165,13 +163,11 @@ private Future> createVendorPiecePair(Pair> pieceIdsByVendors, - RequestContext requestContext) { + private Organization findFirstMissingVendorWithoutIntegrationDetail(JsonObject config, Map> pieceIdsByVendors) { return pieceIdsByVendors.keySet().stream() .filter(vendor -> { var vendorIntegrationDetails = config.stream() .filter(configEntry -> isExportTypeClaimsAndCorrectVendorId(vendor.getId(), configEntry)) - .filter(configEntry -> isCorrectTenant(TenantTool.tenantId(requestContext.getHeaders()), configEntry)) .toList(); log.info("checkVendorIntegrationDetails:: Found vendor integration details, vendorId: {}, integrationDetails: {}", vendor.getId(), vendorIntegrationDetails); return vendorIntegrationDetails.isEmpty(); @@ -205,7 +201,6 @@ private List>> createUpdatePiecesAndJobFutures(ClaimingColle var updatePiecesAndJobFutures = new ArrayList>>(); pieceIdsByVendor.forEach((vendor, pieceIds) -> config.stream() .filter(configEntry -> isExportTypeClaimsAndCorrectVendorId(vendor.getId(), configEntry)) - .filter(configEntry -> isCorrectTenant(TenantTool.tenantId(requestContext.getHeaders()), configEntry)) .forEach(configEntry -> { log.info("createUpdatePiecesAndJobFutures:: Preparing job integration detail for vendor, vendor id: {}, pieces: {}, job key: {}", vendor.getId(), pieceIds.size(), configEntry.getKey()); @@ -241,11 +236,6 @@ private boolean isExportTypeClaimsAndCorrectVendorId(String vendorId, Map.Entry< return configEntry.getKey().startsWith(String.format("%s_%s", EXPORT_TYPE_CLAIMS, vendorId)) && Objects.nonNull(configEntry.getValue()); } - private boolean isCorrectTenant(String tenantId, Map.Entry configEntry) { - var integrationDetail = new JsonObject(String.valueOf(configEntry.getValue())); - return integrationDetail.getString(TENANT.getValue()).equals(tenantId); - } - private static Map> transformAndGroupPieceIdsByVendor(List> piecesByVendorList) { return StreamEx.of(piecesByVendorList).distinct().filter(Objects::nonNull) .groupingBy(Pair::getKey, mapping(Pair::getValue, toList())); diff --git a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java index afdb76c1a..2ccb90b4f 100644 --- a/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java +++ b/src/test/java/org/folio/service/pieces/PiecesClaimingServiceTest.java @@ -1,7 +1,6 @@ package org.folio.service.pieces; import io.vertx.core.Future; -import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; @@ -33,7 +32,6 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.UUID; import static org.folio.models.claiming.IntegrationDetailField.CLAIM_PIECE_IDS; @@ -42,7 +40,6 @@ import static org.folio.models.claiming.IntegrationDetailField.EXPORT_TYPE_SPECIFIC_PARAMETERS; import static org.folio.orders.utils.ResourcePathResolver.DATA_EXPORT_SPRING_CREATE_JOB; import static org.folio.orders.utils.ResourcePathResolver.resourcesPath; -import static org.folio.rest.RestVerticle.OKAPI_HEADER_TENANT; import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_FIND_PIECES_WITH_LATE_STATUS_TO_PROCESS; import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_RETRIEVE_CONFIG_ENTRIES; import static org.folio.rest.core.exceptions.ErrorCodes.CANNOT_SEND_CLAIMS_PIECE_IDS_ARE_EMPTY; @@ -250,38 +247,6 @@ void testSendClaims_missingOrganizationIntegrationDetailsForThreeOrganizations(V }))); } - @Test - void testSendClaims_incorrectRequestTenant(VertxTestContext testContext) { - var pieceId = UUID.randomUUID().toString(); - var piece = new Piece().withId(pieceId).withPoLineId("poLineId").withReceivingStatus(Piece.ReceivingStatus.LATE); - var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of(pieceId)); - var requestContext = new RequestContext(Vertx.vertx().getOrCreateContext(), Map.of(OKAPI_HEADER_TENANT, "folio")); - - when(configurationEntriesCache.loadConfiguration(any(), any())).thenReturn(Future.succeededFuture(new JsonObject() - .put("CLAIMS_vendorId", createIntegrationDetail()))); - when(pieceStorageService.getPiecesByIds(any(), any())).thenReturn(Future.succeededFuture(List.of(piece))); - when(purchaseOrderLineService.getOrderLineById(any(), any())).thenReturn(Future.succeededFuture(new PoLine().withPurchaseOrderId("orderId"))); - when(purchaseOrderStorageService.getPurchaseOrderById(any(), any())).thenReturn(Future.succeededFuture(new PurchaseOrder().withVendor("vendorId"))); - when(organizationService.getVendorById(any(), any())).thenReturn(Future.succeededFuture(new Organization().withId("vendorId").withCode("VENDOR").withIsVendor(true))); - - piecesClaimingService.sendClaims(claimingCollection, requestContext) - .onComplete(testContext.failing(throwable -> testContext.verify(() -> { - Assertions.assertInstanceOf(HttpException.class, throwable); - var httpException = (HttpException) throwable; - var error = httpException.getErrors().getErrors().get(0); - assertEquals(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getCode(), error.getCode()); - assertEquals(String.format(UNABLE_TO_GENERATE_CLAIMS_FOR_ORG_NO_INTEGRATION_DETAILS.getDescription(), "VENDOR"), error.getMessage()); - Assertions.assertEquals(2, error.getParameters().size()); - var parameter1 = error.getParameters().get(0); - Assertions.assertEquals("pieceId", parameter1.getKey()); - Assertions.assertEquals(pieceId, parameter1.getValue()); - var parameter2 = error.getParameters().get(1); - Assertions.assertEquals("vendorCode", parameter2.getKey()); - Assertions.assertEquals("VENDOR", parameter2.getValue()); - testContext.completeNow(); - }))); - } - @Test void testSendClaims_success(VertxTestContext testContext) { var claimingCollection = new ClaimingCollection().withClaimingPieceIds(List.of("pieceId1")).withClaimingInterval(1);