Skip to content

Commit

Permalink
[MODORDERS-1122] Add logic to create item in any tenant for binding (#…
Browse files Browse the repository at this point in the history
…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 1dcda97.

* [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
  • Loading branch information
Saba-Zedginidze-EPAM authored Jun 4, 2024
1 parent 1931b22 commit 3988230
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 82 deletions.
10 changes: 5 additions & 5 deletions ramls/bind-pieces.raml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
129 changes: 59 additions & 70 deletions src/main/java/org/folio/helper/BindHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -69,47 +68,52 @@ private Map<String, Map<String, BindPiecesCollection>> groupBindPieceByPoLineId(
return Map.of(poLineId, bindPieceMap);
}

public Future<ReceivingResults> bindPieces(BindPiecesCollection bindPiecesCollection, RequestContext requestContext) {
public Future<BindPiecesResult> bindPieces(BindPiecesCollection bindPiecesCollection, RequestContext requestContext) {
return removeForbiddenEntities(requestContext)
.compose(vVoid -> processBindPieces(bindPiecesCollection, requestContext));
}

private Future<ReceivingResults> processBindPieces(BindPiecesCollection bindPiecesCollection, RequestContext requestContext) {
private Future<BindPiecesResult> 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<Map<String, List<Piece>>> checkRequestsForPieceItems(Map<String, List<Piece>> piecesGroupedByPoLine,
BindPiecesCollection bindPiecesCollection,
RequestContext requestContext) {
var tenantToItem = mapTenantIdsToItemIds(piecesGroupedByPoLine, requestContext);
private BindPiecesHolder generateHolder(Map<String, List<Piece>> piecesGroupedByPoLine, BindPiecesCollection bindPiecesCollection) {
return new BindPiecesHolder()
.withBindPiecesCollection(bindPiecesCollection)
.withPiecesGroupedByPoLine(piecesGroupedByPoLine);
}

private Future<BindPiecesHolder> 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<Void> validateItemsForRequestTransfer(Set<String> tenants,
List<String> items,
private Future<Void> validateItemsForRequestTransfer(Set<String> tenants, List<String> items,
BindPiecesCollection bindPiecesCollection) {
if (items.isEmpty()) {
return Future.succeededFuture();
Expand All @@ -133,17 +137,16 @@ private Future<Void> validateItemsForRequestTransfer(Set<String> tenants,
return Future.succeededFuture();
}

private Map<String, List<Piece>> updatePieceRecords(Map<String, List<Piece>> 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<Map<String, List<Piece>>> updateItemStatus(Map<String, List<Piece>> piecesGroupedByPoLine, RequestContext requestContext) {
private Future<BindPiecesHolder> 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)
Expand All @@ -158,15 +161,13 @@ private Future<Map<String, List<Piece>>> updateItemStatus(Map<String, List<Piece
});
})
.toList()
).map(f -> piecesGroupedByPoLine);
).map(f -> holder);
}

private Future<Map<String, List<Piece>>> createItemForPiece(Map<String, List<Piece>> piecesGroupedByPoLine,
BindPiecesCollection bindPiecesCollection,
RequestContext requestContext) {
var poLineId = bindPiecesCollection.getPoLineId();
var holdingIds = piecesGroupedByPoLine.values()
.stream().flatMap(List::stream)
private Future<BindPiecesHolder> 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);
Expand All @@ -177,12 +178,12 @@ private Future<Map<String, List<Piece>>> createItemForPiece(Map<String, List<Pie
.map(newItemId -> {
// 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);
});
}

Expand Down Expand Up @@ -214,14 +215,16 @@ private Future<String> createShadowInstanceAndHoldingIfNeeded(CompositePoLine co
.compose(s -> inventoryHoldingManager.createHoldingAndReturnId(instanceId, bindItem.getPermanentLocationId(), locationContext));
}

private Map<String, List<Piece>> updateTitleWithBindItems(Map<String, List<Piece>> piecesByPoLineIds,
RequestContext requestContext) {
piecesByPoLineIds.forEach((poLineId, pieces) -> {
List<String> 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<BindPiecesHolder> storeUpdatedPieces(BindPiecesHolder holder, RequestContext requestContext) {
return storeUpdatedPieceRecords(holder.getPiecesGroupedByPoLine(), requestContext)
.map(m -> holder);
}

private Future<BindPiecesHolder> 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<Void> updateTitle(List<Title> titles, List<String> itemIds, RequestContext requestContext) {
Expand All @@ -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
Expand Down
51 changes: 51 additions & 0 deletions src/main/java/org/folio/models/pieces/BindPiecesHolder.java
Original file line number Diff line number Diff line change
@@ -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();
}

}
18 changes: 12 additions & 6 deletions src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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()));

Expand Down

0 comments on commit 3988230

Please sign in to comment.