Skip to content

Commit

Permalink
[MODORDERS-1210] Update piece update flow manager to include batch st…
Browse files Browse the repository at this point in the history
…atus update
  • Loading branch information
Saba-Zedginidze-EPAM committed Nov 27, 2024
1 parent 09e578c commit b682255
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 81 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.folio.models.pieces;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import org.folio.rest.jaxrs.model.Piece;

import java.util.List;

@AllArgsConstructor
@Getter
@Builder
public class PieceBatchStatusUpdateHolder extends BasePieceFlowHolder {

private Piece.ReceivingStatus receivingStatus;
private List<Piece> pieces;
private String poLineId;

@Override
public String getOrderLineId() {
return poLineId;
}

@Override
public String getTitleId() {
return null;
}

}
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
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.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.orders.utils.HelperUtils;
import org.folio.orders.utils.PoLineCommonUtil;
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.PieceBatchStatus;
import org.folio.service.ProtectionService;
import org.folio.service.orders.PurchaseOrderLineService;
import org.folio.service.pieces.PieceService;
Expand All @@ -32,10 +29,9 @@
import org.folio.service.pieces.flows.DefaultPieceFlowsValidator;

import io.vertx.core.Future;
import io.vertx.core.json.JsonObject;

@Log4j2
public class PieceUpdateFlowManager {
private static final Logger logger = LogManager.getLogger(PieceUpdateFlowManager.class);

private final PieceStorageService pieceStorageService;
private final PieceService pieceService;
Expand All @@ -47,9 +43,9 @@ public class PieceUpdateFlowManager {
private final PurchaseOrderLineService purchaseOrderLineService;

public PieceUpdateFlowManager(PieceStorageService pieceStorageService, PieceService pieceService, ProtectionService protectionService,
PieceUpdateFlowPoLineService updatePoLineService, PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager,
BasePieceFlowHolderBuilder basePieceFlowHolderBuilder, DefaultPieceFlowsValidator defaultPieceFlowsValidator,
PurchaseOrderLineService purchaseOrderLineService) {
PieceUpdateFlowPoLineService updatePoLineService, PieceUpdateFlowInventoryManager pieceUpdateFlowInventoryManager,
BasePieceFlowHolderBuilder basePieceFlowHolderBuilder, DefaultPieceFlowsValidator defaultPieceFlowsValidator,
PurchaseOrderLineService purchaseOrderLineService) {
this.pieceStorageService = pieceStorageService;
this.pieceService = pieceService;
this.protectionService = protectionService;
Expand Down Expand Up @@ -81,78 +77,79 @@ public Future<Void> updatePiece(Piece pieceToUpdate, boolean createItem, boolean
.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 -> {
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;
return Future.succeededFuture();
})
.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<Void> updatePiecesStatuses(List<String> pieceIds, PieceBatchStatus.ReceivingStatus receivingStatus, RequestContext requestContext) {
var newStatus = Piece.ReceivingStatus.fromValue(receivingStatus.value());
return pieceStorageService.getPiecesByIds(pieceIds, 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))
.map(v -> updatePiecesStatusesByPoLine(holder, requestContext)))
.toList())
.compose(HelperUtils::collectResultsOnSuccess)
.mapEmpty();
}

protected Future<Void> 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<Void> updatePoLine(PieceBatchStatusUpdateHolder holder, RequestContext requestContext) {
return updatePoLine(holder, holder.getPieces(), requestContext);
}

protected <T extends BasePieceFlowHolder> Future<Void> updatePoLine(T holder, List<Piece> 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<Piece> 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<Location> 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<Piece> fromStorage, List<Piece> toUpdate) {

// 1. collect all piece statuses
Map<String, Piece.ReceivingStatus> 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();

logger.info("calculatePoLineReceiptStatus:: POL: {}, received: {}, expected: {}",
poLine.getId(), receivedQuantity, expectedQuantity);
private boolean updatePiecesStatusesByPoLine(PieceBatchStatusUpdateHolder holder, RequestContext requestContext) {
var isAnyPiecesUpdated = holder.getPieces().stream().anyMatch(piece -> updatePieceStatus(piece, piece.getReceivingStatus(), holder.getReceivingStatus()));
if (isAnyPiecesUpdated) {
pieceService.receiptConsistencyPiecePoLine(holder.getPoLineId(), requestContext);
}
return isAnyPiecesUpdated;
}

return expectedQuantity == 0 ? FULLY_RECEIVED :
receivedQuantity > 0 ? PARTIALLY_RECEIVED : AWAITING_RECEIPT;
private List<Location> getPieceLocations(List<Piece> pieces, CompositePoLine poLine) {
return pieces.stream()
.flatMap(pieceToUpdate -> PieceUtil.findOrderPieceLineLocation(pieceToUpdate, poLine).stream())
.toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
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.folio.service.orders.utils.StatusUtils.calculatePoLineReceiptStatus;
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;
Expand Down Expand Up @@ -173,7 +175,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),
Expand Down Expand Up @@ -239,7 +241,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();
Expand Down Expand Up @@ -286,7 +288,7 @@ void shouldUpdateLineQuantityIfPoLineIsNotPackageAndHoldingReferenceChangedAndSh
final ArgumentCaptor<Piece> 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<PieceUpdateHolder> pieceUpdateHolderCapture = ArgumentCaptor.forClass(PieceUpdateHolder.class);
doAnswer((Answer<Future<Void>>) invocation -> {
Expand Down Expand Up @@ -320,12 +322,12 @@ void shouldUpdateLineQuantityIfPoLineIsNotPackageAndHoldingReferenceChangedAndSh
@Test
void poLineStatusWhenReceiveLast() {
// given
CompositePoLine poLine = new CompositePoLine().withId(UUID.randomUUID().toString());
String poLineId = UUID.randomUUID().toString();
List<Piece> fromStorage = givenPieces(EXPECTED, RECEIVED, UNRECEIVABLE);
List<Piece> update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(RECEIVED));

// when
var receiptStatus = pieceUpdateFlowManager.calculatePoLineReceiptStatus(poLine, fromStorage, update);
var receiptStatus = calculatePoLineReceiptStatus(poLineId, fromStorage, update);

// then
assertEquals(CompositePoLine.ReceiptStatus.FULLY_RECEIVED, receiptStatus);
Expand All @@ -334,12 +336,12 @@ void poLineStatusWhenReceiveLast() {
@Test
void poLineStatusWhenExpectLast() {
// given
CompositePoLine poLine = new CompositePoLine().withId(UUID.randomUUID().toString());
String poLineId = UUID.randomUUID().toString();
List<Piece> fromStorage = givenPieces(RECEIVED, RECEIVED, UNRECEIVABLE);
List<Piece> update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(EXPECTED));

// when
var receiptStatus = pieceUpdateFlowManager.calculatePoLineReceiptStatus(poLine, fromStorage, update);
var receiptStatus = calculatePoLineReceiptStatus(poLineId, fromStorage, update);

// then
assertEquals(CompositePoLine.ReceiptStatus.PARTIALLY_RECEIVED, receiptStatus);
Expand All @@ -348,12 +350,12 @@ void poLineStatusWhenExpectLast() {
@Test
void poLineStatusWhenExpectAll() {
// given
CompositePoLine poLine = new CompositePoLine().withId(UUID.randomUUID().toString());
String poLineId = UUID.randomUUID().toString();
List<Piece> fromStorage = givenPieces(RECEIVED);
List<Piece> update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(EXPECTED));

// when
var receiptStatus = pieceUpdateFlowManager.calculatePoLineReceiptStatus(poLine, fromStorage, update);
var receiptStatus = calculatePoLineReceiptStatus(poLineId, fromStorage, update);

// then
assertEquals(CompositePoLine.ReceiptStatus.AWAITING_RECEIPT, receiptStatus);
Expand All @@ -362,12 +364,12 @@ void poLineStatusWhenExpectAll() {
@Test
void poLineStatusWhenReceivePart() {
// given
CompositePoLine poLine = new CompositePoLine().withId(UUID.randomUUID().toString());
String poLineId = UUID.randomUUID().toString();
List<Piece> fromStorage = givenPieces(EXPECTED, EXPECTED);
List<Piece> update = List.of(new Piece().withId(fromStorage.get(0).getId()).withReceivingStatus(UNRECEIVABLE));

// when
var receiptStatus = pieceUpdateFlowManager.calculatePoLineReceiptStatus(poLine, fromStorage, update);
var receiptStatus = calculatePoLineReceiptStatus(poLineId, fromStorage, update);

// then
assertEquals(CompositePoLine.ReceiptStatus.PARTIALLY_RECEIVED, receiptStatus);
Expand Down

0 comments on commit b682255

Please sign in to comment.