From 398823051cd1a54082ec2b76968caf730e83f646 Mon Sep 17 00:00:00 2001 From: Saba-Zedginidze-EPAM <148070844+Saba-Zedginidze-EPAM@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:06:39 +0400 Subject: [PATCH] [MODORDERS-1122] Add logic to create item in any tenant for binding (#953) * [MODORDERS-1122] Add bindPiecesResult as a return value for bind endpoint * [MODORDERS-1122] Fix spelling * [MODORDERS-1122] Fix failing test * [MODORDERS-1122] Fix failing test * [MODORDERS-1122] Accept null requestsAction * [MODORDERS-1122] Add BindPiecesHolder * [MODORDERS-1122] Assert itemId not null * [MODORDERS-1122] Add ProcessingStatus to BindPiecesResult * Revert "[MODORDERS-1122] Add ProcessingStatus to BindPiecesResult" This reverts commit 1dcda97bdfea7f7c9173734bc86c976232fa2cc5. * [MODORDERS-1122] Update acq modules * [MODORDERS-1122] Fix failing test * [MODORDERS-1122] Update acq modules * [MODORDERS-1122] Fix failing test * [MODORDERS-1122] Fix failing test --- ramls/acq-models | 2 +- ramls/bind-pieces.raml | 10 +- .../java/org/folio/helper/BindHelper.java | 129 ++++++++---------- .../folio/models/pieces/BindPiecesHolder.java | 51 +++++++ .../rest/impl/CheckinReceivingApiTest.java | 18 ++- 5 files changed, 128 insertions(+), 82 deletions(-) create mode 100644 src/main/java/org/folio/models/pieces/BindPiecesHolder.java diff --git a/ramls/acq-models b/ramls/acq-models index da1f78cd6..25b76842f 160000 --- a/ramls/acq-models +++ b/ramls/acq-models @@ -1 +1 @@ -Subproject commit da1f78cd6503659c37d39011b84509d3341b19ea +Subproject commit 25b76842f353ba1d943e52619b1484b3d6f35d18 diff --git a/ramls/bind-pieces.raml b/ramls/bind-pieces.raml index 5f4fa34a7..a3fcded0a 100644 --- a/ramls/bind-pieces.raml +++ b/ramls/bind-pieces.raml @@ -10,8 +10,8 @@ documentation: types: bind-pieces-collection: !include acq-models/mod-orders/schemas/bindPiecesCollection.json - receiving-results: !include acq-models/mod-orders/schemas/receivingResults.json - + bind-pieces-result: !include acq-models/mod-orders/schemas/bindPiecesResult.json + requestsAction: !include acq-models/mod-orders/schemas/requestsAction.json errors: !include raml-util/schemas/errors.schema UUID: type: string @@ -26,13 +26,13 @@ resourceTypes: /orders/bind-pieces: displayName: Bind pieces description: | - This endpoint used to bind pieces to one item and connect that item to title. + This endpoint is used to bind pieces to one item and connect that item to a title. type: post-with-200: requestSchema: bind-pieces-collection - responseSchema: receiving-results + responseSchema: bind-pieces-result requestExample: !include acq-models/mod-orders/examples/bindPiecesCollection.sample - responseExample: !include acq-models/mod-orders/examples/receivingResults.sample + responseExample: !include acq-models/mod-orders/examples/bindPiecesResult.sample is: [validate] post: description: bind pieces to item and connect that item to title diff --git a/src/main/java/org/folio/helper/BindHelper.java b/src/main/java/org/folio/helper/BindHelper.java index e75ff0a0e..18af7ba15 100644 --- a/src/main/java/org/folio/helper/BindHelper.java +++ b/src/main/java/org/folio/helper/BindHelper.java @@ -5,6 +5,7 @@ import io.vertx.core.json.JsonObject; import org.apache.commons.lang3.StringUtils; import org.folio.models.ItemFields; +import org.folio.models.pieces.BindPiecesHolder; import org.folio.okapi.common.GenericCompositeFuture; import org.folio.orders.utils.PoLineCommonUtil; import org.folio.rest.RestConstants; @@ -13,14 +14,12 @@ import org.folio.rest.core.models.RequestContext; import org.folio.rest.jaxrs.model.BindItem; import org.folio.rest.jaxrs.model.BindPiecesCollection; +import org.folio.rest.jaxrs.model.BindPiecesResult; import org.folio.rest.jaxrs.model.CompositePoLine; import org.folio.rest.jaxrs.model.Error; import org.folio.rest.jaxrs.model.Parameter; import org.folio.rest.jaxrs.model.Piece; -import org.folio.rest.jaxrs.model.ProcessingStatus; import org.folio.rest.jaxrs.model.ReceivedItem; -import org.folio.rest.jaxrs.model.ReceivingResult; -import org.folio.rest.jaxrs.model.ReceivingResults; import org.folio.rest.jaxrs.model.Title; import org.folio.rest.tools.utils.TenantTool; import org.folio.service.inventory.InventoryInstanceManager; @@ -69,47 +68,52 @@ private Map> groupBindPieceByPoLineId( return Map.of(poLineId, bindPieceMap); } - public Future bindPieces(BindPiecesCollection bindPiecesCollection, RequestContext requestContext) { + public Future bindPieces(BindPiecesCollection bindPiecesCollection, RequestContext requestContext) { return removeForbiddenEntities(requestContext) .compose(vVoid -> processBindPieces(bindPiecesCollection, requestContext)); } - private Future processBindPieces(BindPiecesCollection bindPiecesCollection, RequestContext requestContext) { + private Future processBindPieces(BindPiecesCollection bindPiecesCollection, RequestContext requestContext) { // 1. Get piece records from storage return retrievePieceRecords(requestContext) - // 2. Check if there are any open requests for items - .compose(piecesGroupedByPoLine -> checkRequestsForPieceItems(piecesGroupedByPoLine, bindPiecesCollection, requestContext)) - // 3. Update piece isBound flag + // 2. Generate holder object to include necessary data + .map(piecesGroupedByPoLine -> generateHolder(piecesGroupedByPoLine, bindPiecesCollection)) + // 3. Check if there are any open requests for items + .compose(bindPiecesHolder -> checkRequestsForPieceItems(bindPiecesHolder, requestContext)) + // 4. Update piece isBound flag .map(this::updatePieceRecords) - // 4. Update currently associated items - .compose(piecesGroupedByPoLine -> updateItemStatus(piecesGroupedByPoLine, requestContext)) - // 5. Crate item for pieces with specific fields - .compose(piecesGroupedByPoLine -> createItemForPiece(piecesGroupedByPoLine, bindPiecesCollection, requestContext)) - // 6. Update received piece records in the storage - .compose(piecesGroupedByPoLine -> storeUpdatedPieceRecords(piecesGroupedByPoLine, requestContext)) - // 7. Update Title with new bind items - .map(piecesGroupedByPoLine -> updateTitleWithBindItems(piecesGroupedByPoLine, requestContext)) - // 8. Return results to the client - .map(piecesGroupedByPoLine -> prepareResponseBody(piecesGroupedByPoLine, bindPiecesCollection)); + // 5. Update currently associated items + .compose(bindPiecesHolder -> updateItemStatus(bindPiecesHolder, requestContext)) + // 6. Crate item for pieces with specific fields + .compose(bindPiecesHolder -> createItemForPieces(bindPiecesHolder, requestContext)) + // 7. Update received piece records in the storage + .compose(bindPiecesHolder -> storeUpdatedPieces(bindPiecesHolder, requestContext)) + // 8. Update Title with new bind items + .compose(bindPiecesHolder -> updateTitleWithBindItems(bindPiecesHolder, requestContext)) + // 9. Return results to the client + .map(this::prepareResponseBody); } - private Future>> checkRequestsForPieceItems(Map> piecesGroupedByPoLine, - BindPiecesCollection bindPiecesCollection, - RequestContext requestContext) { - var tenantToItem = mapTenantIdsToItemIds(piecesGroupedByPoLine, requestContext); + private BindPiecesHolder generateHolder(Map> piecesGroupedByPoLine, BindPiecesCollection bindPiecesCollection) { + return new BindPiecesHolder() + .withBindPiecesCollection(bindPiecesCollection) + .withPiecesGroupedByPoLine(piecesGroupedByPoLine); + } + + private Future checkRequestsForPieceItems(BindPiecesHolder holder, RequestContext requestContext) { + var tenantToItem = mapTenantIdsToItemIds(holder.getPiecesGroupedByPoLine(), requestContext); return GenericCompositeFuture.all( tenantToItem.entrySet().stream() .map(entry -> { var locationContext = createContextWithNewTenantId(requestContext, entry.getKey()); return inventoryItemRequestService.getItemsWithActiveRequests(entry.getValue(), locationContext) - .compose(items -> validateItemsForRequestTransfer(tenantToItem.keySet(), items, bindPiecesCollection)); + .compose(items -> validateItemsForRequestTransfer(tenantToItem.keySet(), items, holder.getBindPiecesCollection())); }) .toList()) - .map(f -> piecesGroupedByPoLine); + .map(f -> holder); } - private Future validateItemsForRequestTransfer(Set tenants, - List items, + private Future validateItemsForRequestTransfer(Set tenants, List items, BindPiecesCollection bindPiecesCollection) { if (items.isEmpty()) { return Future.succeededFuture(); @@ -133,17 +137,16 @@ private Future validateItemsForRequestTransfer(Set tenants, return Future.succeededFuture(); } - private Map> updatePieceRecords(Map> piecesGroupedByPoLine) { + private BindPiecesHolder updatePieceRecords(BindPiecesHolder holder) { logger.debug("updatePieceRecords:: Updating the piece records to set isBound flag as TRUE"); - extractAllPieces(piecesGroupedByPoLine) - .forEach(piece -> piece.setIsBound(true)); - return piecesGroupedByPoLine; + holder.getPieces().forEach(piece -> piece.setIsBound(true)); + return holder; } - private Future>> updateItemStatus(Map> piecesGroupedByPoLine, RequestContext requestContext) { + private Future updateItemStatus(BindPiecesHolder holder, RequestContext requestContext) { logger.debug("updateItemStatus:: Updating previous item status to 'Unavailable'"); return GenericCompositeFuture.all( - mapTenantIdsToItemIds(piecesGroupedByPoLine, requestContext).entrySet().stream() + mapTenantIdsToItemIds(holder.getPiecesGroupedByPoLine(), requestContext).entrySet().stream() .map(entry -> { var locationContext = createContextWithNewTenantId(requestContext, entry.getKey()); return inventoryItemManager.getItemRecordsByIds(entry.getValue(), locationContext) @@ -158,15 +161,13 @@ private Future>> updateItemStatus(Map piecesGroupedByPoLine); + ).map(f -> holder); } - private Future>> createItemForPiece(Map> piecesGroupedByPoLine, - BindPiecesCollection bindPiecesCollection, - RequestContext requestContext) { - var poLineId = bindPiecesCollection.getPoLineId(); - var holdingIds = piecesGroupedByPoLine.values() - .stream().flatMap(List::stream) + private Future createItemForPieces(BindPiecesHolder holder, RequestContext requestContext) { + var bindPiecesCollection = holder.getBindPiecesCollection(); + var poLineId = holder.getPoLineId(); + var holdingIds = holder.getPieces() .map(Piece::getHoldingId).distinct().toList(); validateHoldingIds(holdingIds, bindPiecesCollection); logger.debug("createItemForPiece:: Trying to get poLine by id '{}'", poLineId); @@ -177,12 +178,12 @@ private Future>> createItemForPiece(Map { // Move requests if requestsAction is TRANSFER, otherwise do nothing if (TRANSFER.equals(bindPiecesCollection.getRequestsAction())) { - var itemIds = extractAllPieces(piecesGroupedByPoLine).map(Piece::getItemId).toList(); + var itemIds = holder.getPieces().map(Piece::getItemId).toList(); inventoryItemRequestService.transferItemsRequests(itemIds, newItemId, requestContext); } - // Set new item ids for pieces - piecesGroupedByPoLine.get(poLineId).forEach(piece -> piece.setItemId(newItemId)); - return piecesGroupedByPoLine; + // Set new item ids for pieces and holder + holder.getPieces().forEach(piece -> piece.setItemId(newItemId)); + return holder.withBindItemId(newItemId); }); } @@ -214,14 +215,16 @@ private Future createShadowInstanceAndHoldingIfNeeded(CompositePoLine co .compose(s -> inventoryHoldingManager.createHoldingAndReturnId(instanceId, bindItem.getPermanentLocationId(), locationContext)); } - private Map> updateTitleWithBindItems(Map> piecesByPoLineIds, - RequestContext requestContext) { - piecesByPoLineIds.forEach((poLineId, pieces) -> { - List itemIds = pieces.stream().map(Piece::getItemId).distinct().toList(); - titlesService.getTitlesByQuery(String.format(TITLE_BY_POLINE_QUERY, poLineId), requestContext) - .map(titles -> updateTitle(titles, itemIds, requestContext)); - }); - return piecesByPoLineIds; + private Future storeUpdatedPieces(BindPiecesHolder holder, RequestContext requestContext) { + return storeUpdatedPieceRecords(holder.getPiecesGroupedByPoLine(), requestContext) + .map(m -> holder); + } + + private Future updateTitleWithBindItems(BindPiecesHolder holder, RequestContext requestContext) { + var itemIds = holder.getPieces().map(Piece::getItemId).distinct().toList(); + return titlesService.getTitlesByQuery(String.format(TITLE_BY_POLINE_QUERY, holder.getPoLineId()), requestContext) + .map(titles -> updateTitle(titles, itemIds, requestContext)) + .map(v -> holder); } private Future updateTitle(List titles, List<String> itemIds, RequestContext requestContext) { @@ -235,25 +238,11 @@ private Future<Void> updateTitle(List<Title> titles, List<String> itemIds, Reque return titlesService.saveTitle(title, requestContext); } - private ReceivingResults prepareResponseBody(Map<String, List<Piece>> piecesGroupedByPoLine, - BindPiecesCollection bindPiecesCollection) { - String poLineId = bindPiecesCollection.getPoLineId(); - - // Get all processed piece records for PO Line - Map<String, Piece> processedPiecesForPoLine = getProcessedPiecesForPoLine(poLineId, piecesGroupedByPoLine); - - var resultCounts = getEmptyResultCounts(); - ReceivingResult result = new ReceivingResult(); - for (String pieceId : bindPiecesCollection.getBindPieceIds()) { - calculateProcessingErrors(poLineId, result, processedPiecesForPoLine, resultCounts, pieceId); - } - - result.withPoLineId(poLineId) - .withProcessedSuccessfully(resultCounts.get(ProcessingStatus.Type.SUCCESS)) - .withProcessedWithError(resultCounts.get(ProcessingStatus.Type.FAILURE)); - return new ReceivingResults() - .withTotalRecords(1) - .withReceivingResults(List.of(result)); + private BindPiecesResult prepareResponseBody(BindPiecesHolder holder) { + return new BindPiecesResult() + .withPoLineId(holder.getPoLineId()) + .withBoundPieceIds(holder.getPieces().map(Piece::getId).toList()) + .withItemId(holder.getBindItemId()); } @Override diff --git a/src/main/java/org/folio/models/pieces/BindPiecesHolder.java b/src/main/java/org/folio/models/pieces/BindPiecesHolder.java new file mode 100644 index 000000000..8f9c68e8e --- /dev/null +++ b/src/main/java/org/folio/models/pieces/BindPiecesHolder.java @@ -0,0 +1,51 @@ +package org.folio.models.pieces; + +import one.util.streamex.StreamEx; +import org.folio.rest.jaxrs.model.Piece; +import org.folio.rest.jaxrs.model.BindPiecesCollection; + +import java.util.List; +import java.util.Map; + +public class BindPiecesHolder { + + private BindPiecesCollection bindPiecesCollection; + private Map<String, List<Piece>> piecesGroupedByPoLine; + private String bindItemId; + + public BindPiecesCollection getBindPiecesCollection() { + return bindPiecesCollection; + } + + public BindPiecesHolder withBindPiecesCollection(BindPiecesCollection bindPiecesCollection) { + this.bindPiecesCollection = bindPiecesCollection; + return this; + } + + public Map<String, List<Piece>> getPiecesGroupedByPoLine() { + return piecesGroupedByPoLine; + } + + public BindPiecesHolder withPiecesGroupedByPoLine(Map<String, List<Piece>> piecesGroupedByPoLine) { + this.piecesGroupedByPoLine = piecesGroupedByPoLine; + return this; + } + + public String getBindItemId() { + return bindItemId; + } + + public BindPiecesHolder withBindItemId(String bindItemId) { + this.bindItemId = bindItemId; + return this; + } + + public StreamEx<Piece> getPieces() { + return StreamEx.ofValues(piecesGroupedByPoLine).flatMap(List::stream); + } + + public String getPoLineId() { + return bindPiecesCollection.getPoLineId(); + } + +} diff --git a/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java b/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java index 1c334965b..7c8b3faea 100644 --- a/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java +++ b/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java @@ -10,6 +10,7 @@ import org.folio.orders.utils.PoLineCommonUtil; import org.folio.rest.acq.model.PieceCollection; import org.folio.rest.jaxrs.model.BindPiecesCollection; +import org.folio.rest.jaxrs.model.BindPiecesResult; import org.folio.rest.jaxrs.model.CheckInPiece; import org.folio.rest.jaxrs.model.CheckinCollection; import org.folio.rest.jaxrs.model.CompositePoLine; @@ -1000,15 +1001,19 @@ void testBindPiecesToTitleWithItem() { addMockEntry(PIECES_STORAGE, bindingPiece2); addMockEntry(TITLES, getTitle(poLine)); + var pieceIds = List.of(bindingPiece1.getId(), bindingPiece2.getId()); var bindPiecesCollection = new BindPiecesCollection() .withPoLineId(poLine.getId()) .withBindItem(getMinimalContentBindItem()) - .withBindPieceIds(List.of(bindingPiece1.getId(), bindingPiece2.getId())); + .withBindPieceIds(pieceIds); var response = verifyPostResponse(ORDERS_BIND_ENDPOINT, JsonObject.mapFrom(bindPiecesCollection).encode(), - prepareHeaders(EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10), APPLICATION_JSON, HttpStatus.HTTP_OK.toInt()); + prepareHeaders(EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10), APPLICATION_JSON, HttpStatus.HTTP_OK.toInt()) + .as(BindPiecesResult.class); - assertThat(response.as(ReceivingResults.class).getReceivingResults().get(0).getProcessedSuccessfully(), is(2)); + assertThat(response.getPoLineId(), is(poLine.getId())); + assertThat(response.getBoundPieceIds(), is(pieceIds)); + assertThat(response.getItemId(), notNullValue()); var pieceUpdates = getPieceUpdates(); assertThat(pieceUpdates, notNullValue()); @@ -1154,12 +1159,13 @@ void testBindPiecesToTitleWithTransferRequestsAction() { addMockEntry(TITLES, getTitle(poLine)); var response = verifyPostResponse(ORDERS_BIND_ENDPOINT, JsonObject.mapFrom(bindPiecesCollection).encode(), - prepareHeaders(EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10), APPLICATION_JSON, HttpStatus.HTTP_OK.toInt()); + prepareHeaders(EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10), APPLICATION_JSON, HttpStatus.HTTP_OK.toInt()) + .as(BindPiecesResult.class); - assertThat(response.as(ReceivingResults.class).getReceivingResults().get(0).getProcessedSuccessfully(), is(1)); + assertThat(response.getPoLineId(), is(poLine.getId())); + assertThat(response.getBoundPieceIds(), is(List.of(bindingPiece.getId()))); var pieceUpdates = getPieceUpdates(); - assertThat(pieceUpdates, notNullValue()); assertThat(pieceUpdates, hasSize(bindPiecesCollection.getBindPieceIds().size()));