From 88ac2132469353c4ee5641b8cacb625e50d7dd2a Mon Sep 17 00:00:00 2001 From: Ruslan Lavrov <47384893+RuslanLavrov@users.noreply.github.com> Date: Wed, 22 May 2024 02:07:52 +0300 Subject: [PATCH] MODORDERS-1102 - Order lines search results do not display fund code for orders created by data import (#943) * Added logic to set poLine fundCode based on fundId populated during poLine mapping * Deleted redundant call of order mapping process --- NEWS.md | 1 + .../handlers/CreateOrderEventHandler.java | 39 +++++++++++- .../handlers/CreateOrderEventHandlerTest.java | 60 ++++++++++++++++++- 3 files changed, 98 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 42add23b4..47bd58b41 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,7 @@ ### Bug Fixes * [MODORDERS-1090](https://folio-org.atlassian.net/browse/MODORDERS-1090) - Quantity = 0 for electronic format orders +* [MODORDERS-1102](https://folio-org.atlassian.net/browse/MODORDERS-1102) - Order lines search results do not display fund code for orders created by data import ## 12.8.0 - Released (Quesnelia R1 2024) This release focused on fixing several bugs as well as implement new features and upgrading dependent libraries diff --git a/src/main/java/org/folio/service/dataimport/handlers/CreateOrderEventHandler.java b/src/main/java/org/folio/service/dataimport/handlers/CreateOrderEventHandler.java index ce16da459..013439283 100644 --- a/src/main/java/org/folio/service/dataimport/handlers/CreateOrderEventHandler.java +++ b/src/main/java/org/folio/service/dataimport/handlers/CreateOrderEventHandler.java @@ -5,6 +5,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; import static org.folio.ActionProfile.Action.CREATE; import static org.folio.ActionProfile.FolioRecord.HOLDINGS; import static org.folio.ActionProfile.FolioRecord.INSTANCE; @@ -32,6 +33,7 @@ import java.time.Period; import java.time.ZoneId; import java.time.ZoneOffset; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -45,6 +47,7 @@ import java.util.stream.Collectors; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.IterableUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -110,6 +113,9 @@ public class CreateOrderEventHandler implements EventHandler { private static final String POL_ERESOURCE_FIELD = "eresource"; private static final String POL_PHYSICAL_FIELD = "physical"; private static final String POL_CREATE_INVENTORY_FIELD = "createInventory"; + private static final String POL_FUND_DISTRIBUTION_FIELD = "fundDistribution"; + private static final String POL_FUND_CODE_FIELD = "code"; + private static final String POL_FUND_ID_FIELD = "fundId"; private static final String PO_LINE_KEY = "PO_LINE"; private static final String RECORD_ID_HEADER = "recordId"; private static final String JOB_PROFILE_SNAPSHOT_ID_KEY = "JOB_PROFILE_SNAPSHOT_ID"; @@ -119,6 +125,8 @@ public class CreateOrderEventHandler implements EventHandler { private static final String WORKFLOW_STATUS_PATH = "order.po.workflowStatus"; private static final String PO_LINE_ORDER_ID_KEY = "purchaseOrderId"; private static final String DEFAULT_PO_LINES_LIMIT = "1"; + private static final char LEFT_BRACKET = '('; + private static final char RIGHT_BRACKET = ')'; private final PurchaseOrderHelper purchaseOrderHelper; private final PurchaseOrderLineHelper poLineHelper; @@ -184,7 +192,6 @@ public CompletableFuture handle(DataImportEventPayload d future.completeExceptionally(result.cause()); } else { Optional poLinesLimitOptional = extractPoLinesLimit(dataImportEventPayload); - MappingManager.map(dataImportEventPayload, new MappingContext()); RequestContext requestContext = new RequestContext(Vertx.currentContext(), okapiHeaders); Future tenantConfigFuture = configurationEntriesCache.loadConfiguration(ORDER_CONFIG_MODULE_NAME, requestContext); @@ -508,6 +515,7 @@ private Future prepareMappingResult(DataImportEventPayload dataImportEvent JsonObject orderJson = mappingResult.getJsonObject(MAPPING_RESULT_FIELD).getJsonObject(ORDER_FIELD); JsonObject poLineJson = mappingResult.getJsonObject(MAPPING_RESULT_FIELD).getJsonObject(PO_LINES_FIELD); calculateActivationDue(poLineJson); + ensureFundCode(poLineJson, dataImportEventPayload); dataImportEventPayload.getContext().put(ORDER.value(), orderJson.encode()); if (WorkflowStatus.OPEN.value().equals(orderJson.getString(ORDER_STATUS_FIELD))) { @@ -533,6 +541,35 @@ private void calculateActivationDue(JsonObject poLineJson) { } } + private void ensureFundCode(JsonObject poLineJson, DataImportEventPayload dataImportEventPayload) { + if (!IterableUtils.isEmpty(poLineJson.getJsonArray(POL_FUND_DISTRIBUTION_FIELD))) { + Map idToFundName = extractFundsData(dataImportEventPayload); + + poLineJson.getJsonArray(POL_FUND_DISTRIBUTION_FIELD).stream() + .map(JsonObject.class::cast) + .filter(fundDistributionJson -> isNotEmpty(fundDistributionJson.getString(POL_FUND_ID_FIELD))) + .forEach(fundDistribution -> fundDistribution.put(POL_FUND_CODE_FIELD, determineFundCode(fundDistribution, idToFundName))); + } + } + + private Map extractFundsData(DataImportEventPayload dataImportEventPayload) { + ProfileSnapshotWrapper mappingProfileWrapper = dataImportEventPayload.getCurrentNode(); + MappingProfile mappingProfile = ObjectMapperTool.getMapper().convertValue(mappingProfileWrapper.getContent(), MappingProfile.class); + + return mappingProfile.getMappingDetails().getMappingFields().stream() + .filter(mappingRule -> POL_FUND_DISTRIBUTION_FIELD.equals(mappingRule.getName()) && !mappingRule.getSubfields().isEmpty()) + .flatMap(mappingRule -> mappingRule.getSubfields().get(0).getFields().stream()) + .filter(mappingRule -> POL_FUND_ID_FIELD.equals(mappingRule.getName())) + .map(mappingRule -> ((Map) mappingRule.getAcceptedValues())) + .findAny() + .orElse(Collections.emptyMap()); + } + + private String determineFundCode(JsonObject fundDistribution, Map idToFundName) { + String fundName = idToFundName.get(fundDistribution.getString(POL_FUND_ID_FIELD)); + return fundName.substring(fundName.lastIndexOf(LEFT_BRACKET) + 1, fundName.lastIndexOf(RIGHT_BRACKET)); + } + private Future overrideCreateInventoryField(JsonObject poLineJson, DataImportEventPayload dataImportEventPayload) { String profileSnapshotId = dataImportEventPayload.getContext().get(JOB_PROFILE_SNAPSHOT_ID_KEY); Map headers = DataImportUtils.extractOkapiHeaders(dataImportEventPayload); diff --git a/src/test/java/org/folio/service/dataimport/handlers/CreateOrderEventHandlerTest.java b/src/test/java/org/folio/service/dataimport/handlers/CreateOrderEventHandlerTest.java index a48772fed..69825015a 100644 --- a/src/test/java/org/folio/service/dataimport/handlers/CreateOrderEventHandlerTest.java +++ b/src/test/java/org/folio/service/dataimport/handlers/CreateOrderEventHandlerTest.java @@ -37,6 +37,7 @@ import org.folio.rest.jaxrs.model.MappingRule; import org.folio.rest.jaxrs.model.Physical; import org.folio.rest.jaxrs.model.ProfileSnapshotWrapper; +import org.folio.rest.jaxrs.model.RepeatableSubfieldMapping; import org.folio.service.dataimport.PoLineImportProgressService; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; @@ -91,6 +92,8 @@ import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.P_E_MIX; import static org.folio.rest.jaxrs.model.EntityType.MARC_BIBLIOGRAPHIC; import static org.folio.rest.jaxrs.model.EntityType.ORDER; +import static org.folio.rest.jaxrs.model.FundDistribution.DistributionType.PERCENTAGE; +import static org.folio.rest.jaxrs.model.MappingRule.RepeatableFieldAction.EXTEND_EXISTING; import static org.folio.rest.jaxrs.model.ProfileSnapshotWrapper.ContentType.ACTION_PROFILE; import static org.folio.rest.jaxrs.model.ProfileSnapshotWrapper.ContentType.JOB_PROFILE; import static org.folio.rest.jaxrs.model.ProfileSnapshotWrapper.ContentType.MAPPING_PROFILE; @@ -578,7 +581,7 @@ public void shouldCreatePendingOrderAndPublishDiCompletedEventWhenOpenStatusSpec assertEquals(DI_ORDER_CREATED.value(), eventPayload.getEventsChain().get(eventPayload.getEventsChain().size() - 1)); CompositePurchaseOrder order = Json.decodeValue(eventPayload.getContext().get(ActionProfile.FolioRecord.ORDER.value()), CompositePurchaseOrder.class); - assertEquals(order.getApproved(), Boolean.FALSE); + assertEquals(Boolean.FALSE, order.getApproved()); verifyOrder(eventPayload); verifyPoLine(eventPayload); @@ -1501,6 +1504,61 @@ public void shouldCreatePendingOrderAndNotMapVendorMaterialSupplierAndAccessProv assertNull(poLine.getEresource()); } + @Test + public void shouldCreatePoLineWithFundIdAndFundCode() throws InterruptedException { + // given + String expectedFundId = "7fbd5d84-62d1-44c6-9c45-6cb173998bbd"; + String expectedFundCode = "AFRICAHIST"; + double expectedDistributionValue = 100; + + MappingRule fundDistributionsRule = new MappingRule() + .withName("fundDistribution") + .withPath("order.poLine.fundDistribution[]") + .withEnabled("true") + .withRepeatableFieldAction(EXTEND_EXISTING) + .withSubfields(List.of(new RepeatableSubfieldMapping() + .withOrder(0) + .withPath("order.poLine.fundDistribution[]") + .withFields(List.of( + new MappingRule().withPath("order.poLine.fundDistribution[].fundId").withName("fundId") + .withValue("\"African (History) (AFRICAHIST)\"") + .withAcceptedValues(new HashMap<>(Map.of(expectedFundId, "African (History) (AFRICAHIST)"))), + new MappingRule().withPath("order.poLine.fundDistribution[].value").withValue("\"100\""), + new MappingRule().withPath("order.poLine.fundDistribution[].distributionType").withValue("\"percentage\""))) + )); + + mappingProfile.getMappingDetails().getMappingFields().add(fundDistributionsRule); + ProfileSnapshotWrapper profileSnapshotWrapper = buildProfileSnapshotWrapper(jobProfile, actionProfile, mappingProfile); + addMockEntry(JOB_PROFILE_SNAPSHOTS_MOCK, profileSnapshotWrapper); + + DataImportEventPayload dataImportEventPayload = new DataImportEventPayload() + .withJobExecutionId(jobExecutionJson.getString(ID_FIELD)) + .withEventType(DI_INCOMING_MARC_BIB_FOR_ORDER_PARSED.value()) + .withTenant(TENANT_ID) + .withOkapiUrl(OKAPI_URL) + .withToken(TOKEN) + .withContext(new HashMap<>() {{ + put(MARC_BIBLIOGRAPHIC.value(), Json.encode(record)); + put(JOB_PROFILE_SNAPSHOT_ID_KEY, profileSnapshotWrapper.getId()); + }}); + + SendKeyValues request = prepareKafkaRequest(dataImportEventPayload); + + // when + kafkaCluster.send(request); + + // then + DataImportEventPayload eventPayload = observeEvent(DI_COMPLETED.value()); + verifyOrder(eventPayload); + CompositePoLine createdPoLine = verifyPoLine(eventPayload); + assertNotNull(createdPoLine.getFundDistribution()); + assertEquals(1, createdPoLine.getFundDistribution().size()); + assertEquals(expectedFundId, createdPoLine.getFundDistribution().get(0).getFundId()); + assertEquals(expectedFundCode, createdPoLine.getFundDistribution().get(0).getCode()); + assertEquals(expectedDistributionValue, createdPoLine.getFundDistribution().get(0).getValue()); + assertEquals(PERCENTAGE, createdPoLine.getFundDistribution().get(0).getDistributionType()); + } + @Test public void shouldReturnFailedByDuplicateEventExceptionFutureWhenRecordIdIsDuplicated(TestContext context) { Async async = context.async();