Skip to content

Commit

Permalink
MODORDERS-1090 - Quantity = 0 for electronic format orders (#901)
Browse files Browse the repository at this point in the history
RuslanLavrov authored Apr 19, 2024
1 parent 55d1907 commit a4eb741
Showing 3 changed files with 6 additions and 536 deletions.
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
## 12.9.0 - Unreleased
### Stories

### Bug Fixes

* [MODORDERS-1090](https://folio-org.atlassian.net/browse/MODORDERS-1090) - Quantity = 0 for electronic format orders

## 12.8.0 - Released (Quesnelia R1 2024)
This release focused on fixing several bugs as well as implement new features and upgrading dependent libraries
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@
import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
@@ -20,33 +19,21 @@
import org.folio.rest.jaxrs.model.CompositePoLine;
import org.folio.rest.jaxrs.model.CompositePurchaseOrder;
import org.folio.rest.jaxrs.model.EntityType;
import org.folio.rest.jaxrs.model.Location;
import org.folio.service.dataimport.PoLineImportProgressService;
import org.folio.service.dataimport.utils.DataImportUtils;
import org.folio.service.orders.PurchaseOrderLineService;
import org.folio.service.orders.PurchaseOrderStorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.folio.DataImportEventTypes.DI_ORDER_CREATED;
import static org.folio.rest.jaxrs.model.EntityType.HOLDINGS;
import static org.folio.rest.jaxrs.model.EntityType.INSTANCE;
import static org.folio.rest.jaxrs.model.EntityType.ITEM;
import static org.folio.rest.jaxrs.model.EntityType.ORDER;
import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.PHYSICAL_RESOURCE;
import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.ELECTRONIC_RESOURCE;
import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.P_E_MIX;
import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.OTHER;
import static org.folio.rest.jaxrs.model.ProfileSnapshotWrapper.ContentType.MAPPING_PROFILE;

@Component
@@ -57,41 +44,11 @@ public class OrderPostProcessingEventHandler implements EventHandler {

private static final String PO_LINE_KEY = "PO_LINE";
private static final String ID_FIELD = "id";
private static final String MATERIAL_TYPE_ID = "materialTypeId";
private static final String PERMANENT_LOCATION_ID = "permanentLocationId";
private static final String HOLDING_ID_FIELD = "holdingId";
private static final String P_E_MIX_UNSUPPORTED_MSG = "Open P/E mix orders do not support multiple item import";
private static final JsonArray EMPTY_JSON_ARRAY = new JsonArray();
private final PurchaseOrderHelper purchaseOrderHelper;
private final PurchaseOrderStorageService purchaseOrderStorageService;
private final Context vertxContext;
private final PoLineImportProgressService poLineImportProgressService;
private final PurchaseOrderLineService purchaseOrderLineService;
private static final EnumMap<CompositePoLine.OrderFormat, BiConsumer<CompositePoLine, String>> orderFormatToAdjustingMaterialType =
new EnumMap<>(CompositePoLine.OrderFormat.class);
private static final EnumMap<CompositePoLine.OrderFormat, BiConsumer<CompositePoLine, List<Location>>> orderFormatToAdjustingCostDetails =
new EnumMap<>(CompositePoLine.OrderFormat.class);
private static final EnumMap<CompositePoLine.OrderFormat, Function<Integer, Location>> orderFormatToLocation = new EnumMap<>(CompositePoLine.OrderFormat.class);

static {
orderFormatToAdjustingMaterialType.put(PHYSICAL_RESOURCE, (CompositePoLine pol, String materialType) -> pol.getPhysical().setMaterialType(materialType));
orderFormatToAdjustingMaterialType.put(ELECTRONIC_RESOURCE, (CompositePoLine pol, String materialType) -> pol.getEresource().setMaterialType(materialType));
orderFormatToAdjustingMaterialType.put(OTHER, (CompositePoLine pol, String materialType) -> pol.getPhysical().setMaterialType(materialType));
orderFormatToAdjustingMaterialType.put(P_E_MIX, (CompositePoLine pol, String materialType) -> {
pol.getPhysical().setMaterialType(materialType);
pol.getEresource().setMaterialType(materialType);
});

orderFormatToAdjustingCostDetails.put(PHYSICAL_RESOURCE, ((poLine, locations) -> poLine.getCost().setQuantityPhysical(getLocationsPhysicalQuantitySum(locations))));
orderFormatToAdjustingCostDetails.put(ELECTRONIC_RESOURCE, ((poLine, locations) -> poLine.getCost().setQuantityElectronic(getLocationsElectronicQuantitySum(locations))));
orderFormatToAdjustingCostDetails.put(OTHER, ((poLine, locations) -> poLine.getCost().setQuantityPhysical(getLocationsPhysicalQuantitySum(locations))));
orderFormatToAdjustingCostDetails.put(P_E_MIX, ((poLine, locations) -> poLine.getCost().setQuantityPhysical(getLocationsPhysicalQuantitySum(locations))));

orderFormatToLocation.put(PHYSICAL_RESOURCE, quantity -> new Location().withQuantityPhysical(quantity));
orderFormatToLocation.put(ELECTRONIC_RESOURCE, quantity -> new Location().withQuantityElectronic(quantity));
orderFormatToLocation.put(OTHER, quantity -> new Location().withQuantityPhysical(quantity));
orderFormatToLocation.put(P_E_MIX, quantity -> new Location().withQuantityPhysical(quantity));
}

@Autowired
public OrderPostProcessingEventHandler(PurchaseOrderHelper purchaseOrderHelper,
@@ -121,8 +78,7 @@ public CompletableFuture<DataImportEventPayload> handle(DataImportEventPayload d
CompositePoLine poLine = Json.decodeValue(payloadContext.get(PO_LINE_KEY), CompositePoLine.class);

LOGGER.info("handle:: jobExecutionId {}, poLineId {}, orderId {}", dataImportEventPayload.getJobExecutionId(), poLine.getId(), poLine.getPurchaseOrderId());
adjustLocationAndMaterialType(poLine, payloadContext)
.compose(adjustedPoLine -> ensurePoLineWithInstanceId(adjustedPoLine, dataImportEventPayload, requestContext))
ensurePoLineWithInstanceId(poLine, dataImportEventPayload, requestContext)
.compose(v -> poLineImportProgressService.trackProcessedPoLine(poLine.getPurchaseOrderId(), dataImportEventPayload.getTenant()))
.compose(poLinesImported -> Boolean.TRUE.equals(poLinesImported)
? openOrder(poLine, requestContext, dataImportEventPayload.getJobExecutionId())
@@ -139,46 +95,6 @@ public CompletableFuture<DataImportEventPayload> handle(DataImportEventPayload d
return future;
}

private Future<CompositePoLine> adjustLocationAndMaterialType(CompositePoLine poLine, HashMap<String, String> payloadContext) {
JsonArray holdingsAsJson = payloadContext.get(HOLDINGS.value()) != null ? new JsonArray(payloadContext.get(HOLDINGS.value())) : EMPTY_JSON_ARRAY;
JsonArray itemsAsJson = payloadContext.get(ITEM.value()) != null ? new JsonArray(payloadContext.get(ITEM.value())) : EMPTY_JSON_ARRAY;
CompositePoLine.OrderFormat orderFormat = poLine.getOrderFormat();

if (holdingsAsJson.size() > 0) {
List<Location> locations = holdingsAsJson.stream()
.map(holding -> constructLocation((JsonObject) holding, itemsAsJson, orderFormat))
.collect(Collectors.toList());
poLine.setLocations(locations);
orderFormatToAdjustingCostDetails.get(orderFormat).accept(poLine, locations);
}

if (itemsAsJson.size() > 0) {
if (orderFormat.equals(P_E_MIX) && itemsAsJson.size() > 1) {
return Future.failedFuture(P_E_MIX_UNSUPPORTED_MSG);
}
String materialType = itemsAsJson.getJsonObject(0).getString(MATERIAL_TYPE_ID);
orderFormatToAdjustingMaterialType.get(poLine.getOrderFormat()).accept(poLine, materialType);
}
return Future.succeededFuture(poLine);
}

private static Integer getLocationsPhysicalQuantitySum(List<Location> locations) {
return locations.stream().mapToInt(Location::getQuantityPhysical).sum();
}

private static Integer getLocationsElectronicQuantitySum(List<Location> locations) {
return locations.stream().mapToInt(Location::getQuantityElectronic).sum();
}

private Location constructLocation(JsonObject holding, JsonArray itemsAsJson, CompositePoLine.OrderFormat orderFormat) {
String locationId = holding.getString(PERMANENT_LOCATION_ID);
Integer quantity = (int) itemsAsJson.stream()
.map(item -> (JsonObject) item)
.filter(item -> StringUtils.equals(item.getString(HOLDING_ID_FIELD), holding.getString(ID_FIELD)))
.count();
return orderFormatToLocation.get(orderFormat).apply(quantity).withLocationId(locationId);
}

private Future<Void> openOrder(CompositePoLine poLine, RequestContext requestContext, String jobExecutionId) {
LOGGER.info("All poLines for order were processed, initializing order opening, orderId: {}, poLineNumber: {}, jobExecutionId: {} ",
poLine.getPurchaseOrderId(), poLine.getPoLineNumber(), jobExecutionId);
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@
import io.vertx.core.Future;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
@@ -28,7 +27,6 @@
import org.folio.rest.jaxrs.model.Event;
import org.folio.rest.jaxrs.model.FundDistribution;
import org.folio.rest.jaxrs.model.Location;
import org.folio.rest.jaxrs.model.Physical;
import org.folio.rest.jaxrs.model.Piece;
import org.folio.rest.jaxrs.model.ProfileSnapshotWrapper;
import org.folio.rest.jaxrs.model.Title;
@@ -78,12 +76,10 @@
import static org.folio.rest.jaxrs.model.ProfileSnapshotWrapper.ContentType.JOB_PROFILE;
import static org.folio.rest.jaxrs.model.ProfileSnapshotWrapper.ContentType.MAPPING_PROFILE;
import static org.folio.service.dataimport.handlers.CreateOrderEventHandler.OKAPI_PERMISSIONS_HEADER;
import static org.folio.service.inventory.InventoryManager.HOLDING_PERMANENT_LOCATION_ID;
import static org.folio.service.inventory.InventoryManager.ID;
import static org.folio.service.inventory.InventoryManager.ITEM_HOLDINGS_RECORD_ID;
import static org.folio.service.inventory.InventoryManager.ITEM_MATERIAL_TYPE_ID;
import static org.folio.service.inventory.InventoryManager.ITEM_PURCHASE_ORDER_LINE_IDENTIFIER;
import static org.folio.service.inventory.InventoryManagerTest.NEW_LOCATION_ID;
import static org.folio.service.inventory.InventoryManagerTest.OLD_LOCATION_ID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -235,453 +231,6 @@ public void shouldOpenOrderAndUseExistingInstanceHoldingsItemWhenAllPoLinesProce
verifyEncumbrancesOnPoUpdate(order.withCompositePoLines(List.of(poLine)));
}

@Test
public void shouldOpenOrderAndPoLineShouldContainLocationAndMaterialTypeFromCreatedHoldingsAndItemIfThoseExistForPhysicalResource(TestContext context)
throws InterruptedException {
CompositePoLine physicalPoLine = new CompositePoLine()
.withId(UUID.randomUUID().toString())
.withTitleOrPackage("poLine for data-import")
.withPurchaseOrderId(order.getId())
.withPoLineNumber("10000-1")
.withSource(CompositePoLine.Source.MARC)
.withOrderFormat(CompositePoLine.OrderFormat.PHYSICAL_RESOURCE)
.withPhysical(new Physical())
.withCost(new Cost().withCurrency("USD"));

JsonObject itemJson = new JsonObject()
.put(ID, ITEM_ID)
.put(HOLDING_ID_FIELD, HOLDINGS_ID)
.put(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER, poLine.getId())
.put(ITEM_MATERIAL_TYPE_ID, PHYSICAL_RESOURCE_MATERIAL_TYPE_ID);

JsonObject holdingJson = new JsonObject()
.put(ID, HOLDINGS_ID)
.put(HOLDING_PERMANENT_LOCATION_ID, NEW_LOCATION_ID);

JsonObject instanceJson = new JsonObject().put(ID_FIELD, INSTANCE_ID);

CompositePoLine updatedPoLine = shouldReturnUpdatedPoLine(physicalPoLine, instanceJson,
new JsonArray().add(holdingJson), new JsonArray().add(itemJson), context);

assertEquals(instanceJson.getString(ID_FIELD), updatedPoLine.getInstanceId());
assertEquals(itemJson.getString(ITEM_MATERIAL_TYPE_ID), updatedPoLine.getPhysical().getMaterialType());
Location location = updatedPoLine.getLocations().get(0);
assertEquals(holdingJson.getString(HOLDING_PERMANENT_LOCATION_ID), location.getLocationId());
assertEquals(1, location.getQuantityPhysical());
assertNull(location.getQuantityElectronic());
assertEquals(1, updatedPoLine.getCost().getQuantityPhysical());
assertNull(updatedPoLine.getCost().getQuantityElectronic());
}

@Test
public void shouldOpenOrderAndPoLineShouldContainLocationAndMaterialTypeFromCreatedHoldingsAndItemIfThoseExistForEResource(TestContext context)
throws InterruptedException {
CompositePoLine eresourcePoLine = new CompositePoLine()
.withId(UUID.randomUUID().toString())
.withTitleOrPackage("poLine for data-import")
.withPurchaseOrderId(order.getId())
.withPoLineNumber("10000-1")
.withSource(CompositePoLine.Source.MARC)
.withOrderFormat(CompositePoLine.OrderFormat.ELECTRONIC_RESOURCE)
.withEresource(new Eresource())
.withCost(new Cost().withCurrency("USD"));

JsonObject itemJson = new JsonObject()
.put(ID, ITEM_ID)
.put(HOLDING_ID_FIELD, HOLDINGS_ID)
.put(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER, poLine.getId())
.put(ITEM_MATERIAL_TYPE_ID, PHYSICAL_RESOURCE_MATERIAL_TYPE_ID);

JsonObject holdingJson = new JsonObject()
.put(ID, HOLDINGS_ID)
.put(HOLDING_PERMANENT_LOCATION_ID, NEW_LOCATION_ID);

JsonObject instanceJson = new JsonObject().put(ID_FIELD, INSTANCE_ID);

CompositePoLine updatedPoLine = shouldReturnUpdatedPoLine(eresourcePoLine, instanceJson, new JsonArray().add(holdingJson),
new JsonArray().add(itemJson), context);

assertEquals(instanceJson.getString(ID_FIELD), updatedPoLine.getInstanceId());
assertEquals(itemJson.getString(ITEM_MATERIAL_TYPE_ID), updatedPoLine.getEresource().getMaterialType());
Location location = updatedPoLine.getLocations().get(0);
assertEquals(holdingJson.getString(HOLDING_PERMANENT_LOCATION_ID), location.getLocationId());
assertEquals(1, location.getQuantityElectronic());
assertNull(location.getQuantityPhysical());
assertEquals(1, updatedPoLine.getCost().getQuantityElectronic());
assertNull(updatedPoLine.getCost().getQuantityPhysical());
}

@Test
public void shouldOpenOrderAndPoLineShouldContainLocationAndMaterialTypeFromCreatedHoldingsAndItemIfThoseExistForP_E_MIX(TestContext context)
throws InterruptedException {

FundDistribution fundDistribution = new FundDistribution()
.withFundId("fb7b70f1-b898-4924-a991-0e4b6312bb5f")
.withValue(100d)
.withDistributionType(FundDistribution.DistributionType.PERCENTAGE);

CompositePoLine P_E_MIX_PoLine = new CompositePoLine()
.withId(UUID.randomUUID().toString())
.withTitleOrPackage("poLine for data-import")
.withPurchaseOrderId(order.getId())
.withPoLineNumber("10000-1")
.withSource(CompositePoLine.Source.MARC)
.withOrderFormat(CompositePoLine.OrderFormat.P_E_MIX)
.withPhysical(new Physical())
.withEresource(new Eresource())
.withFundDistribution(List.of(fundDistribution))
.withCost(new Cost().withCurrency("USD").withQuantityElectronic(2));

JsonObject itemJson = new JsonObject()
.put(ID, ITEM_ID)
.put(HOLDING_ID_FIELD, HOLDINGS_ID)
.put(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER, poLine.getId())
.put(ITEM_MATERIAL_TYPE_ID, PHYSICAL_RESOURCE_MATERIAL_TYPE_ID);

JsonObject holdingJson = new JsonObject()
.put(ID, HOLDINGS_ID)
.put(HOLDING_PERMANENT_LOCATION_ID, NEW_LOCATION_ID);

JsonObject instanceJson = new JsonObject().put(ID_FIELD, INSTANCE_ID);

CompositePoLine updatedPoLine = shouldReturnUpdatedPoLine(P_E_MIX_PoLine, instanceJson,
new JsonArray().add(holdingJson), new JsonArray().add(itemJson), context);

assertEquals(instanceJson.getString(ID_FIELD), updatedPoLine.getInstanceId());
assertEquals(itemJson.getString(ITEM_MATERIAL_TYPE_ID), updatedPoLine.getPhysical().getMaterialType());
assertEquals(itemJson.getString(ITEM_MATERIAL_TYPE_ID), updatedPoLine.getEresource().getMaterialType());
Location location = updatedPoLine.getLocations().get(0);
assertEquals(holdingJson.getString(HOLDING_PERMANENT_LOCATION_ID), location.getLocationId());
assertEquals(1, location.getQuantityPhysical());
assertNull(location.getQuantityElectronic());
assertEquals(1, updatedPoLine.getCost().getQuantityPhysical());
assertEquals(2, updatedPoLine.getCost().getQuantityElectronic());
}

@Test
public void shouldOpenOrderAndPoLineShouldContainLocationAndMaterialTypeFromCreatedHoldingsAndItemIfThoseExistForOther(TestContext context)
throws InterruptedException {
CompositePoLine otherPoLine = new CompositePoLine()
.withId(UUID.randomUUID().toString())
.withTitleOrPackage("poLine for data-import")
.withPurchaseOrderId(order.getId())
.withPoLineNumber("10000-1")
.withSource(CompositePoLine.Source.MARC)
.withOrderFormat(CompositePoLine.OrderFormat.OTHER)
.withPhysical(new Physical())
.withCost(new Cost().withCurrency("USD"));

JsonObject itemJson = new JsonObject()
.put(ID, ITEM_ID)
.put(HOLDING_ID_FIELD, HOLDINGS_ID)
.put(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER, poLine.getId())
.put(ITEM_MATERIAL_TYPE_ID, PHYSICAL_RESOURCE_MATERIAL_TYPE_ID);

JsonObject holdingJson = new JsonObject()
.put(ID, HOLDINGS_ID)
.put(HOLDING_PERMANENT_LOCATION_ID, NEW_LOCATION_ID);

JsonObject instanceJson = new JsonObject().put(ID_FIELD, INSTANCE_ID);

CompositePoLine updatedPoLine = shouldReturnUpdatedPoLine(otherPoLine, instanceJson,
new JsonArray().add(holdingJson), new JsonArray().add(itemJson), context);

assertEquals(instanceJson.getString(ID_FIELD), updatedPoLine.getInstanceId());
assertEquals(itemJson.getString(ITEM_MATERIAL_TYPE_ID), updatedPoLine.getPhysical().getMaterialType());
Location location = updatedPoLine.getLocations().get(0);
assertEquals(holdingJson.getString(HOLDING_PERMANENT_LOCATION_ID), location.getLocationId());
assertEquals(1, location.getQuantityPhysical());
assertNull(location.getQuantityElectronic());
assertEquals(1, updatedPoLine.getCost().getQuantityPhysical());
assertNull(updatedPoLine.getCost().getQuantityElectronic());
}

@Test
public void shouldOpenOrderAndPoLineShouldContainLocationsFromCreatedMultipleHoldingsAndMaterialTypeFromFirstItem(TestContext context)
throws InterruptedException {
CompositePoLine eresourcePoLine = new CompositePoLine()
.withId(UUID.randomUUID().toString())
.withTitleOrPackage("poLine for data-import")
.withPurchaseOrderId(order.getId())
.withPoLineNumber("10000-1")
.withSource(CompositePoLine.Source.MARC)
.withOrderFormat(CompositePoLine.OrderFormat.ELECTRONIC_RESOURCE)
.withEresource(new Eresource())
.withCost(new Cost().withCurrency("USD"));

String holdingsId2 = UUID.randomUUID().toString();

JsonObject itemJson1 = new JsonObject()
.put(ID, ITEM_ID)
.put(HOLDING_ID_FIELD, HOLDINGS_ID)
.put(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER, poLine.getId())
.put(ITEM_MATERIAL_TYPE_ID, PHYSICAL_RESOURCE_MATERIAL_TYPE_ID);

JsonObject itemJson2 = new JsonObject()
.put(ID, ITEM_ID)
.put(HOLDING_ID_FIELD, holdingsId2)
.put(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER, poLine.getId())
.put(ITEM_MATERIAL_TYPE_ID, ELECTRONIC_RESOURCE_MATERIAL_TYPE_ID);

JsonObject holdingJson1 = new JsonObject()
.put(ID, HOLDINGS_ID)
.put(HOLDING_PERMANENT_LOCATION_ID, NEW_LOCATION_ID);

JsonObject holdingJson2 = new JsonObject()
.put(ID, holdingsId2)
.put(HOLDING_PERMANENT_LOCATION_ID, OLD_LOCATION_ID);

JsonObject instanceJson = new JsonObject().put(ID_FIELD, INSTANCE_ID);

JsonArray holdingsArray = new JsonArray().add(holdingJson1).add(holdingJson2);
JsonArray itemsArray = new JsonArray().add(itemJson1).add(itemJson2);

CompositePoLine updatedPoLine = shouldReturnUpdatedPoLine(eresourcePoLine, instanceJson, holdingsArray, itemsArray, context);

assertEquals(instanceJson.getString(ID_FIELD), updatedPoLine.getInstanceId());
assertEquals(itemJson1.getString(ITEM_MATERIAL_TYPE_ID), updatedPoLine.getEresource().getMaterialType());
assertEquals(holdingsArray.size(), updatedPoLine.getLocations().size());
for (int i = 0; i < updatedPoLine.getLocations().size(); i++) {
Location location = updatedPoLine.getLocations().get(i);
assertEquals(holdingsArray.getJsonObject(i).getString(HOLDING_PERMANENT_LOCATION_ID), location.getLocationId());
assertEquals(1, location.getQuantityElectronic());
}
}

@Test
public void shouldOpenOrderAndPoLineShouldContainLocationsQuantityDependsOnAmountOfItemsPerHoldingsPhysical(TestContext context)
throws InterruptedException {
CompositePoLine physicalPoLine = new CompositePoLine()
.withId(UUID.randomUUID().toString())
.withTitleOrPackage("poLine for data-import")
.withPurchaseOrderId(order.getId())
.withPoLineNumber("10000-1")
.withSource(CompositePoLine.Source.MARC)
.withOrderFormat(CompositePoLine.OrderFormat.PHYSICAL_RESOURCE)
.withPhysical(new Physical())
.withCost(new Cost().withCurrency("USD"));

String holdingsId2 = UUID.randomUUID().toString();

JsonObject itemJson1 = new JsonObject()
.put(ID, UUID.randomUUID().toString())
.put(HOLDING_ID_FIELD, HOLDINGS_ID)
.put(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER, poLine.getId())
.put(ITEM_MATERIAL_TYPE_ID, PHYSICAL_RESOURCE_MATERIAL_TYPE_ID);

JsonObject itemJson2 = new JsonObject()
.put(ID, UUID.randomUUID().toString())
.put(HOLDING_ID_FIELD, holdingsId2)
.put(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER, poLine.getId())
.put(ITEM_MATERIAL_TYPE_ID, ELECTRONIC_RESOURCE_MATERIAL_TYPE_ID);

JsonObject holdingJson1 = new JsonObject()
.put(ID, HOLDINGS_ID)
.put(HOLDING_PERMANENT_LOCATION_ID, NEW_LOCATION_ID);

JsonObject holdingJson2 = new JsonObject()
.put(ID, holdingsId2)
.put(HOLDING_PERMANENT_LOCATION_ID, OLD_LOCATION_ID);

JsonObject instanceJson = new JsonObject().put(ID_FIELD, INSTANCE_ID);

JsonArray holdingsArray = new JsonArray().add(holdingJson1).add(holdingJson2);
JsonArray itemsArray = new JsonArray().add(itemJson1).add(itemJson1).add(itemJson1).add(itemJson2).add(itemJson2);

CompositePoLine updatedPoLine = shouldReturnUpdatedPoLine(physicalPoLine, instanceJson, holdingsArray, itemsArray, context);

assertEquals(instanceJson.getString(ID_FIELD), updatedPoLine.getInstanceId());
assertEquals(itemJson1.getString(ITEM_MATERIAL_TYPE_ID), updatedPoLine.getPhysical().getMaterialType());
assertEquals(holdingsArray.size(), updatedPoLine.getLocations().size());
Location location1 = updatedPoLine.getLocations().get(0);
assertEquals(holdingsArray.getJsonObject(0).getString(HOLDING_PERMANENT_LOCATION_ID), location1.getLocationId());
assertEquals(3, location1.getQuantityPhysical());
assertNull(location1.getQuantityElectronic());

Location location2 = updatedPoLine.getLocations().get(1);
assertEquals(holdingsArray.getJsonObject(1).getString(HOLDING_PERMANENT_LOCATION_ID), location2.getLocationId());
assertEquals(2, location2.getQuantityPhysical());
assertNull(location1.getQuantityElectronic());

assertEquals(5, updatedPoLine.getCost().getQuantityPhysical());
assertNull(updatedPoLine.getCost().getQuantityElectronic());
}

@Test
public void shouldOpenOrderAndPoLineShouldContainLocationsQuantityDependsOnAmountOfItemsPerHoldingsEResources(TestContext context)
throws InterruptedException {
CompositePoLine eresourcePoLine = new CompositePoLine()
.withId(UUID.randomUUID().toString())
.withTitleOrPackage("poLine for data-import")
.withPurchaseOrderId(order.getId())
.withPoLineNumber("10000-1")
.withSource(CompositePoLine.Source.MARC)
.withOrderFormat(CompositePoLine.OrderFormat.ELECTRONIC_RESOURCE)
.withEresource(new Eresource())
.withCost(new Cost().withCurrency("USD"));

String holdingsId2 = UUID.randomUUID().toString();

JsonObject itemJson1 = new JsonObject()
.put(ID, UUID.randomUUID().toString())
.put(HOLDING_ID_FIELD, HOLDINGS_ID)
.put(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER, poLine.getId())
.put(ITEM_MATERIAL_TYPE_ID, PHYSICAL_RESOURCE_MATERIAL_TYPE_ID);

JsonObject itemJson2 = new JsonObject()
.put(ID, UUID.randomUUID().toString())
.put(HOLDING_ID_FIELD, holdingsId2)
.put(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER, poLine.getId())
.put(ITEM_MATERIAL_TYPE_ID, ELECTRONIC_RESOURCE_MATERIAL_TYPE_ID);

JsonObject holdingJson1 = new JsonObject()
.put(ID, HOLDINGS_ID)
.put(HOLDING_PERMANENT_LOCATION_ID, NEW_LOCATION_ID);

JsonObject holdingJson2 = new JsonObject()
.put(ID, holdingsId2)
.put(HOLDING_PERMANENT_LOCATION_ID, OLD_LOCATION_ID);

JsonObject instanceJson = new JsonObject().put(ID_FIELD, INSTANCE_ID);

JsonArray holdingsArray = new JsonArray().add(holdingJson1).add(holdingJson2);
JsonArray itemsArray = new JsonArray().add(itemJson1).add(itemJson1).add(itemJson1).add(itemJson2).add(itemJson2);

CompositePoLine updatedPoLine = shouldReturnUpdatedPoLine(eresourcePoLine, instanceJson, holdingsArray, itemsArray, context);

assertEquals(instanceJson.getString(ID_FIELD), updatedPoLine.getInstanceId());
assertEquals(itemJson1.getString(ITEM_MATERIAL_TYPE_ID), updatedPoLine.getEresource().getMaterialType());
assertEquals(holdingsArray.size(), updatedPoLine.getLocations().size());
Location location1 = updatedPoLine.getLocations().get(0);
assertEquals(holdingsArray.getJsonObject(0).getString(HOLDING_PERMANENT_LOCATION_ID), location1.getLocationId());
assertEquals(3, location1.getQuantityElectronic());
assertNull(location1.getQuantityPhysical());

Location location2 = updatedPoLine.getLocations().get(1);
assertEquals(holdingsArray.getJsonObject(1).getString(HOLDING_PERMANENT_LOCATION_ID), location2.getLocationId());
assertEquals(2, location2.getQuantityElectronic());
assertNull(location1.getQuantityPhysical());

assertEquals(5, updatedPoLine.getCost().getQuantityElectronic());
assertNull(updatedPoLine.getCost().getQuantityPhysical());
}

@Test
public void shouldReturnDiErrorIfP_E_MixOrderCreatingWithMultipleItems(TestContext context)
throws InterruptedException {
CompositePoLine P_E_MIX_PoLine = new CompositePoLine()
.withId(UUID.randomUUID().toString())
.withTitleOrPackage("poLine for data-import")
.withPurchaseOrderId(order.getId())
.withPoLineNumber("10000-1")
.withSource(CompositePoLine.Source.MARC)
.withOrderFormat(CompositePoLine.OrderFormat.P_E_MIX)
.withPhysical(new Physical())
.withEresource(new Eresource())
.withCost(new Cost().withCurrency("USD"));

JsonObject itemJson = new JsonObject()
.put(ID, ITEM_ID)
.put(HOLDING_ID_FIELD, HOLDINGS_ID)
.put(ITEM_PURCHASE_ORDER_LINE_IDENTIFIER, P_E_MIX_PoLine.getId())
.put(ITEM_MATERIAL_TYPE_ID, PHYSICAL_RESOURCE_MATERIAL_TYPE_ID);

JsonObject holdingJson = new JsonObject()
.put(ID, HOLDINGS_ID)
.put(HOLDING_PERMANENT_LOCATION_ID, NEW_LOCATION_ID);

JsonObject instanceJson = new JsonObject().put(ID_FIELD, INSTANCE_ID);

JsonArray holdingsJson = new JsonArray().add(holdingJson);
JsonArray itemsJson = new JsonArray().add(itemJson).add(itemJson);

CompositePoLine mockPoLine = JsonObject.mapFrom(P_E_MIX_PoLine).mapTo(CompositePoLine.class);
mockPoLine.setInstanceId(instanceJson.getString(ID_FIELD));

ProfileSnapshotWrapper profileSnapshotWrapper = buildProfileSnapshotWrapper(jobProfile, actionProfile, mappingProfile);
addMockEntry(JOB_PROFILE_SNAPSHOTS_MOCK, profileSnapshotWrapper);
addMockEntry(PURCHASE_ORDER_STORAGE, order);
addMockEntry(PO_LINES_STORAGE, mockPoLine);

DataImportEventPayload dataImportEventPayload = new DataImportEventPayload()
.withCurrentNode(profileSnapshotWrapper.getChildSnapshotWrappers().get(0).getChildSnapshotWrappers().get(0))
.withEventType(DI_ORDER_CREATED_READY_FOR_POST_PROCESSING.value())
.withTenant(TENANT_ID)
.withOkapiUrl(OKAPI_URL)
.withToken(TOKEN)
.withContext(new HashMap<>() {{
put(JOB_PROFILE_SNAPSHOT_ID_KEY, profileSnapshotWrapper.getId());
put(INSTANCE.value(), instanceJson.encodePrettily());
put(ITEM.value(), itemsJson.encode());
put(HOLDINGS.value(), holdingsJson.encodePrettily());
put(ORDER.value(), Json.encodePrettily(order));
put(PO_LINE_KEY, Json.encodePrettily(P_E_MIX_PoLine));
}});

CompletableFuture<Object> future = new CompletableFuture<>();
PoLineImportProgressService polProgressService = getBeanFromSpringContext(vertx, PoLineImportProgressService.class);
polProgressService.savePoLinesAmountPerOrder(order.getId(), 2, TENANT_ID)
.compose(v -> polProgressService.trackProcessedPoLine(order.getId(), TENANT_ID))
.onComplete(context.asyncAssertSuccess(v -> future.complete(null)));

SendKeyValues<String, String> request = prepareKafkaRequest(dataImportEventPayload);
future.join();

// when
kafkaCluster.send(request);
observeEvent(DI_ERROR.value());
}

private CompositePoLine shouldReturnUpdatedPoLine(CompositePoLine poLine, JsonObject instanceJson, JsonArray holdingsJson,
JsonArray itemsJson, TestContext context) throws InterruptedException {
CompositePoLine mockPoLine = JsonObject.mapFrom(poLine).mapTo(CompositePoLine.class);
mockPoLine.setInstanceId(instanceJson.getString(ID_FIELD));

ProfileSnapshotWrapper profileSnapshotWrapper = buildProfileSnapshotWrapper(jobProfile, actionProfile, mappingProfile);
addMockEntry(JOB_PROFILE_SNAPSHOTS_MOCK, profileSnapshotWrapper);
addMockEntry(PURCHASE_ORDER_STORAGE, order);
addMockEntry(PO_LINES_STORAGE, mockPoLine);

DataImportEventPayload dataImportEventPayload = new DataImportEventPayload()
.withCurrentNode(profileSnapshotWrapper.getChildSnapshotWrappers().get(0).getChildSnapshotWrappers().get(0))
.withEventType(DI_ORDER_CREATED_READY_FOR_POST_PROCESSING.value())
.withTenant(TENANT_ID)
.withOkapiUrl(OKAPI_URL)
.withToken(TOKEN)
.withContext(new HashMap<>() {{
put(JOB_PROFILE_SNAPSHOT_ID_KEY, profileSnapshotWrapper.getId());
put(INSTANCE.value(), instanceJson.encodePrettily());
put(ITEM.value(), itemsJson.encode());
put(HOLDINGS.value(), holdingsJson.encodePrettily());
put(ORDER.value(), Json.encodePrettily(order));
put(PO_LINE_KEY, Json.encodePrettily(poLine));
}});

CompletableFuture<Object> future = new CompletableFuture<>();
PoLineImportProgressService polProgressService = getBeanFromSpringContext(vertx, PoLineImportProgressService.class);
polProgressService.savePoLinesAmountPerOrder(order.getId(), 2, TENANT_ID)
.compose(v -> polProgressService.trackProcessedPoLine(order.getId(), TENANT_ID))
.onComplete(context.asyncAssertSuccess(v -> future.complete(null)));

SendKeyValues<String, String> request = prepareKafkaRequest(dataImportEventPayload);
future.join();

// when
kafkaCluster.send(request);

// then
DataImportEventPayload eventPayload = observeEvent(DI_COMPLETED.value());
assertEquals(DI_ORDER_CREATED.value(), eventPayload.getEventsChain().get(eventPayload.getEventsChain().size() - 1));
verifyPoLine(eventPayload);

assertNull(getCreatedInstances());
assertNull(getCreatedHoldings());
assertNull(getCreatedItems());

List<JsonObject> updatedPoLines = MockServer.getRqRsEntries(HttpMethod.PUT, PO_LINES_STORAGE);
return updatedPoLines.get(0).mapTo(CompositePoLine.class);
}

@Test
public void shouldNotUpdateOrderStatusToOpenWhenNotAllPoLinesProcessed(TestContext context) throws InterruptedException {
// given

0 comments on commit a4eb741

Please sign in to comment.