Skip to content

Commit

Permalink
Merge branch 'master' into MODORDERS-852-Some-unit-tests-are-not-exec…
Browse files Browse the repository at this point in the history
…uted-2
  • Loading branch information
SerhiiNosko authored Jan 24, 2024
2 parents af37b75 + d250836 commit 439a244
Show file tree
Hide file tree
Showing 22 changed files with 809 additions and 108 deletions.
28 changes: 28 additions & 0 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,26 @@
"orders-storage.reporting-codes.item.get"
]
},
{
"methods": [
"POST"
],
"pathPattern": "/orders/expect",
"permissionsRequired": [
"orders.expect.collection.post"
],
"modulePermissions": [
"orders-storage.pieces.collection.get",
"orders-storage.pieces.item.put",
"orders-storage.po-lines.collection.get",
"orders-storage.po-lines.item.put",
"orders-storage.purchase-orders.item.get",
"orders-storage.purchase-orders.item.put",
"orders-storage.titles.collection.get",
"acquisitions-units-storage.units.collection.get",
"acquisitions-units-storage.memberships.collection.get"
]
},
{
"methods": [
"GET"
Expand Down Expand Up @@ -485,6 +505,8 @@
"inventory-storage.items.item.post",
"inventory-storage.instance-types.collection.get",
"inventory-storage.instance-statuses.collection.get",
"finance.funds.budget.item.get",
"finance.fiscal-years.item.get",
"finance.order-transaction-summaries.item.get",
"finance.order-transaction-summaries.item.post",
"finance.order-transaction-summaries.item.put",
Expand Down Expand Up @@ -1275,6 +1297,11 @@
"displayName": "Orders - Check-in items",
"description": "Check-in items spanning one or more po-lines in this order"
},
{
"permissionName": "orders.expect.collection.post",
"displayName": "Orders - Expect pieces",
"description": "Expect pieces spanning one or more po-lines in this order"
},
{
"permissionName": "orders.receiving-history.collection.get",
"displayName": "Orders - Receiving history",
Expand Down Expand Up @@ -1695,6 +1722,7 @@
"orders.po-number.item.post",
"orders.receiving.collection.post",
"orders.check-in.collection.post",
"orders.expect.collection.post",
"orders.receiving-history.collection.get",
"orders.pieces.all",
"orders.acquisitions-units-assignments.all",
Expand Down
38 changes: 38 additions & 0 deletions ramls/expect.raml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#%RAML 1.0
title: Receive
baseUri: https://github.com/folio-org/mod-orders
version: v1
protocols: [ HTTP, HTTPS ]

documentation:
- title: Orders Business Logic API
content: <b>API for transitioning pieces status from Unreceivable to Expected</b>

types:
expect-collection: !include acq-models/mod-orders/schemas/expectCollection.json
receiving-results: !include acq-models/mod-orders/schemas/receivingResults.json
errors: !include raml-util/schemas/errors.schema
UUID:
type: string
pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$

traits:
validate: !include raml-util/traits/validation.raml

resourceTypes:
post-with-200: !include rtypes/post-json-200.raml

/orders/expect:
displayName: Expect pieces
description: |
Expect pieces spanning one or more PO lines. The endpoint is used to:
- move a unreceivable piece back to "Expected"
type:
post-with-200:
requestSchema: expect-collection
responseSchema: receiving-results
requestExample: !include acq-models/mod-orders/examples/expectCollection.sample
responseExample: !include acq-models/mod-orders/examples/receivingResults.sample
is: [validate]
post:
description: Expect pieces spanning one or more PO lines
12 changes: 7 additions & 5 deletions src/main/java/org/folio/helper/CheckinReceivePiecesHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.folio.rest.core.exceptions.ErrorCodes.PIECE_UPDATE_FAILED;
import static org.folio.rest.core.exceptions.ErrorCodes.USER_HAS_NO_PERMISSIONS;
import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.AWAITING_RECEIPT;
import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.CANCELLED;
import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.FULLY_RECEIVED;
import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.ONGOING;
import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.PARTIALLY_RECEIVED;
Expand Down Expand Up @@ -191,7 +192,7 @@ private void addPieceIfValid(Piece piece, Map<String, List<Piece>> piecesByPoLin
// request
if (piecesByLineId.containsKey(poLineId) && piecesByLineId.get(poLineId).containsKey(pieceId)) {
// Validate if the piece is not yet received
if (piece.getReceivingStatus() == ReceivingStatus.EXPECTED || isRevertToOnOrder(piece)) {
if (piece.getReceivingStatus() != ReceivingStatus.RECEIVED || isRevertToOnOrder(piece)) {
piecesByPoLine.computeIfAbsent(poLineId, v -> new ArrayList<>())
.add(piece);
} else {
Expand Down Expand Up @@ -299,11 +300,12 @@ protected Future<Map<String, List<Piece>>> updateOrderAndPoLinesStatus(Map<Strin
// Skip status update if PO line status is Ongoing
List<Future<String>> futures = new ArrayList<>();
for (PoLine poLine : poLines) {
if (poLine.getReceiptStatus() != ONGOING) {
List<Piece> successfullyProcessedPieces = getSuccessfullyProcessedPieces(poLine.getId(), piecesGroupedByPoLine);
futures.add(calculatePoLineReceiptStatus(poLine, successfullyProcessedPieces, requestContext)
.compose(status -> purchaseOrderLineService.updatePoLineReceiptStatus(poLine, status, requestContext)));
if (poLine.getReceiptStatus() == CANCELLED || poLine.getReceiptStatus() == ONGOING) {
continue;
}
List<Piece> successfullyProcessedPieces = getSuccessfullyProcessedPieces(poLine.getId(), piecesGroupedByPoLine);
futures.add(calculatePoLineReceiptStatus(poLine, successfullyProcessedPieces, requestContext)
.compose(status -> purchaseOrderLineService.updatePoLineReceiptStatus(poLine, status, requestContext)));
}

return collectResultsOnSuccess(futures).map(updatedPoLines -> {
Expand Down
151 changes: 151 additions & 0 deletions src/main/java/org/folio/helper/ExpectHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package org.folio.helper;

import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.json.JsonObject;
import one.util.streamex.StreamEx;
import org.apache.commons.lang3.StringUtils;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.jaxrs.model.ExpectCollection;
import org.folio.rest.jaxrs.model.ExpectPiece;
import org.folio.rest.jaxrs.model.Piece;
import org.folio.rest.jaxrs.model.ProcessingStatus;
import org.folio.rest.jaxrs.model.ReceivingResult;
import org.folio.rest.jaxrs.model.ReceivingResults;
import org.folio.rest.jaxrs.model.ToBeExpected;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;

public class ExpectHelper extends CheckinReceivePiecesHelper<ExpectPiece> {

/**
* Map with PO line id as a key and value is map with piece id as a key and
* {@link ExpectPiece} as a value
*/
private final Map<String, Map<String, ExpectPiece>> expectPieces;

public ExpectHelper(ExpectCollection expectCollection, Map<String, String> okapiHeaders, Context ctx) {
super(okapiHeaders, ctx);
// Convert request to map representation
expectPieces = groupExpectPieceByPoLineId(expectCollection);

// Logging quantity of the piece records to be expected
if (logger.isDebugEnabled()) {
int poLinesQty = expectPieces.size();
int piecesQty = StreamEx.ofValues(expectPieces)
.mapToInt(Map::size)
.sum();
logger.debug("{} piece record(s) are going to be expected for {} PO line(s)", piecesQty, poLinesQty);
}
}

public Future<ReceivingResults> expectPieces(ExpectCollection expectCollection, RequestContext requestContext) {
return getPoLines(new ArrayList<>(expectPieces.keySet()), requestContext)
.compose(poLines -> removeForbiddenEntities(poLines, expectPieces, requestContext))
.compose(vVoid -> processExpectPieces(expectCollection, requestContext));
}

private Future<ReceivingResults> processExpectPieces(ExpectCollection expectCollection, RequestContext requestContext) {
// 1. Get piece records from storage
return retrievePieceRecords(expectPieces, requestContext)
// 2. Update piece status to Expected
.map(this::updatePieceRecords)
// 3. Update received piece records in the storage
.compose(piecesGroupedByPoLine -> storeUpdatedPieceRecords(piecesGroupedByPoLine, requestContext))
// 4. Return results to the client
.map(piecesGroupedByPoLine -> prepareResponseBody(expectCollection, piecesGroupedByPoLine));
}

private Map<String, Map<String, ExpectPiece>> groupExpectPieceByPoLineId(ExpectCollection expectCollection) {
return StreamEx
.of(expectCollection.getToBeExpected())
.distinct()
.groupingBy(ToBeExpected::getPoLineId,
mapping(ToBeExpected::getExpectPieces,
collectingAndThen(toList(),
lists -> StreamEx.of(lists)
.flatMap(List::stream)
.toMap(ExpectPiece::getId, expectPiece -> expectPiece))));
}

private ReceivingResults prepareResponseBody(ExpectCollection expectCollection,
Map<String, List<Piece>> piecesGroupedByPoLine) {
ReceivingResults results = new ReceivingResults();
results.setTotalRecords(expectCollection.getTotalRecords());
for (ToBeExpected toBeExpected : expectCollection.getToBeExpected()) {
String poLineId = toBeExpected.getPoLineId();
ReceivingResult result = new ReceivingResult();
results.getReceivingResults().add(result);

// Get all processed piece records for PO Line
Map<String, Piece> processedPiecesForPoLine = StreamEx
.of(piecesGroupedByPoLine.getOrDefault(poLineId, Collections.emptyList()))
.toMap(Piece::getId, piece -> piece);

Map<String, Integer> resultCounts = new HashMap<>();
resultCounts.put(ProcessingStatus.Type.SUCCESS.toString(), 0);
resultCounts.put(ProcessingStatus.Type.FAILURE.toString(), 0);
for (ExpectPiece expectPiece : toBeExpected.getExpectPieces()) {
String pieceId = expectPiece.getId();

calculateProcessingErrors(poLineId, result, processedPiecesForPoLine, resultCounts, pieceId);
}

result.withPoLineId(poLineId)
.withProcessedSuccessfully(resultCounts.get(ProcessingStatus.Type.SUCCESS.toString()))
.withProcessedWithError(resultCounts.get(ProcessingStatus.Type.FAILURE.toString()));
}

return results;
}

private Map<String, List<Piece>> updatePieceRecords(Map<String, List<Piece>> piecesGroupedByPoLine) {
StreamEx.ofValues(piecesGroupedByPoLine)
.flatMap(List::stream)
.forEach(this::updatePieceWithExpectInfo);

return piecesGroupedByPoLine;
}

private void updatePieceWithExpectInfo(Piece piece) {
ExpectPiece expectPiece = piecesByLineId.get(piece.getPoLineId())
.get(piece.getId());

piece.setComment(expectPiece.getComment());
piece.setReceivedDate(null);
piece.setReceivingStatus(Piece.ReceivingStatus.EXPECTED);
}

@Override
protected boolean isRevertToOnOrder(Piece piece) {
return false;
}

@Override
protected Future<Boolean> receiveInventoryItemAndUpdatePiece(JsonObject item, Piece piece, RequestContext requestContext) {
return Future.succeededFuture(false);
}

@Override
protected Map<String, List<Piece>> updatePieceRecordsWithoutItems(Map<String, List<Piece>> piecesGroupedByPoLine) {
return Collections.emptyMap();
}

@Override
protected String getHoldingId(Piece piece) {
return StringUtils.EMPTY;
}

@Override
protected String getLocationId(Piece piece) {
return StringUtils.EMPTY;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,13 @@ public void handle(Message<JsonObject> message) {
// 1. Get all pieces for poLineId
getPieces(query, requestContext)
.onSuccess(listOfPieces ->
// 2. Get PoLine for the poLineId which will be used to calculate PoLineReceiptStatus
purchaseOrderLineService.getOrderLineById(poLineIdUpdate, requestContext)
.map(poLine -> {
if (poLine.getReceiptStatus().equals(PoLine.ReceiptStatus.ONGOING)) {
promise.complete();
} else {
// 2. Get PoLine for the poLineId which will be used to calculate PoLineReceiptStatus
purchaseOrderLineService.getOrderLineById(poLineIdUpdate, requestContext)
.map(poLine -> {
if (poLine.getReceiptStatus() == ReceiptStatus.CANCELLED || poLine.getReceiptStatus() == ReceiptStatus.ONGOING) {
promise.complete();
return null;
}
calculatePoLineReceiptStatus(poLine, listOfPieces)
.compose(status -> purchaseOrderLineService.updatePoLineReceiptStatus(poLine, status, requestContext))
.map(updatedPoLineId -> {
Expand All @@ -87,13 +88,12 @@ public void handle(Message<JsonObject> message) {
logger.error("The error updating poLine by id {}", poLineIdUpdate, e);
promise.fail(e);
});
}
return null;
})
.onFailure(e -> {
logger.error("The error getting poLine by id {}", poLineIdUpdate, e);
promise.fail(e);
}))
return null;
})
.onFailure(e -> {
logger.error("The error getting poLine by id {}", poLineIdUpdate, e);
promise.fail(e);
}))
.onFailure(e -> {
logger.error("The error happened getting all pieces by poLine {}", poLineIdUpdate, e);
promise.fail(e);
Expand Down
Loading

0 comments on commit 439a244

Please sign in to comment.