From 77571bc87cecd5f72413200597b29715b5e5d592 Mon Sep 17 00:00:00 2001 From: Saba-Zedginidze-EPAM <148070844+Saba-Zedginidze-EPAM@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:05:58 +0400 Subject: [PATCH 1/2] [MODORDERS-1165] Populate piece receivingTenentId when openning order with Instance, Holding (#999) * [MODORDERS-1165] Populate piece receivingTenentId when openning order with Instance, Holding * [MODORDERS-1165] Check for null tenantIds * [MODORDERS-1165] Refactor location collectors * [MODORDERS-1165] Refactor for more code clarity * [MODORDERS-1165] Minor adjustments * [MODORDERS-1165] Refactor calculatePiecesWithoutItemIdQuantity * [MODORDERS-1165] Remove redundant/repeated code * [MODORDERS-1165] Fix filtering logic for holdingIds * [MODORDERS-1165] Refactor OpenCompositeOrderPieceService * [MODORDERS-1165] Fix failing test * [MODORDERS-1165] Attempt increase coverage * [MODORDERS-1165] Add unit test for update scenario * [MODORDERS-1165] Improve logging and readability * [MODORDERS-1165] Update tests --------- Co-authored-by: Serhii Nosko --- .../folio/orders/utils/PoLineCommonUtil.java | 61 ++++-- .../open/OpenCompositeOrderHolderBuilder.java | 201 ++++++++---------- .../open/OpenCompositeOrderPieceService.java | 98 ++++----- .../OpenCompositeOrderPieceServiceTest.java | 99 +++++++-- 4 files changed, 250 insertions(+), 209 deletions(-) diff --git a/src/main/java/org/folio/orders/utils/PoLineCommonUtil.java b/src/main/java/org/folio/orders/utils/PoLineCommonUtil.java index 6ab96ebba..4894c78e5 100644 --- a/src/main/java/org/folio/orders/utils/PoLineCommonUtil.java +++ b/src/main/java/org/folio/orders/utils/PoLineCommonUtil.java @@ -13,15 +13,16 @@ import static org.folio.rest.jaxrs.model.PoLine.ReceiptStatus.ONGOING; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; +import one.util.streamex.StreamEx; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.folio.rest.core.exceptions.HttpException; @@ -182,32 +183,58 @@ public static boolean isHoldingCreationRequiredForLocation(CompositePoLine compP * @return map of grouped locations where key is location id and value is list of locations with the same id */ public static Map> groupLocationsByLocationId(CompositePoLine compPOL) { - if (CollectionUtils.isEmpty(compPOL.getLocations())) { - return Collections.emptyMap(); - } + return extractLocationsForPoLineByLocationId(compPOL) + .collect(Collectors.groupingBy(Location::getLocationId)); + } - return compPOL.getLocations() - .stream() - .filter(location -> Objects.nonNull(location.getLocationId())) - .filter(location -> !isHoldingCreationRequiredForLocation(compPOL, location)) - .collect(Collectors.groupingBy(Location::getLocationId)); + /** + * Map all PO Line's location to tenantIds for which the holding should be created by location identifier + * @param compPOL PO line with locations + * @return map of locations and tenantIds where key is location id and value is the tenantId of the specified location + */ + public static Map mapLocationIdsToTenantIds(CompositePoLine compPOL) { + return extractLocationsForPoLineByLocationId(compPOL) + .filter(location -> Objects.nonNull(location.getTenantId())) + .collect(Collectors.toMap(Location::getLocationId, Location::getTenantId)); } /** - * Group all PO Line's locations for which the holding should be created by location identifier + * Group all PO Line's locations for which the holding should be created by holding identifier * @param compPOL PO line with locations to group * @return map of grouped locations where key is holding id and value is list of locations with the same id */ public static Map> groupLocationsByHoldingId(CompositePoLine compPOL) { + return extractLocationsForPoLineByHoldingId(compPOL) + .collect(Collectors.groupingBy(Location::getHoldingId)); + } + + /** + * Map all PO Line's location to tenantIds for which the holding should be created by holding identifier + * @param compPOL PO line with locations + * @return map of locations and tenantIds where key is holding id and value is the tenantId of the specified location + */ + public static Map mapHoldingIdsToTenantIds(CompositePoLine compPOL) { + return extractLocationsForPoLineByHoldingId(compPOL) + .filter(location -> Objects.nonNull(location.getTenantId())) + .collect(Collectors.toMap(Location::getHoldingId, Location::getTenantId)); + } + + private static StreamEx extractLocationsForPoLineByLocationId(CompositePoLine compPOL) { + return extractLocationsForPoLine(compPOL, Location::getLocationId) + .filter(location -> !isHoldingCreationRequiredForLocation(compPOL, location)); + } + + private static StreamEx extractLocationsForPoLineByHoldingId(CompositePoLine compPOL) { + return extractLocationsForPoLine(compPOL, Location::getHoldingId) + .filter(location -> isHoldingCreationRequiredForLocation(compPOL, location)); + } + + private static StreamEx extractLocationsForPoLine(CompositePoLine compPOL, Function fieldExtractor) { if (CollectionUtils.isEmpty(compPOL.getLocations())) { - return Collections.emptyMap(); + return StreamEx.empty(); } - - return compPOL.getLocations() - .stream() - .filter(location -> Objects.nonNull(location.getHoldingId())) - .filter(location -> isHoldingCreationRequiredForLocation(compPOL, location)) - .collect(Collectors.groupingBy(Location::getHoldingId)); + return StreamEx.of(compPOL.getLocations()) + .filter(location -> Objects.nonNull(fieldExtractor.apply(location))); } public static List getTenantsFromLocations(CompositePoLine poLine) { diff --git a/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderHolderBuilder.java b/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderHolderBuilder.java index db260e9b2..0033d3be2 100644 --- a/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderHolderBuilder.java +++ b/src/main/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderHolderBuilder.java @@ -9,6 +9,12 @@ import static org.folio.orders.utils.PoLineCommonUtil.groupLocationsByLocationId; import static org.folio.orders.utils.PoLineCommonUtil.isItemsUpdateRequiredForEresource; import static org.folio.orders.utils.PoLineCommonUtil.isItemsUpdateRequiredForPhysical; +import static org.folio.orders.utils.PoLineCommonUtil.mapHoldingIdsToTenantIds; +import static org.folio.orders.utils.PoLineCommonUtil.mapLocationIdsToTenantIds; +import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.ELECTRONIC_RESOURCE; +import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.OTHER; +import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.PHYSICAL_RESOURCE; +import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.P_E_MIX; import java.util.ArrayList; import java.util.Collections; @@ -16,6 +22,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.commons.collections4.CollectionUtils; @@ -40,23 +48,14 @@ public OpenCompositeOrderHolderBuilder(PieceStorageService pieceStorageService) } public Future buildHolder(CompositePoLine compPOL, String titleId, List expectedPiecesWithItem, - RequestContext requestContext) { - OpenOrderPieceHolder holder = new OpenOrderPieceHolder(titleId); + RequestContext requestContext) { return pieceStorageService.getPiecesByPoLineId(compPOL, requestContext) - .map(holder::withExistingPieces) - .map(aHolder -> { - holder.withPiecesWithLocationToProcess(buildPiecesByLocationId(compPOL, expectedPiecesWithItem, holder.getExistingPieces())); - holder.withPiecesWithHoldingToProcess(buildPiecesByHoldingId(compPOL, expectedPiecesWithItem, holder.getExistingPieces())); - return null; - }) - .map(aVoid -> holder.withPiecesWithChangedLocation(getPiecesWithChangedLocation(compPOL, holder.getPiecesWithLocationToProcess(), holder.getExistingPieces()))) - .map(aHolder -> { - if (CollectionUtils.isEmpty(compPOL.getLocations())) { - holder.withPiecesWithoutLocationId(createPiecesWithoutLocationId(compPOL, holder.getExistingPieces())); - } - return null; - }) - .map(aVoid -> holder); + .map(pieces -> new OpenOrderPieceHolder(titleId).withExistingPieces(pieces)) + .map(holder -> holder + .withPiecesWithLocationToProcess(buildPiecesByLocationId(compPOL, expectedPiecesWithItem, holder.getExistingPieces())) + .withPiecesWithHoldingToProcess(buildPiecesByHoldingId(compPOL, expectedPiecesWithItem, holder.getExistingPieces())) + .withPiecesWithChangedLocation(getPiecesWithChangedLocation(compPOL, holder.getPiecesWithLocationToProcess(), holder.getExistingPieces())) + .withPiecesWithoutLocationId(createPiecesWithoutLocationId(compPOL, holder.getExistingPieces()))); } private List buildPiecesByLocationId(CompositePoLine compPOL, List expectedPiecesWithItem, List existingPieces) { @@ -64,11 +63,11 @@ private List buildPiecesByLocationId(CompositePoLine compPOL, List // For each location collect pieces that need to be created. groupLocationsByLocationId(compPOL) .forEach((lineLocationId, lineLocations) -> { - List filteredExistingPieces = filterByLocationId(existingPieces, lineLocationId); - List createdPiecesWithItem = processPiecesWithItemAndLocationId(expectedPiecesWithItem, filteredExistingPieces, lineLocationId); - piecesToCreate.addAll(createdPiecesWithItem); - List piecesWithoutItem = processPiecesWithoutItemAndLocationId(compPOL, filteredExistingPieces, lineLocationId, lineLocations); - piecesToCreate.addAll(piecesWithoutItem); + var tenantId = mapLocationIdsToTenantIds(compPOL).get(lineLocationId); + var filteredExistingPieces = filterByLocationId(existingPieces, lineLocationId); + var filteredExpectedPiecesWithItem = filterByLocationId(expectedPiecesWithItem, lineLocationId); + piecesToCreate.addAll(collectMissingPiecesWithItem(filteredExpectedPiecesWithItem, filteredExistingPieces)); + piecesToCreate.addAll(processPiecesWithoutItemAndLocationId(compPOL, filteredExistingPieces, lineLocationId, tenantId, lineLocations)); }); return piecesToCreate; } @@ -76,41 +75,40 @@ private List buildPiecesByLocationId(CompositePoLine compPOL, List private List getPiecesWithChangedLocation(CompositePoLine compPOL, List needProcessPieces, List existingPieces) { Map> existingPieceMap = numOfPiecesByFormatAndLocationId(existingPieces, compPOL.getId()); Map> needProcessPiecesMap = numOfPiecesByFormatAndLocationId(needProcessPieces, compPOL.getId()); + Map locationToTenantId = mapLocationIdsToTenantIds(compPOL); List piecesForLocationUpdate = new ArrayList<>(); - for (Map.Entry> entry : existingPieceMap.entrySet()) { - String existingPieceLocationId = entry.getKey(); - Map existingPieceQtyMap = entry.getValue(); - for (Map.Entry existPieceFormatQty : existingPieceQtyMap.entrySet()) { - Map pieceLocationMap = needProcessPiecesMap.get(existingPieceLocationId); - if (pieceLocationMap == null) { - needProcessPiecesMap.forEach((newLocationId, value) -> { - Integer pieceQty = value.get(existPieceFormatQty.getKey()); - if (pieceQty != null && pieceQty.equals(existPieceFormatQty.getValue())) { - List piecesWithUpdatedLocation = existingPieces.stream() - .filter(piece -> existingPieceLocationId.equals(piece.getLocationId()) - && existPieceFormatQty.getKey() == piece.getFormat()) - .map(piece -> piece.withLocationId(newLocationId)) - .collect(Collectors.toList()); - piecesForLocationUpdate.addAll(piecesWithUpdatedLocation); - } - }); + existingPieceMap.forEach((existingPieceLocationId, existingPieceQtyMap) -> { + for (var existPieceFormatQty : existingPieceQtyMap.entrySet()) { + if (needProcessPiecesMap.get(existingPieceLocationId) != null) { + continue; } + needProcessPiecesMap.forEach((newLocationId, pieceQtyMap) -> { + var pieceQty = pieceQtyMap.get(existPieceFormatQty.getKey()); + if (pieceQty == null || !pieceQty.equals(existPieceFormatQty.getValue())) { + return; + } + piecesForLocationUpdate.addAll(existingPieces.stream() + .filter(piece -> existingPieceLocationId.equals(piece.getLocationId()) && existPieceFormatQty.getKey() == piece.getFormat()) + .map(piece -> piece.withLocationId(newLocationId).withReceivingTenantId(locationToTenantId.get(newLocationId))) + .toList()); + }); } - } + }); return piecesForLocationUpdate; } private List createPiecesWithoutLocationId(CompositePoLine compPOL, List existingPieces) { + if (CollectionUtils.isNotEmpty(compPOL.getLocations())) { + return Collections.emptyList(); + } List piecesToCreate = new ArrayList<>(); Map expectedQuantitiesWithoutLocation = PieceUtil.calculatePiecesQuantityWithoutLocation(compPOL); Map existingPiecesQuantities = calculateQuantityOfExistingPiecesWithoutLocation(existingPieces); expectedQuantitiesWithoutLocation.forEach((format, expectedQty) -> { int remainingPiecesQuantity = expectedQty - existingPiecesQuantities.getOrDefault(format, 0); - if (remainingPiecesQuantity > 0) { - for (int i = 0; i < remainingPiecesQuantity; i++) { - piecesToCreate.add(new Piece().withFormat(format).withPoLineId(compPOL.getId())); - } + for (int i = 0; i < remainingPiecesQuantity; i++) { + piecesToCreate.add(new Piece().withFormat(format).withPoLineId(compPOL.getId())); } }); return piecesToCreate; @@ -122,38 +120,20 @@ private Map calculateQuantityOfExistingPiecesWithoutLocat .groupingBy(Piece::getFormat, collectingAndThen(toList(), List::size)); } - - private List processPiecesWithItemAndLocationId(List piecesWithItem, List existedPieces, String existingPieceLocationId) { - List expectedPiecesWithItem = filterByLocationId(piecesWithItem, existingPieceLocationId); - return collectMissingPiecesWithItem(expectedPiecesWithItem, existedPieces); - } - - private List filterByLocationId(List pieces, String locationId) { - return pieces.stream() - .filter(piece -> locationId.equals(piece.getLocationId())) - .collect(Collectors.toList()); - } - private List buildPiecesByHoldingId(CompositePoLine compPOL, List expectedPiecesWithItem, List existingPieces) { List piecesToCreate = new ArrayList<>(); // For each location collect pieces that need to be created. groupLocationsByHoldingId(compPOL) .forEach((existingPieceHoldingId, existingPieceLocations) -> { - List filteredExistingPieces = filterByHoldingId(existingPieces, existingPieceHoldingId); - List createdPiecesWithItem = processPiecesWithItemAndHoldingId(expectedPiecesWithItem, filteredExistingPieces, existingPieceHoldingId); - piecesToCreate.addAll(createdPiecesWithItem); - List piecesWithoutItem = processPiecesWithoutItemAndHoldingId(compPOL, filteredExistingPieces, existingPieceHoldingId, existingPieceLocations); - piecesToCreate.addAll(piecesWithoutItem); + var tenantId = mapHoldingIdsToTenantIds(compPOL).get(existingPieceHoldingId); + var filteredExistingPieces = filterByHoldingId(existingPieces, existingPieceHoldingId); + var filteredExpectedPiecesWithItem = filterByHoldingId(expectedPiecesWithItem, existingPieceHoldingId); + piecesToCreate.addAll(collectMissingPiecesWithItem(filteredExpectedPiecesWithItem, filteredExistingPieces)); + piecesToCreate.addAll(processPiecesWithoutItemAndHoldingId(compPOL, filteredExistingPieces, existingPieceHoldingId, tenantId, existingPieceLocations)); }); return piecesToCreate; } - private List processPiecesWithItemAndHoldingId(List piecesWithItem, List existedPieces, String existingPieceLocationId) { - List expectedPiecesWithItem = filterByHoldingId(piecesWithItem, existingPieceLocationId); - return collectMissingPiecesWithItem(expectedPiecesWithItem, existedPieces); - } - - /** * Find pieces for which created items, but which are not yet in the storage. * @@ -164,43 +144,48 @@ private List processPiecesWithItemAndHoldingId(List piecesWithItem private List collectMissingPiecesWithItem(List piecesWithItem, List existingPieces) { return piecesWithItem.stream() .filter(pieceWithItem -> existingPieces.stream() + .filter(existingPiece -> Objects.nonNull(existingPiece.getItemId())) .noneMatch(existingPiece -> pieceWithItem.getItemId().equals(existingPiece.getItemId()))) .collect(Collectors.toList()); } + private List filterByLocationId(List pieces, String locationId) { + return filterByField(pieces, locationId, Piece::getLocationId); + } private List filterByHoldingId(List pieces, String holdingId) { - return pieces.stream() - .filter(piece -> holdingId.equals(piece.getHoldingId())) + return filterByField(pieces, holdingId, Piece::getHoldingId); + } + + private List filterByField(List pieces, String value, Function fieldExtractor) { + return StreamEx.of(pieces) + .filter(piece -> value.equals(fieldExtractor.apply(piece))) .collect(Collectors.toList()); } - private List processPiecesWithoutItemAndLocationId(CompositePoLine compPOL, List existedPieces, String existingPieceLocationId, List existingPieceLocations) { - List piecesToCreate = new ArrayList<>(); - Map expectedQuantitiesWithoutItem = calculatePiecesWithoutItemIdQuantity(compPOL, existingPieceLocations); - Map existedQuantityWithoutItem = calculateQuantityOfExistingPiecesWithoutItem(existedPieces); - expectedQuantitiesWithoutItem.forEach((format, expectedQty) -> { - int remainingPiecesQuantity = expectedQty - existedQuantityWithoutItem.getOrDefault(format, 0); - if (remainingPiecesQuantity > 0) { - for (int i = 0; i < remainingPiecesQuantity; i++) { - piecesToCreate.add(new Piece().withFormat(format).withLocationId(existingPieceLocationId).withPoLineId(compPOL.getId())); - } - } - }); - return piecesToCreate; + private List processPiecesWithoutItemAndLocationId(CompositePoLine compPOL, List existedPieces, String existingPieceLocationId, + String tenantId, List existingPieceLocations) { + return processPiecesWithoutItemAndLocationIdOrHoldingId(compPOL, existedPieces, existingPieceLocations, tenantId, + () -> new Piece().withLocationId(existingPieceLocationId)); } - private List processPiecesWithoutItemAndHoldingId(CompositePoLine compPOL, List existedPieces, String existingPieceHoldingId, List existingPieceLocations) { - List piecesToCreate = new ArrayList<>(); + private List processPiecesWithoutItemAndHoldingId(CompositePoLine compPOL, List existedPieces, String existingPieceHoldingId, + String tenantId, List existingPieceLocations) { + return processPiecesWithoutItemAndLocationIdOrHoldingId(compPOL, existedPieces, existingPieceLocations, tenantId, + () -> new Piece().withHoldingId(existingPieceHoldingId)); + } + + private List processPiecesWithoutItemAndLocationIdOrHoldingId(CompositePoLine compPOL, List existedPieces, List existingPieceLocations, + String receivingTenantId, Supplier pieceSupplier) { Map expectedQuantitiesWithoutItem = calculatePiecesWithoutItemIdQuantity(compPOL, existingPieceLocations); Map existedQuantityWithoutItem = calculateQuantityOfExistingPiecesWithoutItem(existedPieces); + + List piecesToCreate = new ArrayList(); expectedQuantitiesWithoutItem.forEach((format, expectedQty) -> { int remainingPiecesQuantity = expectedQty - existedQuantityWithoutItem.getOrDefault(format, 0); - if (remainingPiecesQuantity > 0) { - for (int i = 0; i < remainingPiecesQuantity; i++) { - piecesToCreate.add(new Piece().withFormat(format).withHoldingId(existingPieceHoldingId).withPoLineId(compPOL.getId())); - } + for (int i = 0; i < remainingPiecesQuantity; i++) { + piecesToCreate.add(pieceSupplier.get().withFormat(format).withPoLineId(compPOL.getId()).withReceivingTenantId(receivingTenantId)); } }); return piecesToCreate; @@ -224,43 +209,27 @@ private Map calculateQuantityOfExistingPiecesWithoutItem( /** * Calculates pieces quantity for list of locations and return map where piece format is a key and corresponding quantity of pieces as value. * - * @param compPOL composite PO Line + * @param compPOL composite PO Line * @param locations list of locations to calculate quantity for * @return quantity of pieces per piece format either not required Inventory item for PO Line */ private Map calculatePiecesWithoutItemIdQuantity(CompositePoLine compPOL, List locations) { // Piece records are not going to be created for PO Line which is going to be checked-in - if (compPOL.getCheckinItems() != null && compPOL.getCheckinItems()) { + if (Boolean.TRUE.equals(compPOL.getCheckinItems())) { return Collections.emptyMap(); } - EnumMap quantities = new EnumMap<>(Piece.Format.class); - switch (compPOL.getOrderFormat()) { - case P_E_MIX: - if (!isItemsUpdateRequiredForPhysical(compPOL)) { - quantities.put(Piece.Format.PHYSICAL, calculatePiecesQuantity(Piece.Format.PHYSICAL, locations)); - } - if (!isItemsUpdateRequiredForEresource(compPOL)) { - quantities.put(Piece.Format.ELECTRONIC, calculatePiecesQuantity(Piece.Format.ELECTRONIC, locations)); - } - return quantities; - case PHYSICAL_RESOURCE: - if (!isItemsUpdateRequiredForPhysical(compPOL)) { - quantities.put(Piece.Format.PHYSICAL, calculatePiecesQuantity(Piece.Format.PHYSICAL, locations)); - } - return quantities; - case ELECTRONIC_RESOURCE: - if (!isItemsUpdateRequiredForEresource(compPOL)) { - quantities.put(Piece.Format.ELECTRONIC, calculatePiecesQuantity(Piece.Format.ELECTRONIC, locations)); - } - return quantities; - case OTHER: - if (!isItemsUpdateRequiredForPhysical(compPOL)) { - quantities.put(Piece.Format.OTHER, calculatePiecesQuantity(Piece.Format.OTHER, locations)); - } - return quantities; - default: - return Collections.emptyMap(); + var quantities = new EnumMap(Piece.Format.class); + var orderFormat = compPOL.getOrderFormat(); + if ((orderFormat == P_E_MIX || orderFormat == PHYSICAL_RESOURCE) && !isItemsUpdateRequiredForPhysical(compPOL)) { + quantities.put(Piece.Format.PHYSICAL, calculatePiecesQuantity(Piece.Format.PHYSICAL, locations)); + } + if ((orderFormat == P_E_MIX || orderFormat == ELECTRONIC_RESOURCE) && !isItemsUpdateRequiredForEresource(compPOL)) { + quantities.put(Piece.Format.ELECTRONIC, calculatePiecesQuantity(Piece.Format.ELECTRONIC, locations)); + } + if (orderFormat == OTHER && !isItemsUpdateRequiredForPhysical(compPOL)) { + quantities.put(Piece.Format.OTHER, calculatePiecesQuantity(Piece.Format.OTHER, locations)); } + return quantities; } } 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 e0a697006..fc90fd041 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,6 +1,5 @@ package org.folio.service.orders.flows.update.open; -import static java.util.stream.Collectors.toList; import static org.folio.orders.utils.HelperUtils.calculateInventoryItemsQuantity; import static org.folio.orders.utils.HelperUtils.collectResultsOnSuccess; import static org.folio.orders.utils.RequestContextUtil.createContextWithNewTenantId; @@ -72,89 +71,78 @@ public OpenCompositeOrderPieceService(PurchaseOrderStorageService purchaseOrderS * @return void future */ public Future> handlePieces(CompositePoLine compPOL, String titleId, List expectedPiecesWithItem, - boolean isInstanceMatchingDisabled, RequestContext requestContext) { - logger.debug("Get pieces by poLine ID"); + boolean isInstanceMatchingDisabled, RequestContext requestContext) { + logger.debug("handlePieces:: Get pieces by poLine ID - {}", compPOL.getId()); return openCompositeOrderHolderBuilder.buildHolder(compPOL, titleId, expectedPiecesWithItem, requestContext) .compose(holder -> { - if (CollectionUtils.isNotEmpty(holder.getPiecesWithLocationToProcess()) && - (holder.getPiecesWithChangedLocation().size() == holder.getPiecesWithLocationToProcess().size())) { - return updatePieces(holder, requestContext); - } else { - return createPieces(holder, isInstanceMatchingDisabled, requestContext); + var piecesWithChangedLocation = holder.getPiecesWithChangedLocation(); + if (CollectionUtils.isEmpty(piecesWithChangedLocation) || piecesWithChangedLocation.size() != holder.getPiecesWithLocationToProcess().size()) { + return purchaseOrderStorageService.getCompositeOrderById(compPOL.getPurchaseOrderId(), requestContext) + .compose(order -> createPieces(holder, order, isInstanceMatchingDisabled, requestContext)); } + return updatePieces(holder, requestContext); }) - .map(pieces -> { - int createdItemsQuantity = expectedPiecesWithItem.size(); - validateItemsCreation(compPOL, createdItemsQuantity); - return pieces; - }); + .map(pieces -> validateItemsCreationForPieces(pieces, compPOL, expectedPiecesWithItem.size())); } private Future> updatePieces(OpenOrderPieceHolder holder, RequestContext requestContext) { - List> updateFutures = holder.getPiecesWithChangedLocation().stream() - .map(piece -> updatePieceRecord(piece, requestContext).map(v -> piece)) - .collect(toList()); - return collectResultsOnSuccess(updateFutures); + logger.debug("updatePieces:: Trying to update pieces"); + return collectResultsOnSuccess(holder.getPiecesWithChangedLocation().stream() + .map(piece -> updatePiece(piece, requestContext)) + .toList()); } - private Future> createPieces(OpenOrderPieceHolder holder, boolean isInstanceMatchingDisabled, RequestContext requestContext) { + private Future> createPieces(OpenOrderPieceHolder holder, CompositePurchaseOrder order, boolean isInstanceMatchingDisabled, RequestContext requestContext) { + logger.debug("createPieces:: Trying to create pieces"); List piecesToCreate = new ArrayList<>(holder.getPiecesWithLocationToProcess()); piecesToCreate.addAll(holder.getPiecesWithHoldingToProcess()); piecesToCreate.addAll(holder.getPiecesWithoutLocationId()); - piecesToCreate.forEach(piece -> piece.setTitleId(holder.getTitleId())); - logger.debug("Trying to create pieces"); - List> piecesToCreateFutures = new ArrayList<>(); - piecesToCreate.forEach(piece -> - piecesToCreateFutures.add(createPiece(piece, isInstanceMatchingDisabled, requestContext)) - ); + + var piecesToCreateFutures = piecesToCreate.stream() + .map(piece -> piece.withTitleId(holder.getTitleId())) + .map(piece -> createPiece(piece, order, isInstanceMatchingDisabled, requestContext)) + .toList(); return collectResultsOnSuccess(piecesToCreateFutures) .recover(th -> { - logger.error("Piece creation error"); - throw new CompletionException("Piece creation error", th); + logger.error("Piece creation failed", th); + throw new CompletionException("Piece creation error", th); }); } - public Future createPiece(Piece piece, boolean isInstanceMatchingDisabled, RequestContext requestContext) { - logger.debug("createPiece start"); - return purchaseOrderStorageService.getCompositeOrderByPoLineId(piece.getPoLineId(), requestContext) - .compose(order -> titlesService.getTitleById(piece.getTitleId(), requestContext) - .compose(title -> protectionService.isOperationRestricted(title.getAcqUnitIds(), ProtectedOperationType.CREATE, requestContext) - .map(v -> order))) - .compose(order -> openOrderUpdateInventory(order, order.getCompositePoLines().get(0), piece, isInstanceMatchingDisabled, requestContext)) + public Future createPiece(Piece piece, CompositePurchaseOrder order, boolean isInstanceMatchingDisabled, RequestContext requestContext) { + logger.debug("createPiece:: Creating piece - {}", piece.getId()); + return titlesService.getTitleById(piece.getTitleId(), requestContext) + .compose(title -> protectionService.isOperationRestricted(title.getAcqUnitIds(), ProtectedOperationType.CREATE, requestContext)) + .compose(v -> openOrderUpdateInventory(order, order.getCompositePoLines().get(0), piece, isInstanceMatchingDisabled, requestContext)) .compose(v -> pieceStorageService.insertPiece(piece, requestContext)); } - public Future updatePieceRecord(Piece piece, RequestContext requestContext) { - return purchaseOrderStorageService.getCompositeOrderByPoLineId(piece.getPoLineId(), requestContext) - .compose(order -> titlesService.getTitleById(piece.getTitleId(), requestContext) - .compose(title -> protectionService.isOperationRestricted(title.getAcqUnitIds(), ProtectedOperationType.UPDATE, requestContext))) + public Future updatePiece(Piece piece, RequestContext requestContext) { + logger.debug("updatePiece:: Updating piece - {}", piece.getId()); + return titlesService.getTitleById(piece.getTitleId(), requestContext) + .compose(title -> protectionService.isOperationRestricted(title.getAcqUnitIds(), ProtectedOperationType.UPDATE, requestContext)) .compose(v -> inventoryItemManager.updateItemWithPieceFields(piece, requestContext)) .compose(vVoid -> pieceStorageService.getPieceById(piece.getId(), requestContext)) .compose(pieceStorage -> { - Piece.ReceivingStatus receivingStatusUpdate = piece.getReceivingStatus(); - Piece.ReceivingStatus receivingStatusStorage = pieceStorage.getReceivingStatus(); - boolean isReceivingStatusChanged = receivingStatusStorage.compareTo(receivingStatusUpdate) != 0; - + var receivingStatusUpdate = piece.getReceivingStatus(); + var receivingStatusStorage = pieceStorage.getReceivingStatus(); + boolean isReceivingStatusChanged = !receivingStatusStorage.equals(receivingStatusUpdate); if (isReceivingStatusChanged) { piece.setStatusUpdatedDate(new Date()); } - return pieceStorageService.updatePiece(piece, requestContext) - .map(ok -> { - JsonObject messageToEventBus = new JsonObject(); - messageToEventBus.put("poLineIdUpdate", piece.getPoLineId()); - logger.debug("receivingStatusStorage -- {}", receivingStatusStorage); - logger.debug("receivingStatusUpdate -- {}", receivingStatusUpdate); - + .compose(v -> { + logger.debug("updatePiece:: receivingStatusStorage - {}, receivingStatusUpdate - {}", receivingStatusStorage, receivingStatusUpdate); if (isReceivingStatusChanged) { + var messageToEventBus = JsonObject.of("poLineIdUpdate", piece.getPoLineId()); receiptStatusPublisher.sendEvent(MessageAddress.RECEIPT_STATUS, messageToEventBus, requestContext); } - return null; + return Future.succeededFuture(); }) .onFailure(e -> logger.error("Error updating piece by id to storage {}", piece.getId(), e)); }) .onFailure(e -> logger.error("Error getting piece by id from storage {}", piece.getId(), e)) - .mapEmpty(); + .map(v -> piece); } /** @@ -192,12 +180,12 @@ private Future updateItemsIfNeeded(CompositePurchaseOrder compPO, Compos : Future.succeededFuture(); } - private void validateItemsCreation(CompositePoLine compPOL, int itemsSize) { + private List validateItemsCreationForPieces(List pieces, CompositePoLine compPOL, int itemsSize) { int expectedItemsQuantity = calculateInventoryItemsQuantity(compPOL); - if (itemsSize != expectedItemsQuantity) { - String message = String.format("Error creating items for PO Line with '%s' id. Expected %d but %d created", - compPOL.getId(), expectedItemsQuantity, itemsSize); - throw new InventoryException(message); + if (itemsSize == expectedItemsQuantity) { + return pieces; } + throw new InventoryException(String.format("Error creating items for PO Line with '%s' id. Expected %d but %d created", + compPOL.getId(), expectedItemsQuantity, itemsSize)); } } diff --git a/src/test/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderPieceServiceTest.java b/src/test/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderPieceServiceTest.java index c8d95379d..64e27b273 100644 --- a/src/test/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderPieceServiceTest.java +++ b/src/test/java/org/folio/service/orders/flows/update/open/OpenCompositeOrderPieceServiceTest.java @@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -142,14 +143,16 @@ void testShouldUpdatePieceByInvokingMethodAndDontSentEventToUpdatePoLineIfReceiv Piece piece = getMockAsJson(PIECE_PATH,"pieceRecord").mapTo(Piece.class); Piece pieceFromStorage = JsonObject.mapFrom(piece).mapTo(Piece.class); CompositePurchaseOrder order = getMockAsJson(ORDER_PATH).mapTo(CompositePurchaseOrder.class); + Title title = new Title().withId(piece.getTitleId()).withTitle("test title"); doReturn(succeededFuture(null)).when(protectionService).isOperationRestricted(any(), eq(ProtectedOperationType.UPDATE), eq(requestContext)); doReturn(succeededFuture(null)).when(inventoryItemManager).updateItemWithPieceFields(eq(piece), eq(requestContext)); doReturn(succeededFuture(order)).when(purchaseOrderStorageService).getCompositeOrderByPoLineId(eq(piece.getPoLineId()), eq(requestContext)); doReturn(succeededFuture(pieceFromStorage)).when(pieceStorageService).getPieceById(eq(piece.getId()), eq(requestContext)); + doReturn(succeededFuture(title)).when(titlesService).getTitleById(eq(title.getId()), eq(requestContext)); doReturn(succeededFuture(null)).when(pieceStorageService).updatePiece(eq(piece), eq(requestContext)); //When - openCompositeOrderPieceService.updatePieceRecord(piece, requestContext).result(); + openCompositeOrderPieceService.updatePiece(piece, requestContext).result(); //Then verify(receiptStatusPublisher, times(0)).sendEvent(eq(MessageAddress.RECEIPT_STATUS), any(JsonObject.class), eq(requestContext)); } @@ -172,7 +175,7 @@ void testShouldUpdatePieceByInvokingMethodAndSentEventToUpdatePoLineIfReceivingS doReturn(succeededFuture(title)).when(titlesService).getTitleById(anyString(), eq(requestContext)); //When - openCompositeOrderPieceService.updatePieceRecord(piece, requestContext).result(); + openCompositeOrderPieceService.updatePiece(piece, requestContext).result(); //Then verify(receiptStatusPublisher, times(1)).sendEvent(eq(MessageAddress.RECEIPT_STATUS), any(JsonObject.class), eq(requestContext)); } @@ -258,18 +261,19 @@ void testShouldUpdateInventoryPositiveCaseIfLineIsPackageAndPieceContainsHolding void shouldCreatePieceWithLocationReferenceIfLineContainsLocationAndInventoryIsInstanceOrNoneAndNoCreatedPieces( String lineType, String createInventory, int qty1, int qty2, String pieceFormat) { //given + String orderId = UUID.randomUUID().toString(); String lineId = UUID.randomUUID().toString(); String locationId1 = UUID.randomUUID().toString(); String locationId2 = UUID.randomUUID().toString(); String titleId = UUID.randomUUID().toString(); - Location location1 = new Location().withLocationId(locationId1).withQuantityPhysical(qty1).withQuantity(qty1); - Location location2 = new Location().withLocationId(locationId2).withQuantityPhysical(qty2).withQuantity(qty2); + Location location1 = new Location().withLocationId(locationId1).withQuantityPhysical(qty1).withQuantity(qty1).withTenantId("tenant1"); + Location location2 = new Location().withLocationId(locationId2).withQuantityPhysical(qty2).withQuantity(qty2).withTenantId("tenant2"); Cost cost = new Cost().withQuantityPhysical(qty1 + qty2).withQuantityElectronic(null); Physical physical = new Physical().withCreateInventory(Physical.CreateInventory.fromValue(createInventory)); - CompositePoLine line = new CompositePoLine().withId(lineId).withCost(cost).withLocations(List.of(location1, location2)) - .withIsPackage(false).withPhysical(physical) - .withOrderFormat(CompositePoLine.OrderFormat.fromValue(lineType)); - CompositePurchaseOrder compOrder = new CompositePurchaseOrder().withCompositePoLines(List.of(line)); + CompositePoLine line = new CompositePoLine().withId(lineId).withPurchaseOrderId(orderId).withCost(cost) + .withLocations(List.of(location1, location2)).withIsPackage(false) + .withPhysical(physical).withOrderFormat(CompositePoLine.OrderFormat.fromValue(lineType)); + CompositePurchaseOrder compOrder = new CompositePurchaseOrder().withId(orderId).withCompositePoLines(List.of(line)); Title title = new Title().withId(titleId).withTitle("test title"); doReturn(succeededFuture(null)).when(openCompositeOrderPieceService).openOrderUpdateInventory(any(CompositePurchaseOrder.class), @@ -277,7 +281,7 @@ void shouldCreatePieceWithLocationReferenceIfLineContainsLocationAndInventoryIsI doReturn(succeededFuture(Collections.emptyList())).when(pieceStorageService).getPiecesByPoLineId(line, requestContext); doReturn(succeededFuture(new Piece())).when(pieceStorageService).insertPiece(any(Piece.class), eq(requestContext)); doReturn(succeededFuture(null)).when(protectionService).isOperationRestricted(any(), any(ProtectedOperationType.class), eq(requestContext)); - doReturn(succeededFuture(compOrder)).when(purchaseOrderStorageService).getCompositeOrderByPoLineId(eq(lineId), eq(requestContext)); + doReturn(succeededFuture(compOrder)).when(purchaseOrderStorageService).getCompositeOrderById(eq(orderId), eq(requestContext)); doReturn(succeededFuture(title)).when(titlesService).getTitleById(titleId, requestContext); final ArgumentCaptor pieceArgumentCaptor = ArgumentCaptor.forClass(Piece.class); @@ -295,6 +299,7 @@ void shouldCreatePieceWithLocationReferenceIfLineContainsLocationAndInventoryIsI piecesLoc1.forEach(piece -> { assertNull(piece.getHoldingId()); assertNull(piece.getItemId()); + assertEquals(location1.getTenantId(), piece.getReceivingTenantId()); assertEquals(lineId, piece.getPoLineId()); assertEquals(titleId, piece.getTitleId()); assertEquals(Piece.Format.fromValue(pieceFormat), piece.getFormat()); @@ -305,6 +310,7 @@ void shouldCreatePieceWithLocationReferenceIfLineContainsLocationAndInventoryIsI piecesLoc2.forEach(piece -> { assertNull(piece.getHoldingId()); assertNull(piece.getItemId()); + assertEquals(location2.getTenantId(), piece.getReceivingTenantId()); assertEquals(lineId, piece.getPoLineId()); assertEquals(titleId, piece.getTitleId()); assertEquals(Piece.Format.fromValue(pieceFormat), piece.getFormat()); @@ -317,6 +323,7 @@ void shouldCreatePieceWithLocationReferenceIfLineContainsLocationAndInventoryIsI void shouldCreatePieceWithLocationReferenceIfElectronicLineContainsLocationAndInventoryIsInstanceOrNoneAndNoCreatedPieces( String lineType, String createInventory, int qty1, int qty2, String pieceFormat) { //given + String orderId = UUID.randomUUID().toString(); String lineId = UUID.randomUUID().toString(); String locationId1 = UUID.randomUUID().toString(); String locationId2 = UUID.randomUUID().toString(); @@ -325,10 +332,11 @@ void shouldCreatePieceWithLocationReferenceIfElectronicLineContainsLocationAndIn Location location2 = new Location().withLocationId(locationId2).withQuantityElectronic(qty2).withQuantity(qty2); Cost cost = new Cost().withQuantityElectronic(qty1 + qty2); Eresource eresource = new Eresource().withCreateInventory(Eresource.CreateInventory.fromValue(createInventory)); - CompositePoLine line = new CompositePoLine().withId(lineId).withCost(cost).withLocations(List.of(location1, location2)) + CompositePoLine line = new CompositePoLine().withId(lineId).withPurchaseOrderId(orderId) + .withCost(cost).withLocations(List.of(location1, location2)) .withIsPackage(false).withEresource(eresource) .withOrderFormat(CompositePoLine.OrderFormat.fromValue(lineType)); - CompositePurchaseOrder compOrder = new CompositePurchaseOrder().withCompositePoLines(List.of(line)); + CompositePurchaseOrder compOrder = new CompositePurchaseOrder().withId(orderId).withCompositePoLines(List.of(line)); Title title = new Title().withId(titleId).withTitle("test title"); @@ -337,7 +345,7 @@ void shouldCreatePieceWithLocationReferenceIfElectronicLineContainsLocationAndIn doReturn(succeededFuture(Collections.emptyList())).when(pieceStorageService).getPiecesByPoLineId(line, requestContext); doReturn(succeededFuture(new Piece())).when(pieceStorageService).insertPiece(any(Piece.class), eq(requestContext)); doReturn(succeededFuture(null)).when(protectionService).isOperationRestricted(any(), any(ProtectedOperationType.class), eq(requestContext)); - doReturn(succeededFuture(compOrder)).when(purchaseOrderStorageService).getCompositeOrderByPoLineId(eq(lineId), eq(requestContext)); + doReturn(succeededFuture(compOrder)).when(purchaseOrderStorageService).getCompositeOrderById(eq(orderId), eq(requestContext)); doReturn(succeededFuture(title)).when(titlesService).getTitleById(titleId, requestContext); final ArgumentCaptor pieceArgumentCaptor = ArgumentCaptor.forClass(Piece.class); @@ -377,6 +385,7 @@ void shouldCreatePieceWithLocationReferenceIfElectronicLineContainsLocationAndIn void shouldCreatePieceWithLocationReferenceIfMixedLineContainsLocationAndInventoryIsInstanceOrNoneAndNoCreatedPieces( String lineType, String elecCreateInventory, String physCreateInventory, int elecQty1, int physQty2) { //given + String orderId = UUID.randomUUID().toString(); String lineId = UUID.randomUUID().toString(); String locationId1 = UUID.randomUUID().toString(); String locationId2 = UUID.randomUUID().toString(); @@ -387,10 +396,11 @@ void shouldCreatePieceWithLocationReferenceIfMixedLineContainsLocationAndInvento Eresource eresource = new Eresource().withCreateInventory(Eresource.CreateInventory.fromValue(elecCreateInventory)); Physical physical = new Physical().withCreateInventory(Physical.CreateInventory.fromValue(physCreateInventory)); - CompositePoLine line = new CompositePoLine().withId(lineId).withCost(cost).withLocations(List.of(location1, location2)) + CompositePoLine line = new CompositePoLine().withId(lineId).withPurchaseOrderId(orderId) + .withCost(cost).withLocations(List.of(location1, location2)) .withIsPackage(false).withEresource(eresource).withPhysical(physical) .withOrderFormat(CompositePoLine.OrderFormat.fromValue(lineType)); - CompositePurchaseOrder compOrder = new CompositePurchaseOrder().withCompositePoLines(List.of(line)); + CompositePurchaseOrder compOrder = new CompositePurchaseOrder().withId(orderId).withCompositePoLines(List.of(line)); Title title = new Title().withId(titleId).withTitle("test title"); doReturn(succeededFuture(null)).when(openCompositeOrderPieceService).openOrderUpdateInventory(any(CompositePurchaseOrder.class), @@ -398,7 +408,7 @@ void shouldCreatePieceWithLocationReferenceIfMixedLineContainsLocationAndInvento doReturn(succeededFuture(Collections.emptyList())).when(pieceStorageService).getPiecesByPoLineId(line, requestContext); doReturn(succeededFuture(new Piece())).when(pieceStorageService).insertPiece(any(Piece.class), eq(requestContext)); doReturn(succeededFuture(null)).when(protectionService).isOperationRestricted(any(), any(ProtectedOperationType.class), eq(requestContext)); - doReturn(succeededFuture(compOrder)).when(purchaseOrderStorageService).getCompositeOrderByPoLineId(eq(lineId), eq(requestContext)); + doReturn(succeededFuture(compOrder)).when(purchaseOrderStorageService).getCompositeOrderById(eq(orderId), eq(requestContext)); doReturn(succeededFuture(title)).when(titlesService).getTitleById(titleId, requestContext); final ArgumentCaptor pieceArgumentCaptor = ArgumentCaptor.forClass(Piece.class); @@ -437,6 +447,7 @@ void shouldCreatePieceWithLocationReferenceIfMixedLineContainsLocationAndInvento void shouldCreatePieceWithLocationAndHoldingReferenceIfMixedLineContainsLocationAndInventoryIsInstanceOrNoneAndNoCreatedPieces( String lineType, String elecCreateInventory, String physCreateInventory, int elecQty1, int physQty2) { //given + String orderId = UUID.randomUUID().toString(); String lineId = UUID.randomUUID().toString(); String locationId1 = UUID.randomUUID().toString(); String holdingId = UUID.randomUUID().toString(); @@ -447,10 +458,11 @@ void shouldCreatePieceWithLocationAndHoldingReferenceIfMixedLineContainsLocation Eresource eresource = new Eresource().withCreateInventory(Eresource.CreateInventory.fromValue(elecCreateInventory)); Physical physical = new Physical().withCreateInventory(Physical.CreateInventory.fromValue(physCreateInventory)); - CompositePoLine line = new CompositePoLine().withId(lineId).withCost(cost).withLocations(List.of(location1, location2)) + CompositePoLine line = new CompositePoLine().withId(lineId).withPurchaseOrderId(orderId) + .withCost(cost).withLocations(List.of(location1, location2)) .withIsPackage(false).withEresource(eresource).withPhysical(physical) .withOrderFormat(CompositePoLine.OrderFormat.fromValue(lineType)); - CompositePurchaseOrder compOrder = new CompositePurchaseOrder().withCompositePoLines(List.of(line)); + CompositePurchaseOrder compOrder = new CompositePurchaseOrder().withId(orderId).withCompositePoLines(List.of(line)); Title title = new Title().withId(titleId).withTitle("test title"); doReturn(succeededFuture(null)).when(openCompositeOrderPieceService).openOrderUpdateInventory(any(CompositePurchaseOrder.class), @@ -458,7 +470,7 @@ void shouldCreatePieceWithLocationAndHoldingReferenceIfMixedLineContainsLocation doReturn(succeededFuture(Collections.emptyList())).when(pieceStorageService).getPiecesByPoLineId(line, requestContext); doReturn(succeededFuture(new Piece())).when(pieceStorageService).insertPiece(any(Piece.class), eq(requestContext)); doReturn(succeededFuture(null)).when(protectionService).isOperationRestricted(any(), any(ProtectedOperationType.class), eq(requestContext)); - doReturn(succeededFuture(compOrder)).when(purchaseOrderStorageService).getCompositeOrderByPoLineId(eq(lineId), eq(requestContext)); + doReturn(succeededFuture(compOrder)).when(purchaseOrderStorageService).getCompositeOrderById(eq(orderId), eq(requestContext)); doReturn(succeededFuture(title)).when(titlesService).getTitleById(titleId, requestContext); final ArgumentCaptor pieceArgumentCaptor = ArgumentCaptor.forClass(Piece.class); @@ -497,6 +509,7 @@ void shouldCreatePieceWithLocationAndHoldingReferenceIfMixedLineContainsLocation void shouldCreatePieceWithLocationAndHoldingReferenceIfMixedLineContainsLocationAndInventoryIsInstanceOrNoneAndExistPiecesWithItems( String lineType, String elecCreateInventory, String physCreateInventory, int elecQty1, int physQty2) { //given + String orderId = UUID.randomUUID().toString(); String lineId = UUID.randomUUID().toString(); String locationId = UUID.randomUUID().toString(); String holdingId = UUID.randomUUID().toString(); @@ -523,10 +536,11 @@ void shouldCreatePieceWithLocationAndHoldingReferenceIfMixedLineContainsLocation Eresource eresource = new Eresource().withCreateInventory(Eresource.CreateInventory.fromValue(elecCreateInventory)); Physical physical = new Physical().withCreateInventory(Physical.CreateInventory.fromValue(physCreateInventory)); - CompositePoLine line = new CompositePoLine().withId(lineId).withCost(cost).withLocations(List.of(elecLocation, physLocation)) + CompositePoLine line = new CompositePoLine().withId(lineId).withPurchaseOrderId(orderId) + .withCost(cost).withLocations(List.of(elecLocation, physLocation)) .withIsPackage(false).withEresource(eresource).withPhysical(physical) .withOrderFormat(CompositePoLine.OrderFormat.fromValue(lineType)); - CompositePurchaseOrder compOrder = new CompositePurchaseOrder().withCompositePoLines(List.of(line)); + CompositePurchaseOrder compOrder = new CompositePurchaseOrder().withId(orderId).withCompositePoLines(List.of(line)); Title title = new Title().withId(titleId).withTitle("test title"); doReturn(succeededFuture(null)).when(openCompositeOrderPieceService).openOrderUpdateInventory(any(CompositePurchaseOrder.class), @@ -534,7 +548,7 @@ void shouldCreatePieceWithLocationAndHoldingReferenceIfMixedLineContainsLocation doReturn(succeededFuture(Collections.emptyList())).when(pieceStorageService).getPiecesByPoLineId(line, requestContext); doReturn(succeededFuture(new Piece())).when(pieceStorageService).insertPiece(any(Piece.class), eq(requestContext)); doReturn(succeededFuture(null)).when(protectionService).isOperationRestricted(any(), any(ProtectedOperationType.class), eq(requestContext)); - doReturn(succeededFuture(compOrder)).when(purchaseOrderStorageService).getCompositeOrderByPoLineId(eq(lineId), eq(requestContext)); + doReturn(succeededFuture(compOrder)).when(purchaseOrderStorageService).getCompositeOrderById(eq(orderId), eq(requestContext)); doReturn(succeededFuture(title)).when(titlesService).getTitleById(titleId, requestContext); final ArgumentCaptor pieceArgumentCaptor = ArgumentCaptor.forClass(Piece.class); @@ -576,6 +590,49 @@ void shouldCreatePieceWithLocationAndHoldingReferenceIfMixedLineContainsLocation }); } + @Test + void shouldUpdatePieceWithLocationOfDifferentTenantAndUpdateReceivingTenantId() { + //given + var tenantId1 = "tenantId1"; + var tenantId2 = "tenantId2"; + var lineId = UUID.randomUUID().toString(); + var titleId = UUID.randomUUID().toString(); + var location1 = new Location().withLocationId(UUID.randomUUID().toString()).withTenantId(tenantId1).withQuantityPhysical(1).withQuantity(1); + var location2 = new Location().withLocationId(UUID.randomUUID().toString()).withTenantId(tenantId2).withQuantityPhysical(1).withQuantity(1); + var cost = new Cost().withQuantityPhysical(1).withQuantityElectronic(null); + var physical = new Physical().withCreateInventory(Physical.CreateInventory.INSTANCE); + var line = new CompositePoLine().withId(lineId).withCost(cost).withLocations(List.of(location2)) + .withIsPackage(false).withPhysical(physical).withOrderFormat(CompositePoLine.OrderFormat.PHYSICAL_RESOURCE); + var title = new Title().withId(titleId); + var pieceId = UUID.randomUUID().toString(); + var pieceBefore = new Piece().withId(pieceId).withLocationId(location1.getLocationId()).withReceivingTenantId(tenantId1) + .withPoLineId(lineId).withTitleId(titleId).withFormat(Piece.Format.PHYSICAL); + + doReturn(succeededFuture(null)).when(openCompositeOrderPieceService).openOrderUpdateInventory(any(CompositePurchaseOrder.class), + any(CompositePoLine.class), any(Piece.class), any(Boolean.class), eq(requestContext)); + doReturn(succeededFuture(null)).when(protectionService).isOperationRestricted(any(), any(ProtectedOperationType.class), eq(requestContext)); + doReturn(succeededFuture(null)).when(inventoryItemManager).updateItemWithPieceFields(any(Piece.class), eq(requestContext)); + doReturn(succeededFuture(title)).when(titlesService).getTitleById(titleId, requestContext); + doReturn(succeededFuture(List.of(pieceBefore))).when(pieceStorageService).getPiecesByPoLineId(line, requestContext); + doReturn(succeededFuture(pieceBefore)).when(pieceStorageService).getPieceById(eq(pieceId), eq(requestContext)); + doReturn(succeededFuture()).when(pieceStorageService).updatePiece(eq(pieceBefore), eq(requestContext)); + + // When + var updatedPieces = openCompositeOrderPieceService.handlePieces(line, titleId, List.of(), false, requestContext).result(); + // Then + var piecesLoc2 = updatedPieces.stream().filter(updatedPiece -> updatedPiece.getLocationId().equals(location2.getLocationId())).toList(); + assertEquals(1, piecesLoc2.size()); + piecesLoc2.forEach(updatedPiece -> { + assertNull(updatedPiece.getHoldingId()); + assertNull(updatedPiece.getItemId()); + assertEquals(pieceId, updatedPiece.getId()); + assertEquals(lineId, updatedPiece.getPoLineId()); + assertEquals(titleId, updatedPiece.getTitleId()); + assertEquals(tenantId2, updatedPiece.getReceivingTenantId()); + assertEquals(Piece.Format.PHYSICAL, updatedPiece.getFormat()); + }); + } + private List createPhysPiecesWithHoldingId(String lineId, String titleId, boolean withItem, Location location) { List pieces = new ArrayList<>(); for (int i = 0; i < location.getQuantityPhysical(); i++) { From 92a68458150eab2350d28ad0318b35174e4efbeb Mon Sep 17 00:00:00 2001 From: Saba-Zedginidze-EPAM <148070844+Saba-Zedginidze-EPAM@users.noreply.github.com> Date: Thu, 22 Aug 2024 18:41:04 +0400 Subject: [PATCH 2/2] [MODORDERS-1166] Add bindItemTenantId to Piece schema (#1003) * [MODORDERS-1166] Add bindItemTenantId to Piece schema * [MODORDERS-1166] Update acq-models pointer * [MODORDERS-1166] Update acq-models pointer --- ramls/acq-models | 2 +- .../java/org/folio/helper/BindHelper.java | 7 ++++--- .../rest/impl/CheckinReceivingApiTest.java | 20 ++++++++++++++----- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/ramls/acq-models b/ramls/acq-models index 25083ed83..f28215d2e 160000 --- a/ramls/acq-models +++ b/ramls/acq-models @@ -1 +1 @@ -Subproject commit 25083ed834962a4b9423b33b4b307f86a2e4918b +Subproject commit f28215d2ee2b2582f96f65b91c388f4ac7395f46 diff --git a/src/main/java/org/folio/helper/BindHelper.java b/src/main/java/org/folio/helper/BindHelper.java index 0f3ade1a8..225a5bfa7 100644 --- a/src/main/java/org/folio/helper/BindHelper.java +++ b/src/main/java/org/folio/helper/BindHelper.java @@ -76,7 +76,7 @@ public Future removeBinding(String pieceId, RequestContext requestContext) return pieceStorageService.getPieceById(pieceId, requestContext) .compose(piece -> { var bindItemId = piece.getBindItemId(); - piece.withBindItemId(null).withIsBound(false); + piece.withBindItemId(null).withBindItemTenantId(null).withIsBound(false); return removeForbiddenEntities(piece, requestContext) .map(v -> Collections.>singletonMap(null, List.of(piece))) .compose(piecesGroupedByPoLine -> storeUpdatedPieceRecords(piecesGroupedByPoLine, requestContext)) @@ -220,10 +220,11 @@ private Future updateItemStatus(BindPiecesHolder holder, Reque private Future createItemForPieces(BindPiecesHolder holder, RequestContext requestContext) { var bindPiecesCollection = holder.getBindPiecesCollection(); var poLineId = holder.getPoLineId(); + var bindItem = bindPiecesCollection.getBindItem(); logger.debug("createItemForPiece:: Trying to get poLine by id '{}'", poLineId); return purchaseOrderLineService.getOrderLineById(poLineId, requestContext) .map(PoLineCommonUtil::convertToCompositePoLine) - .compose(compPOL -> createInventoryObjects(compPOL, bindPiecesCollection.getInstanceId(), bindPiecesCollection.getBindItem(), requestContext)) + .compose(compPOL -> createInventoryObjects(compPOL, bindPiecesCollection.getInstanceId(), bindItem, requestContext)) .map(newItemId -> { // Move requests if requestsAction is TRANSFER, otherwise do nothing if (TRANSFER.equals(bindPiecesCollection.getRequestsAction())) { @@ -231,7 +232,7 @@ private Future createItemForPieces(BindPiecesHolder holder, Re inventoryItemRequestService.transferItemRequests(itemIds, newItemId, requestContext); } // Set new item ids for pieces and holder - holder.getPieces().forEach(piece -> piece.setBindItemId(newItemId)); + holder.getPieces().forEach(piece -> piece.withBindItemTenantId(bindItem.getTenantId()).setBindItemId(newItemId)); return holder.withBindItemId(newItemId); }); } diff --git a/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java b/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java index ba216d0e6..18c15f1fe 100644 --- a/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java +++ b/src/test/java/org/folio/rest/impl/CheckinReceivingApiTest.java @@ -1221,6 +1221,7 @@ void testBindPiecesToTitleWithItem() { var holdingId = "849241fa-4a14-4df5-b951-846dcd6cfc4d"; var receivingStatus = Piece.ReceivingStatus.UNRECEIVABLE; var format = Piece.Format.ELECTRONIC; + var tenantId = "tenantId"; var order = getMinimalContentCompositePurchaseOrder() .withWorkflowStatus(CompositePurchaseOrder.WorkflowStatus.OPEN); @@ -1249,7 +1250,8 @@ void testBindPiecesToTitleWithItem() { .withPoLineId(poLine.getId()) .withBindItem(getMinimalContentBindItem() .withLocationId(null) - .withHoldingId(holdingId)) + .withHoldingId(holdingId) + .withTenantId(tenantId)) .withBindPieceIds(pieceIds); var response = verifyPostResponse(ORDERS_BIND_ENDPOINT, JsonObject.mapFrom(bindPiecesCollection).encode(), @@ -1269,6 +1271,7 @@ void testBindPiecesToTitleWithItem() { .map(json -> json.mapTo(Piece.class)) .filter(piece -> pieceIds.contains(piece.getId())) .filter(piece -> piece.getBindItemId().equals(newItemId)) + .filter(piece -> piece.getBindItemTenantId().equals(tenantId)) .toList(); assertThat(pieceList.size(), is(2)); @@ -1306,6 +1309,8 @@ void testBindPiecesWithLocationIdOnly() { var receivingStatus = Piece.ReceivingStatus.UNRECEIVABLE; var format = Piece.Format.ELECTRONIC; + var tenantId = "tenantId"; + var order = getMinimalContentCompositePurchaseOrder() .withWorkflowStatus(CompositePurchaseOrder.WorkflowStatus.OPEN); var poLine = getMinimalContentCompositePoLine(order.getId()); @@ -1324,7 +1329,8 @@ void testBindPiecesWithLocationIdOnly() { var bindPiecesCollection = new BindPiecesCollection() .withPoLineId(poLine.getId()) .withBindItem(getMinimalContentBindItem() - .withLocationId(locationId)) + .withLocationId(locationId) + .withTenantId(tenantId)) .withBindPieceIds(pieceIds); var response = verifyPostResponse(ORDERS_BIND_ENDPOINT, JsonObject.mapFrom(bindPiecesCollection).encode(), @@ -1342,7 +1348,8 @@ void testBindPiecesWithLocationIdOnly() { var pieceList = pieceUpdates.stream().filter(pol -> { Piece piece = pol.mapTo(Piece.class); String pieceId = piece.getId(); - return Objects.equals(bindingPiece.getId(), pieceId); + return Objects.equals(bindingPiece.getId(), pieceId) + && Objects.equals(piece.getBindItemTenantId(), tenantId); }).toList(); assertThat(pieceList.size(), is(1)); @@ -1504,6 +1511,7 @@ void testRemovePieceBinding() { logger.info("=== Test DELETE Remove binding"); var holdingId = "849241fa-4a14-4df5-b951-846dcd6cfc4d"; + var tenantId = "tenantId"; var order = getMinimalContentCompositePurchaseOrder() .withWorkflowStatus(CompositePurchaseOrder.WorkflowStatus.OPEN); var poLine = getMinimalContentCompositePoLine(order.getId()); @@ -1531,7 +1539,8 @@ void testRemovePieceBinding() { .withPoLineId(poLine.getId()) .withBindItem(getMinimalContentBindItem() .withLocationId(null) - .withHoldingId(holdingId)) + .withHoldingId(holdingId) + .withTenantId(tenantId)) .withBindPieceIds(bindPieceIds); var bindResponse = verifyPostResponse(ORDERS_BIND_ENDPOINT, JsonObject.mapFrom(bindPiecesCollection).encode(), @@ -1543,7 +1552,6 @@ void testRemovePieceBinding() { assertThat(bindResponse.getBoundPieceIds(), is(bindPieceIds)); assertThat(bindResponse.getItemId(), notNullValue()); - var bindItemId = bindResponse.getItemId(); var url = String.format(ORDERS_BIND_ID_ENDPOINT, bindPiece1.getId()); verifyDeleteResponse(url, "", HttpStatus.HTTP_NO_CONTENT.toInt()); @@ -1561,10 +1569,12 @@ void testRemovePieceBinding() { var pieceBefore = pieceList.get(1); assertThat(pieceBefore.getIsBound(), is(true)); assertThat(pieceBefore.getBindItemId(), notNullValue()); + assertThat(pieceBefore.getBindItemTenantId(), notNullValue()); var pieceAfter = pieceList.get(0); assertThat(pieceAfter.getIsBound(), is(false)); assertThat(pieceAfter.getBindItemId(), nullValue()); + assertThat(pieceAfter.getReceivingTenantId(), nullValue()); } @Test