diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json
index 8a882315b..175a06ef3 100644
--- a/descriptors/ModuleDescriptor-template.json
+++ b/descriptors/ModuleDescriptor-template.json
@@ -627,6 +627,31 @@
"invoice.invoice-lines.item.put"
]
},
+ {
+ "methods": ["PUT"],
+ "pathPattern": "/orders/pieces-batch/status",
+ "permissionsRequired": ["orders.pieces.collection.put"],
+ "modulePermissions": [
+ "orders-storage.pieces.item.put",
+ "orders-storage.pieces.collection.get",
+ "orders-storage.po-lines.item.get",
+ "orders-storage.po-lines.item.put",
+ "orders-storage.po-lines.collection.get",
+ "orders-storage.purchase-orders.item.get",
+ "orders-storage.purchase-orders.item.put",
+ "orders-storage.titles.collection.get",
+ "finance.funds.item.get",
+ "finance.ledgers.current-fiscal-year.item.get",
+ "finance.transactions.collection.get",
+ "finance.transactions.batch.execute",
+ "inventory.items.item.get",
+ "inventory.items.item.put",
+ "inventory.items.collection.get",
+ "inventory-storage.holdings.collection.get",
+ "acquisitions-units-storage.units.collection.get",
+ "acquisitions-units-storage.memberships.collection.get"
+ ]
+ },
{
"methods": ["GET"],
"pathPattern": "/orders/pieces-requests",
@@ -1519,6 +1544,11 @@
"displayName": "orders - delete an existing piece record",
"description": "Delete an existing piece"
},
+ {
+ "permissionName": "orders.pieces.collection.put",
+ "displayName": "orders - batch update piece statuses",
+ "description": "Batch update piece statuses"
+ },
{
"permissionName": "orders.piece-requests.collection.get",
"displayName": "Orders - Get piece requests",
@@ -1530,6 +1560,7 @@
"description" : "All permissions for the pieces",
"subPermissions" : [
"orders.pieces.collection.get",
+ "orders.pieces.collection.put",
"orders.pieces.item.post",
"orders.pieces.item.get",
"orders.pieces.item.put",
diff --git a/ramls/acq-models b/ramls/acq-models
index c1f931704..c0c72775d 160000
--- a/ramls/acq-models
+++ b/ramls/acq-models
@@ -1 +1 @@
-Subproject commit c1f931704fc6d2dedfc671e308b27f56499750a7
+Subproject commit c0c72775d08c28a77d1475df435497e8b0020667
diff --git a/ramls/pieces-batch.raml b/ramls/pieces-batch.raml
new file mode 100644
index 000000000..65bcdc992
--- /dev/null
+++ b/ramls/pieces-batch.raml
@@ -0,0 +1,49 @@
+#%RAML 1.0
+title: Pieces Batch Operations
+baseUri: https://github.com/folio-org/mod-orders
+version: v1
+protocols: [ HTTP, HTTPS ]
+
+documentation:
+ - title: Endpoint for batch operations on Pieces
+ content: Endpoint for batch operations on Pieces
+
+types:
+ piece-batch-status-collection: !include acq-models/mod-orders/schemas/pieceStatusBatchCollection.json
+
+/orders/pieces-batch:
+ /status:
+ put:
+ description: Batch status update for pieces
+ body:
+ application/json:
+ type: piece-batch-status-collection
+ example:
+ strict: false
+ value: !include acq-models/mod-orders/examples/pieceStatusBatchCollection.sample
+ responses:
+ 204:
+ description: "Piece records successfully updated"
+ 400:
+ description: "Bad request"
+ body:
+ application/json:
+ example:
+ strict: false
+ value: !include examples/errors_400.sample
+ text/plain:
+ example: "Unable to update pieces - Bad request"
+ 404:
+ description: "Pieces do not exist"
+ body:
+ text/plain:
+ example: "Following pieces are not found: [id1, id2]"
+ 500:
+ description: "Internal server error, e.g. due to misconfiguration"
+ body:
+ application/json:
+ example:
+ strict: false
+ value: !include examples/errors_500.sample
+ text/plain:
+ example: "Unable to update pieces - Internal server error, e.g. due to misconfiguration"
diff --git a/src/main/java/org/folio/config/ApplicationConfig.java b/src/main/java/org/folio/config/ApplicationConfig.java
index 53adb0228..a5fd0af89 100644
--- a/src/main/java/org/folio/config/ApplicationConfig.java
+++ b/src/main/java/org/folio/config/ApplicationConfig.java
@@ -641,13 +641,12 @@ PieceUpdateFlowPoLineService pieceUpdateFlowPoLineService(PurchaseOrderStorageSe
}
@Bean
- PieceUpdateFlowManager pieceUpdateFlowManager(PieceStorageService pieceStorageService, PieceService pieceService, ProtectionService protectionService,
- PieceUpdateFlowPoLineService pieceUpdateFlowPoLineService, PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager,
- BasePieceFlowHolderBuilder basePieceFlowHolderBuilder, DefaultPieceFlowsValidator defaultPieceFlowsValidator,
- PurchaseOrderLineService purchaseOrderLineService) {
- return new PieceUpdateFlowManager(pieceStorageService, pieceService, protectionService, pieceUpdateFlowPoLineService,
- pieceUpdateFlowInventoryManager, basePieceFlowHolderBuilder, defaultPieceFlowsValidator,
- purchaseOrderLineService);
+ PieceUpdateFlowManager pieceUpdateFlowManager(PieceStorageService pieceStorageService, PieceService pieceService, TitlesService titlesService,
+ ProtectionService protectionService, PieceUpdateFlowPoLineService pieceUpdateFlowPoLineService,
+ PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager, BasePieceFlowHolderBuilder basePieceFlowHolderBuilder,
+ DefaultPieceFlowsValidator defaultPieceFlowsValidator, PurchaseOrderLineService purchaseOrderLineService) {
+ return new PieceUpdateFlowManager(pieceStorageService, pieceService, titlesService, protectionService, pieceUpdateFlowPoLineService,
+ pieceUpdateFlowInventoryManager, basePieceFlowHolderBuilder, defaultPieceFlowsValidator, purchaseOrderLineService);
}
@Bean
diff --git a/src/main/java/org/folio/models/pieces/PieceBatchStatusUpdateHolder.java b/src/main/java/org/folio/models/pieces/PieceBatchStatusUpdateHolder.java
new file mode 100644
index 000000000..b65c1663f
--- /dev/null
+++ b/src/main/java/org/folio/models/pieces/PieceBatchStatusUpdateHolder.java
@@ -0,0 +1,28 @@
+package org.folio.models.pieces;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.folio.rest.jaxrs.model.Piece;
+
+import java.util.List;
+
+@AllArgsConstructor
+public class PieceBatchStatusUpdateHolder extends BasePieceFlowHolder {
+
+ @Getter
+ private Piece.ReceivingStatus receivingStatus;
+ @Getter
+ private List pieces;
+ private String poLineId;
+
+ @Override
+ public String getOrderLineId() {
+ return poLineId;
+ }
+
+ @Override
+ public String getTitleId() {
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/folio/orders/events/handlers/ReceiptStatusConsistency.java b/src/main/java/org/folio/orders/events/handlers/ReceiptStatusConsistency.java
index 7b96003da..e234e935e 100644
--- a/src/main/java/org/folio/orders/events/handlers/ReceiptStatusConsistency.java
+++ b/src/main/java/org/folio/orders/events/handlers/ReceiptStatusConsistency.java
@@ -1,162 +1,88 @@
package org.folio.orders.events.handlers;
-import static org.folio.helper.CheckinReceivePiecesHelper.EXPECTED_STATUSES;
-import static org.folio.helper.CheckinReceivePiecesHelper.RECEIVED_STATUSES;
-import static org.folio.orders.utils.ResourcePathResolver.PIECES_STORAGE;
-import static org.folio.orders.utils.ResourcePathResolver.resourcesPath;
-import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.AWAITING_RECEIPT;
-import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.FULLY_RECEIVED;
-import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.PARTIALLY_RECEIVED;
+import static org.folio.orders.events.utils.EventUtils.getPoLineId;
+import static org.folio.orders.utils.HelperUtils.getOkapiHeaders;
+import static org.folio.service.orders.utils.StatusUtils.calculatePoLineReceiptStatus;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import org.apache.commons.collections4.CollectionUtils;
import org.folio.helper.BaseHelper;
import org.folio.orders.utils.HelperUtils;
import org.folio.orders.utils.PoLineCommonUtil;
-import org.folio.rest.core.RestClient;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.jaxrs.model.Piece;
-import org.folio.rest.jaxrs.model.Piece.ReceivingStatus;
-import org.folio.rest.jaxrs.model.PieceCollection;
import org.folio.rest.jaxrs.model.PoLine;
-import org.folio.rest.jaxrs.model.PoLine.ReceiptStatus;
import org.folio.service.orders.PurchaseOrderLineService;
+import org.folio.service.pieces.PieceStorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import io.vertx.core.Future;
import io.vertx.core.Handler;
-import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
-import one.util.streamex.StreamEx;
@Component("receiptStatusHandler")
public class ReceiptStatusConsistency extends BaseHelper implements Handler> {
- private static final int LIMIT = Integer.MAX_VALUE;
- private static final String PIECES_ENDPOINT = resourcesPath(PIECES_STORAGE) + "?query=poLineId==%s&limit=%s";
-
+ private final PieceStorageService pieceStorageService;
private final PurchaseOrderLineService purchaseOrderLineService;
@Autowired
- public ReceiptStatusConsistency(Vertx vertx, PurchaseOrderLineService purchaseOrderLineService) {
+ public ReceiptStatusConsistency(Vertx vertx, PieceStorageService pieceStorageService, PurchaseOrderLineService purchaseOrderLineService) {
super(vertx.getOrCreateContext());
+ this.pieceStorageService = pieceStorageService;
this.purchaseOrderLineService = purchaseOrderLineService;
}
@Override
public void handle(Message message) {
- JsonObject messageFromEventBus = message.body();
-
+ var messageFromEventBus = message.body();
logger.info("Received message body: {}", messageFromEventBus);
- Map okapiHeaders = org.folio.orders.utils.HelperUtils.getOkapiHeaders(message);
+ var okapiHeaders = getOkapiHeaders(message);
var requestContext = new RequestContext(ctx, okapiHeaders);
- List> futures = new ArrayList<>();
- Promise promise = Promise.promise();
- futures.add(promise.future());
-
- String poLineIdUpdate = messageFromEventBus.getString("poLineIdUpdate");
- String query = String.format(PIECES_ENDPOINT, poLineIdUpdate, LIMIT);
-
- // 1. Get all pieces for poLineId
- getPieces(query, requestContext)
- .compose(listOfPieces ->
- // 2. Get PoLine for the poLineId which will be used to calculate PoLineReceiptStatus
- purchaseOrderLineService.getOrderLineById(poLineIdUpdate, requestContext)
- .map(poLine -> {
- if (PoLineCommonUtil.isCancelledOrOngoingStatus(poLine)) {
- promise.complete();
- return null;
- }
- ReceiptStatus receivingStatus = calculatePoLineReceiptStatus(poLine, listOfPieces);
- boolean statusUpdated = purchaseOrderLineService.updatePoLineReceiptStatusWithoutSave(poLine, receivingStatus);
- if (statusUpdated) {
- purchaseOrderLineService.saveOrderLine(poLine, requestContext)
- .map(aVoid -> {
- // send event to update order status
- updateOrderStatus(poLine, okapiHeaders, requestContext);
- promise.complete();
- return null;
- })
- .onFailure(e -> {
- logger.error("The error updating poLine by id {}", poLineIdUpdate, e);
- promise.fail(e);
- });
- } else {
- promise.complete();
- }
- 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);
- });
- // Now wait for all operations to be completed and send reply
- completeAllFutures(futures, message);
- }
+ var poLineId = getPoLineId(messageFromEventBus);
+ var future = pieceStorageService.getPiecesByLineId(poLineId, requestContext)
+ .compose(pieces -> purchaseOrderLineService.getOrderLineById(poLineId, requestContext)
+ .compose(poLine -> updatePoLineAndOrderStatuses(pieces, poLine, requestContext))
+ .onFailure(e -> logger.error("Exception occurred while fetching PoLine by id: '{}'", poLineId, e)))
+ .onFailure(e -> logger.error("Exception occurred while fetching pieces by PoLine id: '{}'", poLineId, e));
- private void updateOrderStatus(PoLine poLine, Map okapiHeaders, RequestContext requestContext) {
- List poIds = StreamEx
- .of(poLine)
- .map(PoLine::getPurchaseOrderId)
- .distinct()
- .map(orderId -> new JsonObject().put(ORDER_ID, orderId))
- .toList();
- JsonObject messageContent = new JsonObject();
- messageContent.put(OKAPI_HEADERS, okapiHeaders);
- // Collect order ids which should be processed
- messageContent.put(EVENT_PAYLOAD, new JsonArray(poIds));
- HelperUtils.sendEvent(MessageAddress.RECEIVE_ORDER_STATUS_UPDATE, messageContent, requestContext);
+ completeAllFutures(List.of(future), message);
}
- private ReceiptStatus calculatePoLineReceiptStatus(PoLine poLine, List pieces) {
-
- if (CollectionUtils.isEmpty(pieces)) {
- logger.info("No pieces processed - receipt status unchanged for PO Line '{}'", poLine.getId());
- return poLine.getReceiptStatus();
+ private Future updatePoLineAndOrderStatuses(List pieces, PoLine poLine, RequestContext requestContext) {
+ if (PoLineCommonUtil.isCancelledOrOngoingStatus(poLine)) {
+ logger.info("updatePoLineAndOrderStatuses:: PoLine with id: '{}' has status: '{}', skipping...", poLine.getId(), poLine.getReceiptStatus());
+ return Future.succeededFuture();
}
-
- long expectedQty = getPiecesQuantityByPoLineAndStatus(EXPECTED_STATUSES, pieces);
- return calculatePoLineReceiptStatus(expectedQty, pieces);
- }
-
- private ReceiptStatus calculatePoLineReceiptStatus(long expectedPiecesQuantity, List pieces) {
- if (expectedPiecesQuantity == 0) {
- logger.info("calculatePoLineReceiptStatus:: Fully received");
- return FULLY_RECEIVED;
- }
-
- if (StreamEx.of(pieces).anyMatch(piece -> RECEIVED_STATUSES.contains(piece.getReceivingStatus()))) {
- logger.info("calculatePoLineReceiptStatus:: Partially Received - In case there is at least one successfully received piece");
- return PARTIALLY_RECEIVED;
+ var newStatus = pieces.isEmpty()
+ ? poLine.getReceiptStatus()
+ : calculatePoLineReceiptStatus(poLine.getId(), pieces);
+ boolean statusUpdated = purchaseOrderLineService.updatePoLineReceiptStatusWithoutSave(poLine, newStatus);
+ if (!statusUpdated) {
+ logger.info("updatePoLineAndOrderStatuses:: PoLine receipt status is not updated, skipping...");
+ return Future.succeededFuture();
}
-
- logger.info("calculatePoLineReceiptStatus::Pieces were rolled-back to Expected, checking if there is any Received piece in the storage");
- long receivedQty = getPiecesQuantityByPoLineAndStatus(RECEIVED_STATUSES, pieces);
- return receivedQty == 0 ? AWAITING_RECEIPT : PARTIALLY_RECEIVED;
+ return purchaseOrderLineService.saveOrderLine(poLine, requestContext)
+ .compose(v -> updateOrderStatus(poLine, okapiHeaders, requestContext))
+ .onSuccess(v -> logger.info("updatePoLineAndOrderStatuses:: Order '{}' and PoLine '{}' updated successfully", poLine.getId(), poLine.getPurchaseOrderId()))
+ .onFailure(e -> logger.error("Exception occurred while updating Order '{}' and PoLine '{}'", poLine.getId(), poLine.getPurchaseOrderId(), e));
}
- private long getPiecesQuantityByPoLineAndStatus(List receivingStatuses, List pieces) {
- return pieces.stream()
- .filter(piece -> receivingStatuses.contains(piece.getReceivingStatus()))
- .count();
+ private Future updateOrderStatus(PoLine poLine, Map okapiHeaders, RequestContext requestContext) {
+ var messageContent = JsonObject.of(
+ OKAPI_HEADERS, okapiHeaders,
+ EVENT_PAYLOAD, JsonArray.of(JsonObject.of(ORDER_ID, poLine.getPurchaseOrderId()))
+ );
+ HelperUtils.sendEvent(MessageAddress.RECEIVE_ORDER_STATUS_UPDATE, messageContent, requestContext);
+ return Future.succeededFuture();
}
- Future> getPieces(String endpoint, RequestContext requestContext) {
- return new RestClient().get(endpoint, PieceCollection.class, requestContext)
- .map(PieceCollection::getPieces);
- }
}
diff --git a/src/main/java/org/folio/orders/events/utils/EventUtils.java b/src/main/java/org/folio/orders/events/utils/EventUtils.java
new file mode 100644
index 000000000..99dcebce6
--- /dev/null
+++ b/src/main/java/org/folio/orders/events/utils/EventUtils.java
@@ -0,0 +1,19 @@
+package org.folio.orders.events.utils;
+
+import io.vertx.core.json.JsonObject;
+
+public class EventUtils {
+
+ public static final String POL_UPDATE_FIELD = "poLineIdUpdate";
+
+ public static JsonObject createPoLineUpdateEvent(String poLineId) {
+ return JsonObject.of(POL_UPDATE_FIELD, poLineId);
+ }
+
+ public static String getPoLineId(JsonObject eventPayload) {
+ return eventPayload.getString(POL_UPDATE_FIELD);
+ }
+
+ private EventUtils() {}
+
+}
diff --git a/src/main/java/org/folio/orders/utils/FutureUtils.java b/src/main/java/org/folio/orders/utils/FutureUtils.java
new file mode 100644
index 000000000..2abbf9e62
--- /dev/null
+++ b/src/main/java/org/folio/orders/utils/FutureUtils.java
@@ -0,0 +1,26 @@
+package org.folio.orders.utils;
+
+import io.vertx.core.Future;
+
+import java.util.concurrent.Callable;
+
+public class FutureUtils {
+
+ public static Future asFuture(Runnable runnable) {
+ return asFuture(() -> {
+ runnable.run();
+ return null;
+ });
+ }
+
+ public static Future asFuture(Callable callable) {
+ try {
+ return Future.succeededFuture(callable.call());
+ } catch (Exception e) {
+ return Future.failedFuture(e);
+ }
+ }
+
+ private FutureUtils() {}
+
+}
diff --git a/src/main/java/org/folio/rest/impl/PiecesAPI.java b/src/main/java/org/folio/rest/impl/PiecesAPI.java
index 550fd6176..d79527686 100644
--- a/src/main/java/org/folio/rest/impl/PiecesAPI.java
+++ b/src/main/java/org/folio/rest/impl/PiecesAPI.java
@@ -13,7 +13,9 @@
import org.folio.rest.annotations.Validate;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.jaxrs.model.Piece;
+import org.folio.rest.jaxrs.model.PieceBatchStatusCollection;
import org.folio.rest.jaxrs.resource.OrdersPieces;
+import org.folio.rest.jaxrs.resource.OrdersPiecesBatch;
import org.folio.rest.jaxrs.resource.OrdersPiecesRequests;
import org.folio.service.CirculationRequestsRetriever;
import org.folio.service.pieces.PieceStorageService;
@@ -29,7 +31,7 @@
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
-public class PiecesAPI extends BaseApi implements OrdersPieces, OrdersPiecesRequests {
+public class PiecesAPI extends BaseApi implements OrdersPieces, OrdersPiecesRequests, OrdersPiecesBatch {
private static final Logger logger = LogManager.getLogger();
@Autowired
@@ -62,9 +64,8 @@ public void postOrdersPieces(boolean createItem, Piece entity, Map> asyncResultHandler, Context vertxContext) {
pieceCreateFlowManager.createPiece(entity, createItem, new RequestContext(vertxContext, okapiHeaders))
.onSuccess(piece -> {
- if (logger.isInfoEnabled()) {
- logger.debug("Successfully created piece: {}", JsonObject.mapFrom(piece)
- .encodePrettily());
+ if (logger.isDebugEnabled()) {
+ logger.debug("Successfully created piece: {}", JsonObject.mapFrom(piece).encodePrettily());
}
asyncResultHandler.handle(succeededFuture(buildCreatedResponse(piece)));
})
@@ -104,10 +105,17 @@ public void deleteOrdersPiecesById(String pieceId, boolean deleteHolding, Map pieceIds, String status, Map okapiHeaders,
- Handler> asyncResultHandler, Context vertxContext) {
+ Handler> asyncResultHandler, Context vertxContext) {
circulationRequestsRetriever.getRequesterIdsToRequestsByPieceIds(pieceIds, status, new RequestContext(vertxContext, okapiHeaders))
.onSuccess(requests -> asyncResultHandler.handle(succeededFuture(buildOkResponse(requests))))
.onFailure(fail -> handleErrorResponse(asyncResultHandler, fail));
}
+ @Override
+ public void putOrdersPiecesBatchStatus(PieceBatchStatusCollection pieceBatchStatusCollection, Map okapiHeaders,
+ Handler> asyncResultHandler, Context vertxContext) {
+ pieceUpdateFlowManager.updatePiecesStatuses(pieceBatchStatusCollection.getPieceIds(), pieceBatchStatusCollection.getReceivingStatus(), new RequestContext(vertxContext, okapiHeaders))
+ .onSuccess(v -> asyncResultHandler.handle(succeededFuture(buildNoContentResponse())))
+ .onFailure(t -> handleErrorResponse(asyncResultHandler, t));
+ }
}
diff --git a/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderPieceService.java b/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderPieceService.java
index fc90fd041..d5cf2777d 100644
--- a/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderPieceService.java
+++ b/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderPieceService.java
@@ -1,17 +1,17 @@
package org.folio.service.orders.flows.update.open;
+import static org.folio.orders.events.utils.EventUtils.createPoLineUpdateEvent;
import static org.folio.orders.utils.HelperUtils.calculateInventoryItemsQuantity;
import static org.folio.orders.utils.HelperUtils.collectResultsOnSuccess;
import static org.folio.orders.utils.RequestContextUtil.createContextWithNewTenantId;
+import static org.folio.service.pieces.PieceUtil.updatePieceStatus;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletionException;
import io.vertx.core.Future;
-import io.vertx.core.json.JsonObject;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -124,18 +124,12 @@ public Future updatePiece(Piece piece, RequestContext requestContext) {
.compose(v -> inventoryItemManager.updateItemWithPieceFields(piece, requestContext))
.compose(vVoid -> pieceStorageService.getPieceById(piece.getId(), requestContext))
.compose(pieceStorage -> {
- var receivingStatusUpdate = piece.getReceivingStatus();
- var receivingStatusStorage = pieceStorage.getReceivingStatus();
- boolean isReceivingStatusChanged = !receivingStatusStorage.equals(receivingStatusUpdate);
- if (isReceivingStatusChanged) {
- piece.setStatusUpdatedDate(new Date());
- }
+ var isReceivingStatusChanged = updatePieceStatus(piece, pieceStorage.getReceivingStatus(), piece.getReceivingStatus());
return pieceStorageService.updatePiece(piece, requestContext)
.compose(v -> {
- logger.debug("updatePiece:: receivingStatusStorage - {}, receivingStatusUpdate - {}", receivingStatusStorage, receivingStatusUpdate);
+ logger.debug("updatePiece:: Status updated from: {} to {}", pieceStorage.getReceivingStatus(), piece.getReceivingStatus());
if (isReceivingStatusChanged) {
- var messageToEventBus = JsonObject.of("poLineIdUpdate", piece.getPoLineId());
- receiptStatusPublisher.sendEvent(MessageAddress.RECEIPT_STATUS, messageToEventBus, requestContext);
+ receiptStatusPublisher.sendEvent(MessageAddress.RECEIPT_STATUS, createPoLineUpdateEvent(piece.getPoLineId()), requestContext);
}
return Future.succeededFuture();
})
diff --git a/src/main/java/org/folio/service/orders/utils/StatusUtils.java b/src/main/java/org/folio/service/orders/utils/StatusUtils.java
index d598778b3..cd7b57241 100644
--- a/src/main/java/org/folio/service/orders/utils/StatusUtils.java
+++ b/src/main/java/org/folio/service/orders/utils/StatusUtils.java
@@ -1,19 +1,29 @@
package org.folio.service.orders.utils;
+import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.folio.rest.jaxrs.model.CloseReason;
import org.folio.rest.jaxrs.model.CompositePoLine;
+import org.folio.rest.jaxrs.model.Piece;
import org.folio.rest.jaxrs.model.PoLine;
import org.folio.rest.jaxrs.model.PoLine.PaymentStatus;
import org.folio.rest.jaxrs.model.PoLine.ReceiptStatus;
import org.folio.rest.jaxrs.model.PurchaseOrder;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
+import static org.folio.helper.CheckinReceivePiecesHelper.EXPECTED_STATUSES;
+import static org.folio.helper.CheckinReceivePiecesHelper.RECEIVED_STATUSES;
import static org.folio.orders.utils.HelperUtils.REASON_CANCELLED;
import static org.folio.orders.utils.HelperUtils.REASON_COMPLETE;
+import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.AWAITING_RECEIPT;
+import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.FULLY_RECEIVED;
+import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.PARTIALLY_RECEIVED;
+@Log4j2
public class StatusUtils {
private static final Set resolutionPaymentStatus = Set.of(PaymentStatus.CANCELLED.value(), PaymentStatus.PAYMENT_NOT_REQUIRED.value(), PaymentStatus.FULLY_PAID.value());
@@ -106,6 +116,36 @@ private static boolean closeOrder(PurchaseOrder purchaseOrder, String reason) {
return true;
}
+ public static PoLine.ReceiptStatus calculatePoLineReceiptStatus(String poLineId, List piecesFromStorage) {
+ return calculatePoLineReceiptStatus(poLineId, piecesFromStorage, List.of());
+ }
+
+ public static PoLine.ReceiptStatus calculatePoLineReceiptStatus(String poLineId, List piecesFromStorage, List piecesToUpdate) {
+ // 1. Get map of all persistent piece statuses
+ var pieceStatues = piecesFromStorage.stream().collect(Collectors.toMap(Piece::getId, Piece::getReceivingStatus));
+ // 2. Update new piece statuses (if any)
+ piecesToUpdate.forEach(piece -> pieceStatues.put(piece.getId(), piece.getReceivingStatus()));
+ // 3. Calculate receipt status
+ return calculatePoLineReceiptStatus(poLineId, pieceStatues);
+ }
+
+ private static PoLine.ReceiptStatus calculatePoLineReceiptStatus(String poLineId, Map pieceStatuses) {
+ // Count received and expected statuses
+ long receivedQuantity = pieceStatuses.values().stream().filter(RECEIVED_STATUSES::contains).count();
+ long expectedQuantity = pieceStatuses.values().stream().filter(EXPECTED_STATUSES::contains).count();
+
+ if (expectedQuantity == 0) {
+ log.info("calculatePoLineReceiptStatus:: PoLine with id: '{}', status: Fully Received", poLineId);
+ return FULLY_RECEIVED;
+ }
+ if (receivedQuantity > 0) {
+ log.info("calculatePoLineReceiptStatus:: PoLine with id: '{}', status: Partially Received. Successfully Received pieces: {}", poLineId, receivedQuantity);
+ return PARTIALLY_RECEIVED;
+ }
+ log.info("calculatePoLineReceiptStatus:: PoLine with id: '{}', status: Awaiting Receipt. Pieces were rolled-back to Expected", poLineId);
+ return AWAITING_RECEIPT;
+ }
+
private StatusUtils() {}
}
diff --git a/src/main/java/org/folio/service/pieces/PieceService.java b/src/main/java/org/folio/service/pieces/PieceService.java
index e0046db49..3203ca8f7 100644
--- a/src/main/java/org/folio/service/pieces/PieceService.java
+++ b/src/main/java/org/folio/service/pieces/PieceService.java
@@ -1,14 +1,13 @@
package org.folio.service.pieces;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
+import lombok.extern.log4j.Log4j2;
import org.folio.orders.events.handlers.MessageAddress;
import org.folio.rest.core.models.RequestContext;
-import io.vertx.core.json.JsonObject;
+import static org.folio.orders.events.utils.EventUtils.createPoLineUpdateEvent;
+@Log4j2
public class PieceService {
- private static final Logger logger = LogManager.getLogger(PieceService.class);
private final PieceChangeReceiptStatusPublisher receiptStatusPublisher;
@@ -16,11 +15,10 @@ public PieceService(PieceChangeReceiptStatusPublisher receiptStatusPublisher) {
this.receiptStatusPublisher = receiptStatusPublisher;
}
- public void receiptConsistencyPiecePoLine(JsonObject jsonObj, RequestContext requestContext) {
- logger.debug("Sending event to verify receipt status");
-
- receiptStatusPublisher.sendEvent(MessageAddress.RECEIPT_STATUS, jsonObj, requestContext);
-
- logger.debug("Event to verify receipt status - sent");
+ public void receiptConsistencyPiecePoLine(String poLineId, RequestContext requestContext) {
+ log.debug("Sending event to verify receipt status for poLineId: {}", poLineId);
+ receiptStatusPublisher.sendEvent(MessageAddress.RECEIPT_STATUS, createPoLineUpdateEvent(poLineId), requestContext);
+ log.debug("Event to verify receipt status is sent for poLineId: {}", poLineId);
}
+
}
diff --git a/src/main/java/org/folio/service/pieces/PieceUtil.java b/src/main/java/org/folio/service/pieces/PieceUtil.java
index d0f02ff19..e97d67103 100644
--- a/src/main/java/org/folio/service/pieces/PieceUtil.java
+++ b/src/main/java/org/folio/service/pieces/PieceUtil.java
@@ -2,6 +2,7 @@
import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.OTHER;
+import java.util.Date;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
@@ -68,7 +69,17 @@ private static EnumMap calculateElectronicPiecesQuantityW
}
private static boolean isLocationMatch(Piece piece, Location loc) {
- return (Objects.nonNull(piece.getLocationId()) && piece.getLocationId().equals(loc.getLocationId())) ||
- (Objects.nonNull(piece.getHoldingId()) && piece.getHoldingId().equals(loc.getHoldingId()));
+ return (Objects.nonNull(piece.getLocationId()) && piece.getLocationId().equals(loc.getLocationId()))
+ || (Objects.nonNull(piece.getHoldingId()) && piece.getHoldingId().equals(loc.getHoldingId()));
}
+
+ public static boolean updatePieceStatus(Piece piece, Piece.ReceivingStatus oldStatus, Piece.ReceivingStatus newStatus) {
+ var isStatusChanged = !oldStatus.equals(newStatus);
+ if (isStatusChanged) {
+ piece.setStatusUpdatedDate(new Date());
+ }
+ piece.setReceivingStatus(newStatus);
+ return isStatusChanged;
+ }
+
}
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 84da4a84c..3c28b8831 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
@@ -1,28 +1,28 @@
package org.folio.service.pieces.flows.update;
-import static org.folio.helper.CheckinReceivePiecesHelper.EXPECTED_STATUSES;
-import static org.folio.helper.CheckinReceivePiecesHelper.RECEIVED_STATUSES;
+import static org.folio.orders.utils.FutureUtils.asFuture;
import static org.folio.orders.utils.ProtectedOperationType.UPDATE;
-import static org.folio.rest.jaxrs.model.CompositePoLine.ReceiptStatus.AWAITING_RECEIPT;
-import static org.folio.rest.jaxrs.model.CompositePoLine.ReceiptStatus.FULLY_RECEIVED;
-import static org.folio.rest.jaxrs.model.CompositePoLine.ReceiptStatus.PARTIALLY_RECEIVED;
+import static org.folio.service.orders.utils.StatusUtils.calculatePoLineReceiptStatus;
+import static org.folio.service.pieces.PieceUtil.updatePieceStatus;
-import java.util.Date;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
+import java.util.stream.Collectors;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
+import lombok.extern.log4j.Log4j2;
+import org.folio.models.pieces.BasePieceFlowHolder;
+import org.folio.models.pieces.PieceBatchStatusUpdateHolder;
import org.folio.models.pieces.PieceUpdateHolder;
+import org.folio.okapi.common.GenericCompositeFuture;
+import org.folio.orders.utils.HelperUtils;
import org.folio.orders.utils.PoLineCommonUtil;
+import org.folio.orders.utils.ProtectedOperationType;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.jaxrs.model.CompositePoLine;
-import org.folio.rest.jaxrs.model.CompositePurchaseOrder;
import org.folio.rest.jaxrs.model.CompositePurchaseOrder.OrderType;
import org.folio.rest.jaxrs.model.CompositePurchaseOrder.WorkflowStatus;
import org.folio.rest.jaxrs.model.Location;
import org.folio.rest.jaxrs.model.Piece;
+import org.folio.rest.jaxrs.model.PieceBatchStatusCollection;
import org.folio.service.ProtectionService;
import org.folio.service.orders.PurchaseOrderLineService;
import org.folio.service.pieces.PieceService;
@@ -32,13 +32,14 @@
import org.folio.service.pieces.flows.DefaultPieceFlowsValidator;
import io.vertx.core.Future;
-import io.vertx.core.json.JsonObject;
+import org.folio.service.titles.TitlesService;
+@Log4j2
public class PieceUpdateFlowManager {
- private static final Logger logger = LogManager.getLogger(PieceUpdateFlowManager.class);
private final PieceStorageService pieceStorageService;
private final PieceService pieceService;
+ private final TitlesService titlesService;
private final ProtectionService protectionService;
private final PieceUpdateFlowPoLineService updatePoLineService;
private final PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager;
@@ -46,12 +47,13 @@ public class PieceUpdateFlowManager {
private final DefaultPieceFlowsValidator defaultPieceFlowsValidator;
private final PurchaseOrderLineService purchaseOrderLineService;
- public PieceUpdateFlowManager(PieceStorageService pieceStorageService, PieceService pieceService, ProtectionService protectionService,
- PieceUpdateFlowPoLineService updatePoLineService, PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager,
- BasePieceFlowHolderBuilder basePieceFlowHolderBuilder, DefaultPieceFlowsValidator defaultPieceFlowsValidator,
- PurchaseOrderLineService purchaseOrderLineService) {
+ public PieceUpdateFlowManager(PieceStorageService pieceStorageService, PieceService pieceService, TitlesService titlesService, ProtectionService protectionService,
+ PieceUpdateFlowPoLineService updatePoLineService, PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager,
+ BasePieceFlowHolderBuilder basePieceFlowHolderBuilder, DefaultPieceFlowsValidator defaultPieceFlowsValidator,
+ PurchaseOrderLineService purchaseOrderLineService) {
this.pieceStorageService = pieceStorageService;
this.pieceService = pieceService;
+ this.titlesService = titlesService;
this.protectionService = protectionService;
this.updatePoLineService = updatePoLineService;
this.pieceUpdateFlowInventoryManager = pieceUpdateFlowInventoryManager;
@@ -74,85 +76,97 @@ public Future updatePiece(Piece pieceToUpdate, boolean createItem, boolean
.map(holder::withPieceFromStorage)
.compose(aHolder -> basePieceFlowHolderBuilder.updateHolderWithOrderInformation(holder, requestContext))
.compose(aHolder -> basePieceFlowHolderBuilder.updateHolderWithTitleInformation(holder, requestContext))
- .map(v -> {
- defaultPieceFlowsValidator.isPieceRequestValid(pieceToUpdate, holder.getOriginPoLine(), createItem);
- return null;
- })
+ .compose(v -> asFuture(() -> defaultPieceFlowsValidator.isPieceRequestValid(pieceToUpdate, holder.getOriginPoLine(), createItem)))
.compose(title -> protectionService.isOperationRestricted(holder.getTitle().getAcqUnitIds(), UPDATE, requestContext))
.compose(v -> pieceUpdateFlowInventoryManager.processInventory(holder, requestContext))
.compose(v -> updatePoLine(holder, requestContext))
- .map(v -> {
- Piece.ReceivingStatus receivingStatusStorage = holder.getPieceFromStorage().getReceivingStatus();
- Piece.ReceivingStatus receivingStatusUpdate = holder.getPieceToUpdate().getReceivingStatus();
- logger.debug("receivingStatusStorage -- {}", receivingStatusStorage);
- logger.debug("receivingStatusUpdate -- {}", receivingStatusUpdate);
- if (receivingStatusStorage.compareTo(receivingStatusUpdate) != 0) {
- holder.getPieceToUpdate().setStatusUpdatedDate(new Date());
- return true;
- }
- return false;
- })
- .compose(verifyReceiptStatus -> pieceStorageService.updatePiece(holder.getPieceToUpdate(), requestContext)
- .map(verifyReceiptStatus))
- .map(verifyReceiptStatus -> {
+ .map(v -> updatePieceStatus(holder.getPieceToUpdate(), holder.getPieceFromStorage().getReceivingStatus(), holder.getPieceToUpdate().getReceivingStatus()))
+ .compose(verifyReceiptStatus -> pieceStorageService.updatePiece(holder.getPieceToUpdate(), requestContext).map(verifyReceiptStatus))
+ .compose(verifyReceiptStatus -> asFuture(() -> {
if (Boolean.TRUE.equals(verifyReceiptStatus)) {
- JsonObject messageToEventBus = new JsonObject();
- messageToEventBus.put("poLineIdUpdate", holder.getPieceToUpdate().getPoLineId());
- pieceService.receiptConsistencyPiecePoLine(messageToEventBus, requestContext);
+ pieceService.receiptConsistencyPiecePoLine(holder.getPieceToUpdate().getPoLineId(), requestContext);
}
- return null;
- })
- .onFailure(t -> logger.error("User to update piece with id={}", holder.getPieceToUpdate().getId(), t))
+ }))
+ .onFailure(t -> log.error("User to update piece with id={}", holder.getPieceToUpdate().getId(), t))
+ .mapEmpty();
+ }
+
+ public Future updatePiecesStatuses(List pieceIds, PieceBatchStatusCollection.ReceivingStatus receivingStatus, RequestContext requestContext) {
+ var newStatus = Piece.ReceivingStatus.fromValue(receivingStatus.value());
+ return pieceStorageService.getPiecesByIds(pieceIds, requestContext)
+ .compose(pieces -> isOperationRestricted(pieces, requestContext))
+ .map(pieces -> pieces.stream().collect(Collectors.groupingBy(Piece::getPoLineId)))
+ .map(piecesByPoLineId -> piecesByPoLineId.entrySet().stream()
+ .map(entry -> new PieceBatchStatusUpdateHolder(newStatus, entry.getValue(), entry.getKey()))
+ .map(holder -> basePieceFlowHolderBuilder.updateHolderWithOrderInformation(holder, requestContext)
+ .compose(v -> updatePoLine(holder, requestContext))
+ .compose(v -> updatePiecesStatusesByPoLine(holder, requestContext)))
+ .toList())
+ .compose(HelperUtils::collectResultsOnSuccess)
+ .onSuccess(v -> log.info("Pieces statuses are updated for pieceIds: {} to status: {}", pieceIds, receivingStatus))
+ .onFailure(t -> log.error("Failed to update pieces statuses for pieceIds: {} to status: {}", pieceIds, receivingStatus, t))
.mapEmpty();
}
protected Future updatePoLine(PieceUpdateHolder holder, RequestContext requestContext) {
- CompositePoLine originPoLine = holder.getOriginPoLine();
+ return updatePoLine(holder, List.of(holder.getPieceToUpdate()), requestContext)
+ .compose(v -> !Boolean.TRUE.equals(holder.getOriginPoLine().getIsPackage()) && !Boolean.TRUE.equals(holder.getOriginPoLine().getCheckinItems())
+ ? updatePoLineService.updatePoLine(holder, requestContext)
+ : Future.succeededFuture());
+ }
+ protected Future updatePoLine(PieceBatchStatusUpdateHolder holder, RequestContext requestContext) {
+ return updatePoLine(holder, holder.getPieces(), requestContext);
+ }
+
+ private Future updatePoLine(T holder, List piecesToUpdate, RequestContext requestContext) {
+ var originPurchaseOrder = holder.getOriginPurchaseOrder();
+ if (originPurchaseOrder.getOrderType() != OrderType.ONE_TIME || originPurchaseOrder.getWorkflowStatus() != WorkflowStatus.OPEN) {
+ return Future.succeededFuture();
+ }
+
+ var originPoLine = holder.getOriginPoLine();
+ var poLineToSave = holder.getPoLineToSave();
+ var pieceIds = piecesToUpdate.stream().map(Piece::getId).toList();
return pieceStorageService.getPiecesByLineId(originPoLine.getId(), requestContext)
.compose(pieces -> {
- CompositePurchaseOrder order = holder.getOriginPurchaseOrder();
- if (order.getOrderType() != OrderType.ONE_TIME || order.getWorkflowStatus() != WorkflowStatus.OPEN) {
- return Future.succeededFuture();
- }
- List piecesToUpdate = List.of(holder.getPieceToUpdate());
- CompositePoLine poLineToSave = holder.getPoLineToSave();
if (PoLineCommonUtil.isCancelledOrOngoingStatus(PoLineCommonUtil.convertToPoLine(poLineToSave))) {
- logger.info("updatePoLine:: Skipping updating POL '{}' status for CANCELLED or ONGOING po lines", poLineToSave.getId());
+ log.info("updatePoLine:: Skip updating PoLine: '{}' with status: '{}'", poLineToSave.getId(), poLineToSave.getReceiptStatus());
} else {
- poLineToSave.setReceiptStatus(calculatePoLineReceiptStatus(originPoLine, pieces, piecesToUpdate));
+ var newStatus = calculatePoLineReceiptStatus(poLineToSave.getId(), pieces, piecesToUpdate);
+ poLineToSave.setReceiptStatus(CompositePoLine.ReceiptStatus.fromValue(newStatus.value()));
}
- List locations = PieceUtil.findOrderPieceLineLocation(holder.getPieceToUpdate(), poLineToSave);
+ var locations = getPieceLocations(piecesToUpdate, poLineToSave);
return purchaseOrderLineService.saveOrderLine(poLineToSave, locations, requestContext);
- }).compose(v -> {
- if (!Boolean.TRUE.equals(originPoLine.getIsPackage()) &&
- !Boolean.TRUE.equals(originPoLine.getCheckinItems())) {
- return updatePoLineService.updatePoLine(holder, requestContext);
- }
- return Future.succeededFuture();
})
- .onSuccess(aVoid -> logger.info("updatePoLine:: Po line with id: {} is updated for pieceId: {}",
- originPoLine.getId(), holder.getPieceToUpdate().getId()))
- .onFailure(t -> logger.error("Failed to update PO line with id: {} for pieceId: {}",
- originPoLine.getId(), holder.getPieceToUpdate().getId(), t));
+ .onSuccess(v -> log.info("updatePoLine:: PoLine with id: '{}' is updated for pieceIds: {}", originPoLine.getId(), pieceIds))
+ .onFailure(t -> log.error("Failed to update PO line with id: '{}' for pieceIds: {}", originPoLine.getId(), pieceIds, t));
}
- CompositePoLine.ReceiptStatus calculatePoLineReceiptStatus(CompositePoLine poLine, List fromStorage, List toUpdate) {
-
- // 1. collect all piece statuses
- Map map = new HashMap<>();
- fromStorage.forEach(piece -> map.put(piece.getId(), piece.getReceivingStatus()));
- toUpdate.forEach(piece -> map.put(piece.getId(), piece.getReceivingStatus()));
-
- // 2. count received and expected statuses
- long receivedQuantity = map.values().stream().filter(RECEIVED_STATUSES::contains).count();
- long expectedQuantity = map.values().stream().filter(EXPECTED_STATUSES::contains).count();
+ private Future updatePiecesStatusesByPoLine(PieceBatchStatusUpdateHolder holder, RequestContext requestContext) {
+ var isAnyPiecesUpdated = holder.getPieces().stream().anyMatch(piece -> updatePieceStatus(piece, piece.getReceivingStatus(), holder.getReceivingStatus()));
+ if (!isAnyPiecesUpdated) {
+ return Future.succeededFuture();
+ }
+ var updates = holder.getPieces().stream().map(piece -> pieceStorageService.updatePiece(piece, requestContext)).toList();
+ return HelperUtils.collectResultsOnSuccess(updates)
+ .compose(v -> asFuture(() -> pieceService.receiptConsistencyPiecePoLine(holder.getOrderLineId(), requestContext)));
+ }
- logger.info("calculatePoLineReceiptStatus:: POL: {}, received: {}, expected: {}",
- poLine.getId(), receivedQuantity, expectedQuantity);
+ private List getPieceLocations(List pieces, CompositePoLine poLine) {
+ return pieces.stream()
+ .flatMap(pieceToUpdate -> PieceUtil.findOrderPieceLineLocation(pieceToUpdate, poLine).stream())
+ .toList();
+ }
- return expectedQuantity == 0 ? FULLY_RECEIVED :
- receivedQuantity > 0 ? PARTIALLY_RECEIVED : AWAITING_RECEIPT;
+ protected Future> isOperationRestricted(List pieces, RequestContext requestContext) {
+ var pieceIds = pieces.stream().map(Piece::getId).toList();
+ return titlesService.getTitlesByPieceIds(pieceIds, requestContext)
+ .map(titles -> titles.stream()
+ .map(title -> protectionService.isOperationRestricted(title.getAcqUnitIds(), ProtectedOperationType.UPDATE, requestContext))
+ .toList())
+ .map(GenericCompositeFuture::all)
+ .map(pieces);
}
}
diff --git a/src/test/java/org/folio/orders/events/handlers/HandlersTestHelper.java b/src/test/java/org/folio/orders/events/handlers/HandlersTestHelper.java
index 50744e2b0..d331d60ad 100644
--- a/src/test/java/org/folio/orders/events/handlers/HandlersTestHelper.java
+++ b/src/test/java/org/folio/orders/events/handlers/HandlersTestHelper.java
@@ -4,6 +4,7 @@
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.awaitility.Awaitility.await;
import static org.folio.helper.BaseHelper.EVENT_PAYLOAD;
+import static org.folio.orders.events.utils.EventUtils.POL_UPDATE_FIELD;
import static org.folio.rest.impl.EventBusContextConfiguration.eventMessages;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -71,7 +72,7 @@ public static void verifyReceiptStatusUpdateEvent(int msgQty) {
assertThat(message.headers(), not(emptyIterable()));
assertThat(message.body(), notNullValue());
assertThat(message.body()
- .getString("poLineIdUpdate"), not(is(emptyOrNullString())));
+ .getString(POL_UPDATE_FIELD), not(is(emptyOrNullString())));
assertThat(message.body()
.getString(HelperUtils.LANG), not(is(emptyOrNullString())));
}
diff --git a/src/test/java/org/folio/orders/events/handlers/ReceiptStatusConsistencyTest.java b/src/test/java/org/folio/orders/events/handlers/ReceiptStatusConsistencyTest.java
index 109bc1433..aa2ac5a03 100644
--- a/src/test/java/org/folio/orders/events/handlers/ReceiptStatusConsistencyTest.java
+++ b/src/test/java/org/folio/orders/events/handlers/ReceiptStatusConsistencyTest.java
@@ -5,6 +5,7 @@
import static org.folio.TestConfig.isVerticleNotDeployed;
import static org.folio.TestUtils.checkVertxContextCompletion;
import static org.folio.TestUtils.getMockAsJson;
+import static org.folio.orders.events.utils.EventUtils.POL_UPDATE_FIELD;
import static org.folio.rest.impl.MockServer.POLINES_COLLECTION;
import static org.folio.rest.impl.MockServer.PO_LINES_MOCK_DATA_PATH;
import static org.folio.rest.impl.MockServer.getPieceSearches;
@@ -33,6 +34,7 @@
import org.folio.rest.impl.MockServer;
import org.folio.rest.jaxrs.model.CompositePoLine;
import org.folio.service.orders.PurchaseOrderLineService;
+import org.folio.service.pieces.PieceStorageService;
import org.folio.spring.SpringContextUtil;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
@@ -67,6 +69,8 @@ public class ReceiptStatusConsistencyTest {
@Autowired
private PurchaseOrderLineService purchaseOrderLineService;
+ @Autowired
+ private PieceStorageService pieceStorageService;
@BeforeAll
static void before() throws InterruptedException, ExecutionException, TimeoutException {
@@ -82,7 +86,7 @@ static void before() throws InterruptedException, ExecutionException, TimeoutExc
@BeforeEach
void setUp() {
SpringContextUtil.autowireDependencies(this, vertx.getOrCreateContext());
- vertx.eventBus().consumer(TEST_ADDRESS, new ReceiptStatusConsistency(vertx, purchaseOrderLineService));
+ vertx.eventBus().consumer(TEST_ADDRESS, new ReceiptStatusConsistency(vertx, pieceStorageService, purchaseOrderLineService));
}
@AfterEach
@@ -224,7 +228,7 @@ void testPieceReceiptStatusFailureWhenNoMatchingPoLineForPiece(VertxTestContext
private JsonObject createBody(String poLineId) {
JsonObject jsonObj = new JsonObject();
- jsonObj.put("poLineIdUpdate", poLineId);
+ jsonObj.put(POL_UPDATE_FIELD, poLineId);
return jsonObj;
}
diff --git a/src/test/java/org/folio/orders/utils/StatusUtilsTest.java b/src/test/java/org/folio/orders/utils/StatusUtilsTest.java
index 6cba2a5d4..540b8ff8f 100644
--- a/src/test/java/org/folio/orders/utils/StatusUtilsTest.java
+++ b/src/test/java/org/folio/orders/utils/StatusUtilsTest.java
@@ -2,19 +2,26 @@
import org.folio.CopilotGenerated;
import org.folio.rest.jaxrs.model.CompositePoLine;
+import org.folio.rest.jaxrs.model.Piece;
import org.folio.rest.jaxrs.model.PoLine;
import org.folio.rest.jaxrs.model.PurchaseOrder;
import org.folio.service.orders.utils.StatusUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import java.util.Arrays;
import java.util.List;
+import java.util.UUID;
+import static org.folio.rest.jaxrs.model.Piece.ReceivingStatus.EXPECTED;
+import static org.folio.rest.jaxrs.model.Piece.ReceivingStatus.RECEIVED;
+import static org.folio.rest.jaxrs.model.Piece.ReceivingStatus.UNRECEIVABLE;
+import static org.folio.service.orders.utils.StatusUtils.calculatePoLineReceiptStatus;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-@CopilotGenerated
+@CopilotGenerated(partiallyGenerated = true)
public class StatusUtilsTest {
private PurchaseOrder purchaseOrder;
@@ -115,4 +122,67 @@ void changeOrderStatusForPoLineUpdate_shouldNotReopenOrder_whenAllPoLinesComplet
assertFalse(StatusUtils.changeOrderStatusForPoLineUpdate(purchaseOrder, List.of(poLine1)));
assertEquals(PurchaseOrder.WorkflowStatus.CLOSED, purchaseOrder.getWorkflowStatus());
}
+
+ @Test
+ void testCalculatePoLineReceiptStatusWhenReceiveLast() {
+ // given
+ String poLineId = UUID.randomUUID().toString();
+ List fromStorage = givenPieces(EXPECTED, RECEIVED, UNRECEIVABLE);
+ List update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(RECEIVED));
+
+ // when
+ var receiptStatus = calculatePoLineReceiptStatus(poLineId, fromStorage, update);
+
+ // then
+ assertEquals(PoLine.ReceiptStatus.FULLY_RECEIVED, receiptStatus);
+ }
+
+ @Test
+ void testCalculatePoLineReceiptStatusWhenExpectLast() {
+ // given
+ String poLineId = UUID.randomUUID().toString();
+ List fromStorage = givenPieces(RECEIVED, RECEIVED, UNRECEIVABLE);
+ List update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(EXPECTED));
+
+ // when
+ var receiptStatus = calculatePoLineReceiptStatus(poLineId, fromStorage, update);
+
+ // then
+ assertEquals(PoLine.ReceiptStatus.PARTIALLY_RECEIVED, receiptStatus);
+ }
+
+ @Test
+ void testCalculatePoLineReceiptStatusWhenExpectAll() {
+ // given
+ String poLineId = UUID.randomUUID().toString();
+ List fromStorage = givenPieces(RECEIVED);
+ List update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(EXPECTED));
+
+ // when
+ var receiptStatus = calculatePoLineReceiptStatus(poLineId, fromStorage, update);
+
+ // then
+ assertEquals(PoLine.ReceiptStatus.AWAITING_RECEIPT, receiptStatus);
+ }
+
+ @Test
+ void testCalculatePoLineReceiptStatusWhenReceivePart() {
+ // given
+ String poLineId = UUID.randomUUID().toString();
+ List fromStorage = givenPieces(EXPECTED, EXPECTED);
+ List update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(UNRECEIVABLE));
+
+ // when
+ var receiptStatus = calculatePoLineReceiptStatus(poLineId, fromStorage, update);
+
+ // then
+ assertEquals(PoLine.ReceiptStatus.PARTIALLY_RECEIVED, receiptStatus);
+ }
+
+ private static List givenPieces(Piece.ReceivingStatus... statuses) {
+ return Arrays.stream(statuses)
+ .map(status -> new Piece().withId(UUID.randomUUID().toString()).withReceivingStatus(status))
+ .toList();
+ }
+
}
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 1b3cae601..1fbb37eeb 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
@@ -10,14 +10,12 @@
import static org.folio.TestConfig.isVerticleNotDeployed;
import static org.folio.TestConstants.ID;
import static org.folio.rest.jaxrs.model.Eresource.CreateInventory.INSTANCE_HOLDING_ITEM;
-import static org.folio.rest.jaxrs.model.Piece.ReceivingStatus.EXPECTED;
-import static org.folio.rest.jaxrs.model.Piece.ReceivingStatus.RECEIVED;
-import static org.folio.rest.jaxrs.model.Piece.ReceivingStatus.UNRECEIVABLE;
import static org.folio.service.inventory.InventoryHoldingManager.HOLDING_PERMANENT_LOCATION_ID;
import static org.folio.service.inventory.InventoryItemManager.ITEM_STATUS;
import static org.folio.service.inventory.InventoryItemManager.ITEM_STATUS_NAME;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -27,7 +25,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -46,17 +43,20 @@
import org.folio.rest.jaxrs.model.Eresource;
import org.folio.rest.jaxrs.model.Location;
import org.folio.rest.jaxrs.model.Piece;
+import org.folio.rest.jaxrs.model.PieceBatchStatusCollection;
import org.folio.rest.jaxrs.model.PoLine;
import org.folio.rest.jaxrs.model.PurchaseOrder;
import org.folio.rest.jaxrs.model.Title;
import org.folio.service.ProtectionService;
import org.folio.service.caches.InventoryCache;
import org.folio.service.orders.PurchaseOrderLineService;
+import org.folio.service.orders.PurchaseOrderStorageService;
import org.folio.service.pieces.PieceService;
import org.folio.service.pieces.PieceStorageService;
import org.folio.service.pieces.PieceUtil;
import org.folio.service.pieces.flows.BasePieceFlowHolderBuilder;
import org.folio.service.pieces.flows.DefaultPieceFlowsValidator;
+import org.folio.service.titles.TitlesService;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
@@ -78,14 +78,26 @@
@ExtendWith(VertxExtension.class)
public class PieceUpdateFlowManagerTest {
- @Autowired PieceUpdateFlowManager pieceUpdateFlowManager;
- @Autowired PieceStorageService pieceStorageService;
- @Autowired ProtectionService protectionService;
- @Autowired PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager;
- @Autowired PieceService pieceService;
- @Autowired BasePieceFlowHolderBuilder basePieceFlowHolderBuilder;
- @Autowired PieceUpdateFlowPoLineService pieceUpdateFlowPoLineService;
- @Autowired PurchaseOrderLineService purchaseOrderLineService;
+ @Autowired
+ PieceUpdateFlowManager pieceUpdateFlowManager;
+ @Autowired
+ PieceStorageService pieceStorageService;
+ @Autowired
+ ProtectionService protectionService;
+ @Autowired
+ PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager;
+ @Autowired
+ PieceService pieceService;
+ @Autowired
+ TitlesService titlesService;
+ @Autowired
+ BasePieceFlowHolderBuilder basePieceFlowHolderBuilder;
+ @Autowired
+ PieceUpdateFlowPoLineService pieceUpdateFlowPoLineService;
+ @Autowired
+ PurchaseOrderStorageService purchaseOrderStorageService;
+ @Autowired
+ PurchaseOrderLineService purchaseOrderLineService;
private final Context ctx = getFirstContextFromVertx(getVertx());
@Mock
@@ -95,7 +107,7 @@ public class PieceUpdateFlowManagerTest {
private static boolean runningOnOwn;
@BeforeEach
- void initMocks(){
+ void initMocks() {
MockitoAnnotations.openMocks(this);
autowireDependencies(this);
requestContext = new RequestContext(ctx, okapiHeadersMock);
@@ -173,7 +185,7 @@ void shouldNotUpdateLineQuantityIfPoLineIsPackageAndShouldRunProcessInventory()
doReturn(succeededFuture(null)).when(protectionService).isOperationRestricted(any(), any(ProtectedOperationType.class), eq(requestContext));
doReturn(succeededFuture(null)).when(pieceUpdateFlowInventoryManager).processInventory(any(PieceUpdateHolder.class), eq(requestContext));
- doNothing().when(pieceService).receiptConsistencyPiecePoLine(any(JsonObject.class), eq(requestContext));
+ doNothing().when(pieceService).receiptConsistencyPiecePoLine(anyString(), eq(requestContext));
doReturn(succeededFuture(null)).when(pieceUpdateFlowPoLineService).updatePoLine(pieceUpdateHolderCapture.capture(), eq(requestContext));
doReturn(succeededFuture(null))
.when(purchaseOrderLineService).saveOrderLine(any(CompositePoLine.class),
@@ -239,7 +251,7 @@ void shouldNotUpdateLineQuantityIfManualPieceCreateTrueAndShouldRunProcessInvent
doReturn(succeededFuture(null)).when(protectionService).isOperationRestricted(any(), any(ProtectedOperationType.class), eq(requestContext));
doReturn(succeededFuture(null)).when(pieceUpdateFlowInventoryManager).processInventory(any(PieceUpdateHolder.class), eq(requestContext));
- doNothing().when(pieceService).receiptConsistencyPiecePoLine(any(JsonObject.class), eq(requestContext));
+ doNothing().when(pieceService).receiptConsistencyPiecePoLine(anyString(), eq(requestContext));
doReturn(succeededFuture(null)).when(pieceUpdateFlowPoLineService).updatePoLine(pieceUpdateHolderCapture.capture(), eq(requestContext));
//When
pieceUpdateFlowManager.updatePiece(pieceToUpdate, true, true, requestContext).result();
@@ -286,7 +298,7 @@ void shouldUpdateLineQuantityIfPoLineIsNotPackageAndHoldingReferenceChangedAndSh
final ArgumentCaptor pieceToUpdateCapture = ArgumentCaptor.forClass(Piece.class);
doReturn(succeededFuture(null)).when(pieceStorageService).updatePiece(pieceToUpdateCapture.capture(), eq(requestContext));
givenPoLineHasPieces(lineId, List.of());
- doNothing().when(pieceService).receiptConsistencyPiecePoLine(any(JsonObject.class), eq(requestContext));
+ doNothing().when(pieceService).receiptConsistencyPiecePoLine(anyString(), eq(requestContext));
final ArgumentCaptor pieceUpdateHolderCapture = ArgumentCaptor.forClass(PieceUpdateHolder.class);
doAnswer((Answer>) invocation -> {
@@ -318,65 +330,29 @@ void shouldUpdateLineQuantityIfPoLineIsNotPackageAndHoldingReferenceChangedAndSh
}
@Test
- void poLineStatusWhenReceiveLast() {
- // given
- CompositePoLine poLine = new CompositePoLine().withId(UUID.randomUUID().toString());
- List fromStorage = givenPieces(EXPECTED, RECEIVED, UNRECEIVABLE);
- List update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(RECEIVED));
-
- // when
- var receiptStatus = pieceUpdateFlowManager.calculatePoLineReceiptStatus(poLine, fromStorage, update);
-
- // then
- assertEquals(CompositePoLine.ReceiptStatus.FULLY_RECEIVED, receiptStatus);
- }
-
- @Test
- void poLineStatusWhenExpectLast() {
- // given
- CompositePoLine poLine = new CompositePoLine().withId(UUID.randomUUID().toString());
- List fromStorage = givenPieces(RECEIVED, RECEIVED, UNRECEIVABLE);
- List update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(EXPECTED));
-
- // when
- var receiptStatus = pieceUpdateFlowManager.calculatePoLineReceiptStatus(poLine, fromStorage, update);
-
- // then
- assertEquals(CompositePoLine.ReceiptStatus.PARTIALLY_RECEIVED, receiptStatus);
- }
-
- @Test
- void poLineStatusWhenExpectAll() {
- // given
- CompositePoLine poLine = new CompositePoLine().withId(UUID.randomUUID().toString());
- List fromStorage = givenPieces(RECEIVED);
- List update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(EXPECTED));
-
- // when
- var receiptStatus = pieceUpdateFlowManager.calculatePoLineReceiptStatus(poLine, fromStorage, update);
-
- // then
- assertEquals(CompositePoLine.ReceiptStatus.AWAITING_RECEIPT, receiptStatus);
- }
-
- @Test
- void poLineStatusWhenReceivePart() {
- // given
- CompositePoLine poLine = new CompositePoLine().withId(UUID.randomUUID().toString());
- List fromStorage = givenPieces(EXPECTED, EXPECTED);
- List update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(UNRECEIVABLE));
-
- // when
- var receiptStatus = pieceUpdateFlowManager.calculatePoLineReceiptStatus(poLine, fromStorage, update);
-
- // then
- assertEquals(CompositePoLine.ReceiptStatus.PARTIALLY_RECEIVED, receiptStatus);
- }
-
- private static List givenPieces(Piece.ReceivingStatus... statuses) {
- return Arrays.stream(statuses).map(status ->
- new Piece().withId(UUID.randomUUID().toString()).withReceivingStatus(status)
- ).toList();
+ void shouldUpdatePiecesStatusesSuccessfully() {
+ List pieceIds = List.of(UUID.randomUUID().toString(), UUID.randomUUID().toString());
+ PieceBatchStatusCollection.ReceivingStatus receivingStatus = PieceBatchStatusCollection.ReceivingStatus.RECEIVED;
+ Title title = new Title().withId(UUID.randomUUID().toString()).withAcqUnitIds(List.of(UUID.randomUUID().toString()));
+ PurchaseOrder purchaseOrder = new PurchaseOrder().withId(UUID.randomUUID().toString());
+ PoLine poLine1 = new PoLine().withId(UUID.randomUUID().toString()).withOrderFormat(PoLine.OrderFormat.P_E_MIX).withPurchaseOrderId(purchaseOrder.getId());
+ PoLine poLine2 = new PoLine().withId(UUID.randomUUID().toString()).withOrderFormat(PoLine.OrderFormat.P_E_MIX).withPurchaseOrderId(purchaseOrder.getId());
+ Piece piece1 = new Piece().withId(pieceIds.get(0)).withPoLineId(poLine1.getId()).withTitleId(title.getId());
+ Piece piece2 = new Piece().withId(pieceIds.get(1)).withPoLineId(poLine2.getId()).withTitleId(title.getId());
+
+ doReturn(succeededFuture(List.of(piece1, piece2))).when(pieceStorageService).getPiecesByIds(pieceIds, requestContext);
+ doReturn(succeededFuture(List.of(title))).when(titlesService).getTitlesByPieceIds(pieceIds, requestContext);
+ doReturn(succeededFuture()).when(protectionService).isOperationRestricted(title.getAcqUnitIds(), ProtectedOperationType.UPDATE, requestContext);
+ doReturn(succeededFuture(poLine1)).when(purchaseOrderLineService).getOrderLineById(poLine1.getId(), requestContext);
+ doReturn(succeededFuture(poLine2)).when(purchaseOrderLineService).getOrderLineById(poLine2.getId(), requestContext);
+ doReturn(succeededFuture(purchaseOrder)).when(purchaseOrderStorageService).getPurchaseOrderById(purchaseOrder.getId(), requestContext);
+ doReturn(succeededFuture()).when(pieceUpdateFlowPoLineService).updatePoLine(any(), eq(requestContext));
+ doReturn(succeededFuture()).when(pieceStorageService).updatePiece(any(), eq(requestContext));
+ doNothing().when(pieceService).receiptConsistencyPiecePoLine(anyString(), eq(requestContext));
+
+ Future result = pieceUpdateFlowManager.updatePiecesStatuses(pieceIds, receivingStatus, requestContext);
+
+ assertTrue(result.succeeded());
}
private void givenPoLineHasPieces(String lineId, List pieces) {
@@ -386,46 +362,74 @@ private void givenPoLineHasPieces(String lineId, List pieces) {
}
private static class ContextConfiguration {
- @Bean PieceStorageService pieceStorageService() {
+ @Bean
+ PieceStorageService pieceStorageService() {
return mock(PieceStorageService.class);
}
- @Bean ProtectionService protectionService() {
+
+ @Bean
+ ProtectionService protectionService() {
return mock(ProtectionService.class);
}
- @Bean PieceService pieceService() {
+
+ @Bean
+ PieceService pieceService() {
return mock(PieceService.class);
}
- @Bean PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager() {
+
+ @Bean
+ TitlesService titlesService() {
+ return mock(TitlesService.class);
+ }
+
+ @Bean
+ PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager() {
return mock(PieceUpdateFlowInventoryManager.class);
}
- @Bean BasePieceFlowHolderBuilder basePieceFlowHolderBuilder() {
- return mock(BasePieceFlowHolderBuilder.class);
+
+ @Bean
+ BasePieceFlowHolderBuilder basePieceFlowHolderBuilder(PurchaseOrderStorageService purchaseOrderStorageService,
+ PurchaseOrderLineService purchaseOrderLineService,
+ TitlesService titlesService) {
+ return spy(new BasePieceFlowHolderBuilder(purchaseOrderStorageService, purchaseOrderLineService, titlesService));
}
- @Bean PieceUpdateFlowPoLineService pieceUpdateFlowPoLineService() {
- return mock(PieceUpdateFlowPoLineService.class);
+
+ @Bean
+ PieceUpdateFlowPoLineService pieceUpdateFlowPoLineService() {
+ return mock(PieceUpdateFlowPoLineService.class);
}
- @Bean DefaultPieceFlowsValidator defaultPieceFlowsValidator() {
+
+ @Bean
+ DefaultPieceFlowsValidator defaultPieceFlowsValidator() {
return spy(new DefaultPieceFlowsValidator());
}
- @Bean RestClient restClient() {
+ @Bean
+ RestClient restClient() {
return mock(RestClient.class);
}
- @Bean InventoryCache inventoryCache() {
+ @Bean
+ InventoryCache inventoryCache() {
return mock(InventoryCache.class);
}
- @Bean PurchaseOrderLineService purchaseOrderLineService() {
+ @Bean
+ PurchaseOrderStorageService purchaseOrderStorageService() {
+ return mock(PurchaseOrderStorageService.class);
+ }
+
+ @Bean
+ PurchaseOrderLineService purchaseOrderLineService() {
return mock(PurchaseOrderLineService.class);
}
@Bean
- PieceUpdateFlowManager pieceUpdateFlowManager(PieceStorageService pieceStorageService, PieceService pieceService,
+ PieceUpdateFlowManager pieceUpdateFlowManager(PieceStorageService pieceStorageService, PieceService pieceService, TitlesService titlesService,
ProtectionService protectionService, PieceUpdateFlowPoLineService pieceUpdateFlowPoLineService,
PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager, BasePieceFlowHolderBuilder basePieceFlowHolderBuilder,
DefaultPieceFlowsValidator defaultPieceFlowsValidator, PurchaseOrderLineService purchaseOrderLineService) {
- return new PieceUpdateFlowManager(pieceStorageService, pieceService, protectionService,
+ return new PieceUpdateFlowManager(pieceStorageService, pieceService, titlesService, protectionService,
pieceUpdateFlowPoLineService, pieceUpdateFlowInventoryManager, basePieceFlowHolderBuilder,
defaultPieceFlowsValidator, purchaseOrderLineService);
}