diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json
index 8aa900318..d28607e6c 100644
--- a/descriptors/ModuleDescriptor-template.json
+++ b/descriptors/ModuleDescriptor-template.json
@@ -106,6 +106,10 @@
"id": "orders-storage.po-lines",
"version": "12.1"
},
+ {
+ "id": "orders-storage.pieces",
+ "version": "5.0"
+ },
{
"id": "organizations.organizations",
"version": "1.2"
diff --git a/folio-export-common b/folio-export-common
index dcc732532..839deafc2 160000
--- a/folio-export-common
+++ b/folio-export-common
@@ -1 +1 @@
-Subproject commit dcc732532ea07c4f90f7d999ce9c3126c331817a
+Subproject commit 839deafc288914ba7bcc58a8c598b369f4af466c
diff --git a/pom.xml b/pom.xml
index 9d16a573f..11d0fe7f2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -59,6 +59,7 @@
5.7.1
12.1
2.9.2
+ 0.8.3
2.4.0
@@ -205,7 +206,7 @@
io.xlate
staedi
- 1.24.0
+ 1.25.2
@@ -250,6 +251,12 @@
pom
+
+ one.util
+ streamex
+ ${streamex.version}
+
+
diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/CompositePOConverter.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/CompositePOConverter.java
index d94c95dcf..0b7e8b92a 100644
--- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/CompositePOConverter.java
+++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/CompositePOConverter.java
@@ -7,11 +7,14 @@
import org.folio.dew.batch.acquisitions.edifact.services.ConfigurationService;
import org.folio.dew.domain.dto.CompositePoLine;
import org.folio.dew.domain.dto.CompositePurchaseOrder;
+import org.folio.dew.domain.dto.Piece;
import org.folio.dew.domain.dto.acquisitions.edifact.EdiFileConfig;
import org.folio.dew.error.NotFoundException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.Map;
public class CompositePOConverter {
private static final String RUSH_ORDER = "224";
@@ -25,7 +28,8 @@ public CompositePOConverter(CompositePOLineConverter compositePOLineConverter, C
this.configurationService = configurationService;
}
- public void convertPOtoEdifact(EDIStreamWriter writer, CompositePurchaseOrder compPO, EdiFileConfig ediFileConfig) throws EDIStreamException {
+ public void convertPOtoEdifact(EDIStreamWriter writer, CompositePurchaseOrder compPO,
+ Map> poLineToPieces, EdiFileConfig ediFileConfig) throws EDIStreamException {
int messageSegmentCount = 0;
messageSegmentCount++;
@@ -65,7 +69,8 @@ public void convertPOtoEdifact(EDIStreamWriter writer, CompositePurchaseOrder co
int totalNumberOfLineItems = 0;
for (CompositePoLine poLine : compPO.getCompositePoLines()) {
int quantityOrdered = getPoLineQuantityOrdered(poLine);
- int segments = compositePOLineConverter.convertPOLine(poLine, writer, ++totalNumberOfLineItems, quantityOrdered);
+ var pieces = poLineToPieces.getOrDefault(poLine.getId(), List.of());
+ int segments = compositePOLineConverter.convertPOLine(poLine, pieces, writer, ++totalNumberOfLineItems, quantityOrdered);
messageSegmentCount += segments;
totalQuantity += quantityOrdered;
}
diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/CompositePOLineConverter.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/CompositePOLineConverter.java
index f4b354e96..cb09bac3e 100644
--- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/CompositePOLineConverter.java
+++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/CompositePOLineConverter.java
@@ -1,12 +1,17 @@
package org.folio.dew.batch.acquisitions.edifact;
+import static org.folio.dew.domain.dto.ReferenceNumberItem.RefNumberTypeEnum.ORDER_REFERENCE_NUMBER;
+
import javax.money.CurrencyUnit;
import javax.money.Monetary;
import javax.money.MonetaryAmount;
import io.xlate.edi.stream.EDIStreamException;
import io.xlate.edi.stream.EDIStreamWriter;
-import liquibase.util.StringUtil;
+import one.util.streamex.StreamEx;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
import org.folio.dew.batch.acquisitions.edifact.services.ExpenseClassService;
import org.folio.dew.batch.acquisitions.edifact.services.HoldingService;
import org.folio.dew.batch.acquisitions.edifact.services.IdentifierTypeService;
@@ -17,16 +22,18 @@
import org.folio.dew.domain.dto.Cost;
import org.folio.dew.domain.dto.FundDistribution;
import org.folio.dew.domain.dto.Location;
+import org.folio.dew.domain.dto.Piece;
import org.folio.dew.domain.dto.ProductIdentifier;
import org.folio.dew.domain.dto.ReferenceNumberItem;
+import org.folio.dew.domain.dto.VendorDetail;
import org.javamoney.moneta.Money;
-import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
public class CompositePOLineConverter {
private static final int MAX_CHARS_PER_LINE = 70;
@@ -44,18 +51,22 @@ public class CompositePOLineConverter {
private static final String EN_PRODUCT_ID_QUALIFIER = "EN";
private static final int EAN_IDENTIFIER_LENGTH = 13;
- @Autowired
- private IdentifierTypeService identifierTypeService;
- @Autowired
- private MaterialTypeService materialTypeService;
- @Autowired
- private ExpenseClassService expenseClassService;
- @Autowired
- private LocationService locationService;
- @Autowired
- private HoldingService holdingService;
-
- public int convertPOLine(CompositePoLine poLine, EDIStreamWriter writer, int currentLineNumber, int quantityOrdered) throws EDIStreamException {
+ private final IdentifierTypeService identifierTypeService;
+ private final MaterialTypeService materialTypeService;
+ private final ExpenseClassService expenseClassService;
+ private final LocationService locationService;
+ private final HoldingService holdingService;
+
+ public CompositePOLineConverter(IdentifierTypeService identifierTypeService, MaterialTypeService materialTypeService,
+ ExpenseClassService expenseClassService, LocationService locationService, HoldingService holdingService) {
+ this.identifierTypeService = identifierTypeService;
+ this.materialTypeService = materialTypeService;
+ this.expenseClassService = expenseClassService;
+ this.locationService = locationService;
+ this.holdingService = holdingService;
+ }
+
+ public int convertPOLine(CompositePoLine poLine, List pieces, EDIStreamWriter writer, int currentLineNumber, int quantityOrdered) throws EDIStreamException {
int messageSegmentCount = 0;
Map productTypeProductIdentifierMap = new HashMap<>();
@@ -82,6 +93,14 @@ public int convertPOLine(CompositePoLine poLine, EDIStreamWriter writer, int cur
writeTitle(titlePart, writer);
}
+ for (Piece piece : pieces) {
+ var pieceDetails = getPieceDetails(piece);
+ if (StringUtils.isNotBlank(pieceDetails)) {
+ messageSegmentCount++;
+ writePiece(pieceDetails, writer);
+ }
+ }
+
if (poLine.getPublisher() != null) {
messageSegmentCount++;
writePublisher(poLine.getPublisher(), writer);
@@ -93,13 +112,13 @@ public int convertPOLine(CompositePoLine poLine, EDIStreamWriter writer, int cur
}
String physicalMaterial = getPhysicalMaterial(poLine);
- if (StringUtil.isNotEmpty(physicalMaterial)) {
+ if (StringUtils.isNotEmpty(physicalMaterial)) {
messageSegmentCount++;
writeMaterialType(physicalMaterial, writer);
}
String electronicMaterial = getElectronicMaterial(poLine);
- if (StringUtil.isNotEmpty(electronicMaterial)) {
+ if (StringUtils.isNotEmpty(electronicMaterial)) {
messageSegmentCount++;
writeMaterialType(electronicMaterial, writer);
}
@@ -121,7 +140,7 @@ public int convertPOLine(CompositePoLine poLine, EDIStreamWriter writer, int cur
messageSegmentCount++;
writePoLineCurrency(poLine, writer);
- if (poLine.getVendorDetail() != null && StringUtil.isNotEmpty(poLine.getVendorDetail().getInstructions())) {
+ if (poLine.getVendorDetail() != null && StringUtils.isNotEmpty(poLine.getVendorDetail().getInstructions())) {
messageSegmentCount++;
writeInstructionsToVendor(poLine.getVendorDetail().getInstructions(), writer);
}
@@ -141,16 +160,25 @@ public int convertPOLine(CompositePoLine poLine, EDIStreamWriter writer, int cur
writeFundCode(getFundAndExpenseClass(fundDistribution), writer);
}
- if (poLine.getVendorDetail() != null && referenceQuantity < MAX_NUMBER_OF_REFS) {
- for (ReferenceNumberItem number : poLine.getVendorDetail().getReferenceNumbers()) {
- if (referenceQuantity >= MAX_NUMBER_OF_REFS) {
- break;
- }
- if (number.getRefNumber() != null) {
- referenceQuantity++;
- messageSegmentCount++;
- writeVendorReferenceNumber(number.getRefNumber(), writer);
- }
+ var referenceNumbers = getVendorReferenceNumbers(poLine);
+ // Non-empty pieces list is a sign that the export is for claims
+ if (CollectionUtils.isNotEmpty(pieces)) {
+ // Vendor order number is a required field for claims export, it must be included regardless of the number of references
+ var vendorOrderNumber = getAndRemoveVendorOrderNumber(referenceNumbers);
+ if (vendorOrderNumber != null) {
+ messageSegmentCount++;
+ writeVendorOrderNumber(vendorOrderNumber.getRefNumber(), writer);
+ }
+ }
+
+ for (ReferenceNumberItem number : getVendorReferenceNumbers(poLine)) {
+ if (referenceQuantity >= MAX_NUMBER_OF_REFS) {
+ break;
+ }
+ if (number.getRefNumber() != null) {
+ referenceQuantity++;
+ messageSegmentCount++;
+ writeVendorReferenceNumber(number.getRefNumber(), writer);
}
}
@@ -245,6 +273,19 @@ private void writeTitle(String titlePart, EDIStreamWriter writer) throws EDIStre
.writeEndSegment();
}
+ private void writePiece(String pieceDetails, EDIStreamWriter writer) throws EDIStreamException {
+ writer.writeStartSegment("IMD")
+ .writeElement("L")
+ .writeElement("080")
+ .writeStartElement()
+ .writeComponent("")
+ .writeComponent("")
+ .writeComponent("")
+ .writeComponent(pieceDetails)
+ .endElement()
+ .writeEndSegment();
+ }
+
private void writePublisher(String publisher, EDIStreamWriter writer) throws EDIStreamException {
writer.writeStartSegment("IMD")
.writeElement("L")
@@ -356,10 +397,18 @@ private void writeFundCode(String fundAndExpenseClass, EDIStreamWriter writer) t
.writeEndSegment();
}
+ private void writeVendorOrderNumber(String number, EDIStreamWriter writer) throws EDIStreamException {
+ writeVendorReferenceNumber(number, "SNA", writer);
+ }
+
private void writeVendorReferenceNumber(String number, EDIStreamWriter writer) throws EDIStreamException {
+ writeVendorReferenceNumber(number, "SLI", writer);
+ }
+
+ private void writeVendorReferenceNumber(String number, String component, EDIStreamWriter writer) throws EDIStreamException {
writer.writeStartSegment("RFF")
.writeStartElement()
- .writeComponent("SLI")
+ .writeComponent(component)
.writeComponent(number)
.endElement()
.writeEndSegment();
@@ -417,6 +466,12 @@ private String[] getTitleParts(CompositePoLine poLine) {
return title.split("(?<=\\G.{" + MAX_CHARS_PER_LINE + "})");
}
+ private String getPieceDetails(Piece piece) {
+ return StreamEx.of(piece.getDisplaySummary(), piece.getChronology(), piece.getEnumeration())
+ .filter(StringUtils::isNotBlank)
+ .joining(":");
+ }
+
private String getPhysicalMaterial(CompositePoLine poLine) {
if (poLine.getPhysical() != null && poLine.getPhysical().getMaterialType() != null) {
String materialTypeId = poLine.getPhysical().getMaterialType();
@@ -457,6 +512,21 @@ private String getExpenseClassCode(FundDistribution fundDistribution) {
return "";
}
+ private List getVendorReferenceNumbers(CompositePoLine poLine) {
+ return Optional.ofNullable(poLine.getVendorDetail())
+ .map(VendorDetail::getReferenceNumbers)
+ .orElse(new ArrayList<>());
+ }
+
+ private ReferenceNumberItem getAndRemoveVendorOrderNumber(List referenceNumberItems) {
+ var vendorOrderNumber = referenceNumberItems.stream()
+ .filter(r -> r.getRefNumberType() == ORDER_REFERENCE_NUMBER)
+ .findFirst()
+ .orElse(null);
+ referenceNumberItems.remove(vendorOrderNumber);
+ return vendorOrderNumber;
+ }
+
private List getLocations(CompositePoLine poLine) {
if (poLine.getLocations() != null) {
return poLine.getLocations();
diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/PurchaseOrdersToEdifactMapper.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/PurchaseOrdersToEdifactMapper.java
index e59d19df4..3d4443cd0 100644
--- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/PurchaseOrdersToEdifactMapper.java
+++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/PurchaseOrdersToEdifactMapper.java
@@ -1,7 +1,8 @@
package org.folio.dew.batch.acquisitions.edifact;
+import static java.util.stream.Collectors.groupingBy;
+
import java.io.ByteArrayOutputStream;
-import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@@ -12,6 +13,8 @@
import io.xlate.edi.stream.EDIOutputFactory;
import io.xlate.edi.stream.EDIStreamException;
import io.xlate.edi.stream.EDIStreamWriter;
+
+import org.folio.dew.domain.dto.Piece;
import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig;
import org.folio.dew.domain.dto.acquisitions.edifact.EdiFileConfig;
@@ -23,6 +26,10 @@ public PurchaseOrdersToEdifactMapper(CompositePOConverter compositePOConverter)
}
public String convertOrdersToEdifact(List compPOs, VendorEdiOrdersExportConfig ediExportConfig, String jobName) throws EDIStreamException {
+ return convertOrdersToEdifact(compPOs, List.of(), ediExportConfig, jobName);
+ }
+
+ public String convertOrdersToEdifact(List compPOs, List pieces, VendorEdiOrdersExportConfig ediExportConfig, String jobName) throws EDIStreamException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
EDIOutputFactory factory = EDIOutputFactory.newFactory();
@@ -44,9 +51,10 @@ public String convertOrdersToEdifact(List compPOs, Vendo
writeInterchangeHeader(writer, ediFileConfig);
+ var poLineIdToPieces = pieces.stream().collect(groupingBy(Piece::getPoLineId));
// Purchase orders
for (CompositePurchaseOrder compPO : compPOs) {
- compositePOConverter.convertPOtoEdifact(writer, compPO, ediFileConfig);
+ compositePOConverter.convertPOtoEdifact(writer, compPO, poLineIdToPieces, ediFileConfig);
messageCount++;
}
@@ -57,10 +65,6 @@ public String convertOrdersToEdifact(List compPOs, Vendo
return stream.toString();
}
- public byte[] convertOrdersToEdifactArray(List compPOs, VendorEdiOrdersExportConfig ediExportConfig, String jobName) throws EDIStreamException {
- return convertOrdersToEdifact(compPOs, ediExportConfig, jobName).getBytes(StandardCharsets.UTF_8);
- }
-
// Start of file - Can contain multiple order messages
private void writeStartFile(EDIStreamWriter writer) throws EDIStreamException {
writer.writeStartSegment("UNA")
diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/config/EdifactPurchaseOrderConfig.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/config/EdifactPurchaseOrderConfig.java
index c2d2feb05..f7dbbf7c2 100644
--- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/config/EdifactPurchaseOrderConfig.java
+++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/config/EdifactPurchaseOrderConfig.java
@@ -4,6 +4,11 @@
import org.folio.dew.batch.acquisitions.edifact.CompositePOLineConverter;
import org.folio.dew.batch.acquisitions.edifact.PurchaseOrdersToEdifactMapper;
import org.folio.dew.batch.acquisitions.edifact.services.ConfigurationService;
+import org.folio.dew.batch.acquisitions.edifact.services.ExpenseClassService;
+import org.folio.dew.batch.acquisitions.edifact.services.HoldingService;
+import org.folio.dew.batch.acquisitions.edifact.services.IdentifierTypeService;
+import org.folio.dew.batch.acquisitions.edifact.services.LocationService;
+import org.folio.dew.batch.acquisitions.edifact.services.MaterialTypeService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -12,8 +17,9 @@
@ComponentScan({ "org.folio.dew.batch.acquisitions.edifact" })
public class EdifactPurchaseOrderConfig {
@Bean
- CompositePOLineConverter compositePOLineConverter() {
- return new CompositePOLineConverter();
+ CompositePOLineConverter compositePOLineConverter(IdentifierTypeService identifierTypeService, MaterialTypeService materialTypeService,
+ ExpenseClassService expenseClassService, LocationService locationService, HoldingService holdingService) {
+ return new CompositePOLineConverter(identifierTypeService, materialTypeService, expenseClassService, locationService, holdingService);
}
@Bean
diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/exceptions/OrderNotFoundException.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/exceptions/OrderNotFoundException.java
deleted file mode 100644
index 23f9f3bf2..000000000
--- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/exceptions/OrderNotFoundException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.folio.dew.batch.acquisitions.edifact.exceptions;
-
-public class OrderNotFoundException extends RuntimeException {
-
- public OrderNotFoundException(String message, boolean writableStackTrace) {
- super(message, null, false, writableStackTrace);
- }
-
-}
diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdifactExportJobConfig.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdifactExportJobConfig.java
index 667b18e0c..ba5367604 100644
--- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdifactExportJobConfig.java
+++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdifactExportJobConfig.java
@@ -17,18 +17,18 @@
@Log4j2
@RequiredArgsConstructor
public class EdifactExportJobConfig {
- @Bean
- public Job edifactExportJob(
- EdiExportJobCompletionListener ediExportJobCompletionListener,
- JobRepository jobRepository,
- Step mapToEdifactStep,
- Step saveToFTPStep,
- Step saveToMinIOStep,
- Step createExportHistoryRecordsStep) {
- return new JobBuilder(ExportType.EDIFACT_ORDERS_EXPORT.getValue(), jobRepository)
- .incrementer(new RunIdIncrementer())
+
+ public static final String POL_MEM_KEY = "poLineIds";
+
+ private Job constructEdifactExportJob(JobBuilder jobBuilder,
+ EdiExportJobCompletionListener ediExportJobCompletionListener,
+ Step mapToEdifactOrdersStep,
+ Step saveToFTPStep,
+ Step saveToMinIOStep,
+ Step createExportHistoryRecordsStep) {
+ return jobBuilder.incrementer(new RunIdIncrementer())
.listener(ediExportJobCompletionListener)
- .start(mapToEdifactStep)
+ .start(mapToEdifactOrdersStep)
.next(saveToMinIOStep)
.next(saveToFTPStep)
.next(createExportHistoryRecordsStep)
@@ -36,10 +36,32 @@ public Job edifactExportJob(
}
@Bean
- public Step mapToEdifactStep(MapToEdifactTasklet mapToEdifactTasklet, JobRepository jobRepository,
- PlatformTransactionManager transactionManager) {
+ public Job edifactOrdersExportJob(EdiExportJobCompletionListener ediExportJobCompletionListener, JobRepository jobRepository,
+ Step mapToEdifactOrdersStep, Step saveToFTPStep, Step saveToMinIOStep, Step createExportHistoryRecordsStep) {
+ return constructEdifactExportJob(new JobBuilder(ExportType.EDIFACT_ORDERS_EXPORT.getValue(), jobRepository),
+ ediExportJobCompletionListener, mapToEdifactOrdersStep, saveToFTPStep, saveToMinIOStep, createExportHistoryRecordsStep);
+ }
+
+ @Bean
+ public Job edifactClaimsExportJob(EdiExportJobCompletionListener ediExportJobCompletionListener, JobRepository jobRepository,
+ Step mapToEdifactClaimsStep, Step saveToFTPStep, Step saveToMinIOStep, Step createExportHistoryRecordsStep) {
+ return constructEdifactExportJob(new JobBuilder(ExportType.CLAIMS.getValue(), jobRepository),
+ ediExportJobCompletionListener, mapToEdifactClaimsStep, saveToFTPStep, saveToMinIOStep, createExportHistoryRecordsStep);
+ }
+
+ @Bean
+ public Step mapToEdifactOrdersStep(MapToEdifactOrdersTasklet mapToEdifactOrdersTasklet, JobRepository jobRepository,
+ PlatformTransactionManager transactionManager) {
+ return new StepBuilder("mapToEdifactStep", jobRepository)
+ .tasklet(mapToEdifactOrdersTasklet, transactionManager)
+ .build();
+ }
+
+ @Bean
+ public Step mapToEdifactClaimsStep(MapToEdifactClaimsTasklet mapToEdifactClaimsTasklet, JobRepository jobRepository,
+ PlatformTransactionManager transactionManager) {
return new StepBuilder("mapToEdifactStep", jobRepository)
- .tasklet(mapToEdifactTasklet, transactionManager)
+ .tasklet(mapToEdifactClaimsTasklet, transactionManager)
.build();
}
diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTasklet.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTasklet.java
index ca668b25c..2035f2bd6 100644
--- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTasklet.java
+++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTasklet.java
@@ -1,5 +1,6 @@
package org.folio.dew.batch.acquisitions.edifact.jobs;
+import static org.folio.dew.batch.acquisitions.edifact.jobs.EdifactExportJobConfig.POL_MEM_KEY;
import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_FILE_NAME;
import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_ORDERS_EXPORT;
@@ -77,7 +78,7 @@ ExportHistory buildExportHistory(ChunkContext chunkContext) {
List getPoLineIdsFromExecutionContext(StepExecution stepExecutionContext) {
try {
- return ediObjectMapper.readValue((String) ExecutionContextUtils.getExecutionVariable(stepExecutionContext, "polineIds"), new TypeReference<>() {});
+ return ediObjectMapper.readValue((String) ExecutionContextUtils.getExecutionVariable(stepExecutionContext, POL_MEM_KEY), new TypeReference<>() {});
} catch (Exception e) {
return Collections.emptyList();
}
diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTasklet.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTasklet.java
new file mode 100644
index 000000000..a45ffa5f1
--- /dev/null
+++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTasklet.java
@@ -0,0 +1,50 @@
+package org.folio.dew.batch.acquisitions.edifact.jobs;
+
+import static org.folio.dew.utils.QueryUtils.convertIdsToCqlQuery;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.folio.dew.batch.acquisitions.edifact.PurchaseOrdersToEdifactMapper;
+import org.folio.dew.batch.acquisitions.edifact.services.OrdersService;
+import org.folio.dew.domain.dto.Piece;
+import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig;
+import org.folio.dew.domain.dto.acquisitions.edifact.EdifactExportHolder;
+import org.folio.dew.error.NotFoundException;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+import org.springframework.batch.core.scope.context.ChunkContext;
+import org.springframework.stereotype.Component;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@Component
+@StepScope
+public class MapToEdifactClaimsTasklet extends MapToEdifactTasklet {
+
+ public static final String CLAIM_PIECE_IDS = "claimPieceIds";
+
+ public MapToEdifactClaimsTasklet(ObjectMapper ediObjectMapper, OrdersService ordersService,
+ PurchaseOrdersToEdifactMapper purchaseOrdersToEdifactMapper) {
+ super(ediObjectMapper, ordersService, purchaseOrdersToEdifactMapper);
+ }
+
+ protected List getExportConfigMissingFields(VendorEdiOrdersExportConfig ediOrdersExportConfig) {
+ return CollectionUtils.isEmpty(ediOrdersExportConfig.getClaimPieceIds())
+ ? List.of(CLAIM_PIECE_IDS)
+ : List.of();
+ }
+
+ @Override
+ protected EdifactExportHolder buildEdifactExportHolder(ChunkContext chunkContext, VendorEdiOrdersExportConfig ediExportConfig, Map jobParameters) {
+ var pieces = ordersService.getPiecesByIdsAndReceivingStatus(ediExportConfig.getClaimPieceIds(), Piece.ReceivingStatusEnum.LATE);
+ if (pieces.isEmpty()) {
+ throw new NotFoundException(Piece.class);
+ }
+
+ var poLineQuery = convertIdsToCqlQuery(pieces.stream().map(Piece::getPoLineId).toList());
+ var compOrders = getCompositeOrders(poLineQuery);
+ return new EdifactExportHolder(compOrders, pieces);
+ }
+
+}
diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrdersTasklet.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrdersTasklet.java
new file mode 100644
index 000000000..f78df6a0b
--- /dev/null
+++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrdersTasklet.java
@@ -0,0 +1,95 @@
+package org.folio.dew.batch.acquisitions.edifact.jobs;
+
+import static org.folio.dew.utils.QueryUtils.combineCqlExpressions;
+import static org.folio.dew.utils.QueryUtils.convertFieldListToEnclosedCqlQuery;
+import static org.folio.dew.utils.QueryUtils.getCqlExpressionForFieldNullValue;
+import static org.folio.dew.utils.QueryUtils.negateQuery;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.folio.dew.batch.acquisitions.edifact.PurchaseOrdersToEdifactMapper;
+import org.folio.dew.batch.acquisitions.edifact.services.OrdersService;
+import org.folio.dew.client.DataExportSpringClient;
+import org.folio.dew.domain.dto.ExportConfigCollection;
+import org.folio.dew.domain.dto.ExportType;
+import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig;
+import org.folio.dew.domain.dto.acquisitions.edifact.EdifactExportHolder;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+import org.springframework.batch.core.scope.context.ChunkContext;
+import org.springframework.stereotype.Component;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import lombok.extern.log4j.Log4j2;
+
+@Component
+@StepScope
+@Log4j2
+public class MapToEdifactOrdersTasklet extends MapToEdifactTasklet {
+
+ private final DataExportSpringClient dataExportSpringClient;
+
+ public MapToEdifactOrdersTasklet(ObjectMapper ediObjectMapper, OrdersService ordersService,
+ DataExportSpringClient dataExportSpringClient,
+ PurchaseOrdersToEdifactMapper purchaseOrdersToEdifactMapper) {
+ super(ediObjectMapper, ordersService, purchaseOrdersToEdifactMapper);
+ this.dataExportSpringClient = dataExportSpringClient;
+ }
+
+ protected List getExportConfigMissingFields(VendorEdiOrdersExportConfig ediOrdersExportConfig) {
+ return List.of();
+ }
+
+ @Override
+ protected EdifactExportHolder buildEdifactExportHolder(ChunkContext chunkContext, VendorEdiOrdersExportConfig ediExportConfig, Map jobParameters) {
+ var poLineQuery = getPoLineQuery(ediExportConfig);
+ var compOrders = getCompositeOrders(poLineQuery);
+ return new EdifactExportHolder(compOrders, List.of());
+ }
+
+ protected String getPoLineQuery(VendorEdiOrdersExportConfig ediConfig) {
+ var acqMethods = ediConfig.getEdiConfig().getDefaultAcquisitionMethods();
+ var resultQuery = combineCqlExpressions("AND",
+ // Order filters
+ "purchaseOrder.workflowStatus==Open", // order status is Open
+ "purchaseOrder.vendor==%s".formatted(ediConfig.getVendorId()), // vendor id matches
+ negateQuery("purchaseOrder.manualPo==true"), // not a manual order
+
+ // Order line filters
+ "automaticExport==true", // line with automatic export
+ getCqlExpressionForFieldNullValue("lastEDIExportDate"), // has not been exported yet
+ convertFieldListToEnclosedCqlQuery(acqMethods, "acquisitionMethod", true), // acquisitionMethod in default list
+ getVendorAccountFilter(ediConfig) // vendor account no filter
+ );
+ log.info("getPoLineQuery:: Fetching purchase order lines with query: {}", resultQuery);
+ return resultQuery;
+ }
+
+ private String getVendorAccountFilter(VendorEdiOrdersExportConfig ediConfig) {
+ if (Boolean.TRUE.equals(ediConfig.getIsDefaultConfig())) {
+ var configQuery = "configName==%s_%s*".formatted(ExportType.EDIFACT_ORDERS_EXPORT, ediConfig.getVendorId());
+ var configs = dataExportSpringClient.getExportConfigs(configQuery);
+ return configs.getTotalRecords() > 1
+ ? negateQuery(convertFieldListToEnclosedCqlQuery(extractAllAccountNoLists(configs), "vendorDetail.vendorAccount", true))
+ : "";
+ }
+ return convertFieldListToEnclosedCqlQuery(ediConfig.getEdiConfig().getAccountNoList(), "vendorDetail.vendorAccount", true);
+ }
+
+ private Set extractAllAccountNoLists(ExportConfigCollection configs) {
+ return configs.getConfigs().stream()
+ .map(config -> config.getExportTypeSpecificParameters().getVendorEdiOrdersExportConfig().getEdiConfig())
+ .map(config -> Objects.nonNull(config) && CollectionUtils.isNotEmpty(config.getAccountNoList())
+ ? config.getAccountNoList()
+ : List.of())
+ .flatMap(Collection::stream)
+ .collect(Collectors.toSet());
+ }
+
+}
diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTasklet.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTasklet.java
index 04695c88a..d5fcd6b16 100644
--- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTasklet.java
+++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTasklet.java
@@ -2,75 +2,62 @@
import static java.util.Objects.requireNonNullElse;
import static java.util.stream.Collectors.groupingBy;
+import static org.folio.dew.batch.acquisitions.edifact.jobs.EdifactExportJobConfig.POL_MEM_KEY;
import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_ORDERS_EXPORT;
-import java.util.Collection;
-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.stream.Collectors;
-import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.folio.dew.batch.ExecutionContextUtils;
import org.folio.dew.batch.acquisitions.edifact.PurchaseOrdersToEdifactMapper;
import org.folio.dew.batch.acquisitions.edifact.exceptions.CompositeOrderMappingException;
import org.folio.dew.batch.acquisitions.edifact.exceptions.EdifactException;
-import org.folio.dew.batch.acquisitions.edifact.exceptions.OrderNotFoundException;
import org.folio.dew.batch.acquisitions.edifact.services.OrdersService;
-import org.folio.dew.client.DataExportSpringClient;
import org.folio.dew.domain.dto.CompositePoLine;
import org.folio.dew.domain.dto.CompositePurchaseOrder;
import org.folio.dew.domain.dto.JobParameterNames;
-import org.folio.dew.domain.dto.EdiConfig;
-import org.folio.dew.domain.dto.ExportConfig;
-import org.folio.dew.domain.dto.ExportConfigCollection;
-import org.folio.dew.domain.dto.ExportType;
import org.folio.dew.domain.dto.PoLine;
import org.folio.dew.domain.dto.PurchaseOrder;
import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig;
+import org.folio.dew.domain.dto.acquisitions.edifact.EdifactExportHolder;
+import org.folio.dew.error.NotFoundException;
import org.springframework.batch.core.StepContribution;
-import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
-import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import io.xlate.edi.stream.EDIStreamException;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
@RequiredArgsConstructor
-@Component
-@StepScope
@Log4j2
-public class MapToEdifactTasklet implements Tasklet {
- private final ObjectMapper ediObjectMapper;
+public abstract class MapToEdifactTasklet implements Tasklet {
- private final OrdersService ordersService;
- private final DataExportSpringClient dataExportSpringClient;
+ private final ObjectMapper ediObjectMapper;
+ protected final OrdersService ordersService;
private final PurchaseOrdersToEdifactMapper purchaseOrdersToEdifactMapper;
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
- log.info("Execute MapToEdifactTasklet");
+ log.info("execute:: Executing MapToEdifactTasklet with job: {}", chunkContext.getStepContext().getJobName());
var jobParameters = chunkContext.getStepContext().getJobParameters();
var ediExportConfig = ediObjectMapper.readValue((String)jobParameters.get(EDIFACT_ORDERS_EXPORT), VendorEdiOrdersExportConfig.class);
validateEdiExportConfig(ediExportConfig);
- List compOrders = getCompPOList(ediExportConfig);
- // save poLineIds in memory
- persistPoLineIds(chunkContext, compOrders);
+ var holder = buildEdifactExportHolder(chunkContext, ediExportConfig, jobParameters);
+ persistPoLineIds(chunkContext, holder.orders());
String jobName = jobParameters.get(JobParameterNames.JOB_NAME).toString();
- String edifactOrderAsString = purchaseOrdersToEdifactMapper.convertOrdersToEdifact(compOrders, ediExportConfig, jobName);
+ var edifactStringResult = purchaseOrdersToEdifactMapper.convertOrdersToEdifact(holder.orders(), holder.pieces(), ediExportConfig, jobName);
+
// save edifact file content in memory
- ExecutionContextUtils.addToJobExecutionContext(chunkContext.getStepContext().getStepExecution(), "edifactOrderAsString", edifactOrderAsString, "");
+ ExecutionContextUtils.addToJobExecutionContext(chunkContext.getStepContext().getStepExecution(), "edifactOrderAsString", edifactStringResult, "");
return RepeatStatus.FINISHED;
}
@@ -86,93 +73,34 @@ private void validateEdiExportConfig(VendorEdiOrdersExportConfig ediExportConfig
if (port.isEmpty()) {
throw new EdifactException("Export configuration is incomplete, missing FTP/SFTP Port");
}
+
+ var missingFields = getExportConfigMissingFields(ediExportConfig);
+ if (!missingFields.isEmpty()) {
+ throw new EdifactException("Export configuration is incomplete, missing required fields: %s".formatted(missingFields));
+ }
}
- private List getCompPOList(VendorEdiOrdersExportConfig ediConfig) {
- var poLineQuery = buildPoLineQuery(ediConfig);
+protected List getCompositeOrders(String poLineQuery) {
var poLines = ordersService.getPoLinesByQuery(poLineQuery);
-
var orderIds = poLines.stream()
.map(PoLine::getPurchaseOrderId)
.distinct()
.toList();
- var orders = ordersService.getPurchaseOrdersByIds(orderIds);
-
- var compOrders = assembleCompositeOrders(orders, poLines);
-
- log.debug("composite purchase orders: {}", compOrders);
+ var compOrders = assembleCompositeOrders(ordersService.getPurchaseOrdersByIds(orderIds), poLines);
+ log.debug("getCompositeOrders:: {}", compOrders);
if (compOrders.isEmpty()) {
- throw new OrderNotFoundException("Orders for export not found", false);
+ throw new NotFoundException(PurchaseOrder.class);
}
return compOrders;
}
- private String buildPoLineQuery(VendorEdiOrdersExportConfig ediConfig) {
- // Order filters
- var workflowStatusFilter = "purchaseOrder.workflowStatus==Open"; // order status is Open
- var vendorFilter = String.format(" AND purchaseOrder.vendor==%s", ediConfig.getVendorId()); // vendor id matches
- var notManualFilter = " AND (cql.allRecords=1 NOT purchaseOrder.manualPo==true)"; // not a manual order
-
- // Order line filters
- var automaticExportFilter = " AND automaticExport==true"; // line with automatic export
- var ediExportDateFilter = " AND (cql.allRecords=1 NOT lastEDIExportDate=\"\")"; // has not been exported yet
- var acqMethodsFilter = fieldInListFilter("acquisitionMethod",
- ediConfig.getEdiConfig().getDefaultAcquisitionMethods()); // acquisitionMethod in default list
- String vendorAccountFilter = "";
- if (Boolean.TRUE.equals(ediConfig.getIsDefaultConfig())) {
- var configQuery = String.format("configName==%s_%s*", ExportType.EDIFACT_ORDERS_EXPORT, ediConfig.getVendorId());
- var configs = dataExportSpringClient.getExportConfigs(configQuery);
- if (configs.getTotalRecords() > 1) {
- var accountNoSetForExclude = getAccountNoSet(configs);
- vendorAccountFilter = fieldNotInListFilter("vendorDetail.vendorAccount", accountNoSetForExclude);
- }
- } else {
- // vendorAccount in the config account number list
- vendorAccountFilter = fieldInListFilter("vendorDetail.vendorAccount",
- ediConfig.getEdiConfig().getAccountNoList());
- }
-
- var resultQuery = String.format("%s%s%s%s%s%s%s", workflowStatusFilter, vendorFilter, notManualFilter,
- automaticExportFilter, ediExportDateFilter, acqMethodsFilter, vendorAccountFilter);
- log.info("GET purchase order line query: {}", resultQuery);
- return resultQuery;
- }
-
- private Set getAccountNoSet(ExportConfigCollection configs) {
- Set accountNoSet = new HashSet<>();
- for (ExportConfig exportConfig : configs.getConfigs()) {
- EdiConfig ediConfig = exportConfig.getExportTypeSpecificParameters().getVendorEdiOrdersExportConfig().getEdiConfig();
- if (Objects.nonNull(ediConfig)) {
- List currentAccountNoList = ediConfig.getAccountNoList();
- if (CollectionUtils.isNotEmpty(currentAccountNoList)) {
- accountNoSet.addAll(currentAccountNoList);
- }
- }
- }
- return accountNoSet;
- }
-
- private void persistPoLineIds(ChunkContext chunkContext, List compOrders) throws JsonProcessingException {
- var polineIds = compOrders.stream()
+ protected void persistPoLineIds(ChunkContext chunkContext, List compOrders) throws JsonProcessingException {
+ var poLineIds = compOrders.stream()
.flatMap(ord -> ord.getCompositePoLines().stream())
.map(CompositePoLine::getId)
.toList();
- ExecutionContextUtils.addToJobExecutionContext(chunkContext.getStepContext().getStepExecution(),"polineIds", ediObjectMapper.writeValueAsString(polineIds),"");
- }
-
- private String fieldInListFilter(String fieldName, List> list) {
- return String.format(" AND %s==%s", fieldName,
- list.stream()
- .map(item -> String.format("\"%s\"", item.toString()))
- .collect(Collectors.joining(" OR ", "(", ")")));
- }
-
- private static String fieldNotInListFilter(String fieldName, Collection> list) {
- return String.format(" AND cql.allRecords=1 NOT %s==%s", fieldName,
- list.stream()
- .map(item -> String.format("\"%s\"", item.toString()))
- .collect(Collectors.joining(" OR ", "(", ")")));
+ ExecutionContextUtils.addToJobExecutionContext(chunkContext.getStepContext().getStepExecution(), POL_MEM_KEY, ediObjectMapper.writeValueAsString(poLineIds),"");
}
private List assembleCompositeOrders(List orders, List poLines) {
@@ -193,4 +121,10 @@ private T convertTo(Object value, Class c) {
throw new CompositeOrderMappingException(String.format("%s for object %s", ex.getMessage(), value));
}
}
+
+ protected abstract List getExportConfigMissingFields(VendorEdiOrdersExportConfig ediOrdersExportConfig);
+
+ protected abstract EdifactExportHolder buildEdifactExportHolder(ChunkContext chunkContext, VendorEdiOrdersExportConfig ediExportConfig,
+ Map jobParameters) throws JsonProcessingException, EDIStreamException;
+
}
diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/services/OrdersService.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/services/OrdersService.java
index 1ee319f78..fe0ba2d2f 100644
--- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/services/OrdersService.java
+++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/services/OrdersService.java
@@ -1,28 +1,35 @@
package org.folio.dew.batch.acquisitions.edifact.services;
+import static org.folio.dew.utils.QueryUtils.combineCqlExpressions;
+import static org.folio.dew.utils.QueryUtils.convertIdsToCqlQuery;
+
import org.folio.dew.client.OrdersStorageClient;
+import org.folio.dew.domain.dto.Piece;
import org.folio.dew.domain.dto.PoLine;
import org.folio.dew.domain.dto.PurchaseOrder;
+import org.folio.dew.utils.QueryUtils;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
+import one.util.streamex.StreamEx;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
@Service
@RequiredArgsConstructor
@Log4j2
public class OrdersService {
+
private static final int CHUNK_SIZE = 50;
+ private static final String PIECES_BY_REC_STATUS_QUERY = "receivingStatus==%s";
+
+
private final OrdersStorageClient ordersStorageClient;
public List getPoLinesByQuery(String query) {
- log.debug("OrdersService.getPoLinesByQuery: {}", query);
+ log.debug("getPoLinesByQuery:: Fetching PoLines by query: {}", query);
var allLines = new ArrayList();
String lastId = null;
while (true) {
@@ -37,27 +44,31 @@ public List getPoLinesByQuery(String query) {
allLines.addAll(linesInChunk);
lastId = linesInChunk.get(linesInChunk.size() - 1).getId();
}
- log.debug("OrdersService.getPoLinesByQuery: returned {} lines", allLines.size());
+ log.debug("getPoLinesByQuery:: Fetched {} PoLines", allLines.size());
return allLines;
}
public List getPurchaseOrdersByIds(List orderIds) {
- log.debug("OrdersService.getPurchaseOrdersByIds: {}", orderIds);
- List orders = new ArrayList<>();
- Collection> idChunks = partitionUsingChunkSize(orderIds);
- for (List idChunk : idChunks) {
- String query = idChunk.stream().collect(Collectors.joining(" OR ", "id==(", ")"));
- orders.addAll(ordersStorageClient.getPurchaseOrdersByQuery(query, 0, Integer.MAX_VALUE).getPurchaseOrders());
- }
- log.debug("OrdersService.getPurchaseOrdersByIds: returned {} orders", orders.size());
+ log.debug("getPurchaseOrdersByIds: Fetching orders: {}", orderIds);
+ var orders = StreamEx.ofSubLists(orderIds, CHUNK_SIZE)
+ .map(QueryUtils::convertIdsToCqlQuery)
+ .map(query -> ordersStorageClient.getPurchaseOrdersByQuery(query, 0, Integer.MAX_VALUE))
+ .flatMap(collection -> collection.getPurchaseOrders().stream())
+ .toList();
+ log.debug("getPurchaseOrdersByIds:: Fetched {} orders", orders.size());
return orders;
}
- private Collection> partitionUsingChunkSize(List inputList) {
- return IntStream.range(0, inputList.size())
- .boxed()
- .collect(Collectors.groupingBy(partition -> partition / CHUNK_SIZE,
- Collectors.mapping(inputList::get, Collectors.toList())))
- .values();
+ public List getPiecesByIdsAndReceivingStatus(List pieceIds, Piece.ReceivingStatusEnum receivingStatus) {
+ log.debug("getPiecesByIdsAndReceivingStatus:: Fetching pieces: {} by status: {}", pieceIds, receivingStatus);
+ var receivingStatusQuery = PIECES_BY_REC_STATUS_QUERY.formatted(receivingStatus.getValue());
+ var pieces = StreamEx.ofSubLists(pieceIds, CHUNK_SIZE)
+ .map(ids -> combineCqlExpressions("and", convertIdsToCqlQuery(ids), receivingStatusQuery))
+ .map(query -> ordersStorageClient.getPiecesByQuery(query, 0, Integer.MAX_VALUE))
+ .flatMap(collection -> collection.getPieces().stream())
+ .toList();
+ log.debug("getPiecesByIdsAndReceivingStatus:: Fetched {} pieces", pieces.size());
+ return pieces;
}
+
}
diff --git a/src/main/java/org/folio/dew/client/OrdersStorageClient.java b/src/main/java/org/folio/dew/client/OrdersStorageClient.java
index 2439d2688..a77edbf96 100644
--- a/src/main/java/org/folio/dew/client/OrdersStorageClient.java
+++ b/src/main/java/org/folio/dew/client/OrdersStorageClient.java
@@ -1,5 +1,6 @@
package org.folio.dew.client;
+import org.folio.dew.domain.dto.PieceCollection;
import org.folio.dew.domain.dto.PoLineCollection;
import org.folio.dew.domain.dto.PurchaseOrderCollection;
import org.springframework.cloud.openfeign.FeignClient;
@@ -24,4 +25,11 @@ PoLineCollection getPoLinesByQuery(
@RequestParam("limit") int limit
);
+ @GetMapping(value = "/pieces", produces = MediaType.APPLICATION_JSON_VALUE)
+ PieceCollection getPiecesByQuery(
+ @RequestParam("query") String query,
+ @RequestParam("offset") int offset,
+ @RequestParam("limit") int limit
+ );
+
}
diff --git a/src/main/java/org/folio/dew/domain/dto/acquisitions/edifact/EdifactExportHolder.java b/src/main/java/org/folio/dew/domain/dto/acquisitions/edifact/EdifactExportHolder.java
new file mode 100644
index 000000000..8108aed51
--- /dev/null
+++ b/src/main/java/org/folio/dew/domain/dto/acquisitions/edifact/EdifactExportHolder.java
@@ -0,0 +1,10 @@
+package org.folio.dew.domain.dto.acquisitions.edifact;
+
+import java.util.List;
+
+import org.folio.dew.domain.dto.CompositePurchaseOrder;
+import org.folio.dew.domain.dto.Piece;
+
+public record EdifactExportHolder(List orders, List pieces) {
+
+}
diff --git a/src/main/java/org/folio/dew/error/NotFoundException.java b/src/main/java/org/folio/dew/error/NotFoundException.java
index d8bf36e6c..8317c2648 100644
--- a/src/main/java/org/folio/dew/error/NotFoundException.java
+++ b/src/main/java/org/folio/dew/error/NotFoundException.java
@@ -1,7 +1,15 @@
package org.folio.dew.error;
public class NotFoundException extends RuntimeException {
+
+ private static final String EXCEPTION_MESSAGE = "Entities not found: %s";
+
+ public NotFoundException(Class> entityClass) {
+ super(EXCEPTION_MESSAGE.formatted(entityClass.getSimpleName()), null, false, false);
+ }
+
public NotFoundException(String message) {
super(message);
}
+
}
diff --git a/src/main/java/org/folio/dew/utils/QueryUtils.java b/src/main/java/org/folio/dew/utils/QueryUtils.java
new file mode 100644
index 000000000..17c7f8576
--- /dev/null
+++ b/src/main/java/org/folio/dew/utils/QueryUtils.java
@@ -0,0 +1,119 @@
+package org.folio.dew.utils;
+
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import one.util.streamex.StreamEx;
+
+public class QueryUtils {
+
+ public static final String ID = "id";
+ private static final String CQL_COMBINE_OPERATOR = ") %s (";
+ private static final String CQL_MATCH_STRICT = "%s==%s";
+ private static final String CQL_MATCH = "%s=%s";
+ private static final String CQL_PREFIX = "(";
+ private static final String CQL_SUFFIX = ")";
+ private static final String CQL_NEGATE_PREFIX = "cql.allRecords=1 NOT ";
+ private static final String CQL_UNDEFINED_FIELD_EXPRESSION = CQL_NEGATE_PREFIX + "%s=\"\"";
+ private static final Pattern CQL_SORT_BY_PATTERN = Pattern.compile("(.*)(\\ssortBy\\s.*)", Pattern.CASE_INSENSITIVE); //NOSONAR
+
+ private QueryUtils() {}
+
+ public static String encodeQuery(String query) {
+ return URLEncoder.encode(query, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Combines multiple CQL expressions using the specified logical operator. For example:
+ * Call: combineCqlExpressions("and", "field1==value1", "field2==value2")
+ * Result: (field1==value1) and (field2==value2)
+ *
+ * @param operator The logical operator to combine the expressions (e.g., "and", "or").
+ * @param expressions The CQL expressions to combine.
+ * @return A single CQL query string combining the provided expressions with the specified operator.
+ */
+ public static String combineCqlExpressions(String operator, String... expressions) {
+ if (ArrayUtils.isEmpty(expressions)) {
+ return StringUtils.EMPTY;
+ }
+ var sorting = StringUtils.EMPTY;
+ // Check whether last expression contains sorting query. If it does, extract it to be added in the end of the resulting query
+ Matcher matcher = CQL_SORT_BY_PATTERN.matcher(expressions[expressions.length - 1]);
+ if (matcher.find()) {
+ expressions[expressions.length - 1] = matcher.group(1);
+ sorting = matcher.group(2);
+ }
+
+ var suffix = CQL_SUFFIX + sorting;
+ var delimiter = String.format(CQL_COMBINE_OPERATOR, operator);
+ return StreamEx.of(expressions)
+ .filter(StringUtils::isNotBlank)
+ .joining(delimiter, CQL_PREFIX, suffix);
+ }
+
+ /**
+ * Converts a collection of IDs to a CQL query string using the specified ID field.
+ *
+ * @param ids The collection of IDs to be converted.
+ * @param idField The field name to be used in the CQL query.
+ * @return A CQL query string representing the IDs.
+ */
+ public static String convertIdsToCqlQuery(Collection ids, String idField) {
+ return convertFieldListToCqlQuery(ids, idField, true, false);
+ }
+
+ /**
+ * Converts a collection of IDs to a CQL query string using the default ID field.
+ *
+ * @param ids The collection of IDs to be converted.
+ * @return A CQL query string representing the IDs.
+ */
+ public static String convertIdsToCqlQuery(Collection ids) {
+ return convertFieldListToCqlQuery(ids, ID, true, false);
+ }
+
+ /**
+ * Transform list of values for some property to CQL query using 'or' operation and enclosing values with quotes
+ *
+ * @param values list of field values
+ * @param fieldName the property name to search by
+ * @param strictMatch indicates whether strict match mode (i.e. ==) should be used or not (i.e. =)
+ * @return String representing CQL query to get records by some property enclosed values
+ */
+ public static String convertFieldListToEnclosedCqlQuery(Collection> values, String fieldName, boolean strictMatch) {
+ return convertFieldListToCqlQuery(values, fieldName, strictMatch, true);
+ }
+
+ /**
+ * Transform list of values for some property to CQL query using 'or' operation
+ *
+ * @param values list of field values
+ * @param fieldName the property name to search by
+ * @param strictMatch indicates whether strict match mode (i.e. ==) should be used or not (i.e. =)
+ * @param enclosed indicates whether values should be enclosed with quotes (i.e. asd) or not (i.e. "asd")
+ * @return String representing CQL query to get records by some property values
+ */
+ public static String convertFieldListToCqlQuery(Collection> values, String fieldName, boolean strictMatch, boolean enclosed) {
+ var prefix = String.format(strictMatch ? CQL_MATCH_STRICT : CQL_MATCH, fieldName, CQL_PREFIX);
+ var enclose = enclosed ? "\"%s\"" : "%s";
+ return StreamEx.of(values)
+ .map(Object::toString)
+ .map(enclose::formatted)
+ .joining(" or ", prefix, CQL_SUFFIX);
+ }
+
+ public static String negateQuery(String cql) {
+ return CQL_NEGATE_PREFIX + cql;
+ }
+
+ public static String getCqlExpressionForFieldNullValue(String fieldName) {
+ return String.format(CQL_UNDEFINED_FIELD_EXPRESSION, fieldName);
+ }
+
+}
diff --git a/src/main/resources/swagger.api/order-export.yaml b/src/main/resources/swagger.api/order-export.yaml
index c87821b23..31ee4e1fa 100644
--- a/src/main/resources/swagger.api/order-export.yaml
+++ b/src/main/resources/swagger.api/order-export.yaml
@@ -61,3 +61,7 @@ components:
$ref: '../../../../folio-export-common/schemas/acquisitions/mod-orders-storage/purchase_order.json#/PurchaseOrder'
purchaseOrderCollection:
$ref: '../../../../folio-export-common/schemas/acquisitions/mod-orders-storage/purchase_order_collection.json#/PurchaseOrderCollection'
+ piece:
+ $ref: '../../../../folio-export-common/schemas/acquisitions/mod-orders-storage/piece.json#/Piece'
+ pieceCollection:
+ $ref: '../../../../folio-export-common/schemas/acquisitions/mod-orders-storage/piece_collection.json#/PieceCollection'
diff --git a/src/test/java/org/folio/dew/BaseBatchTest.java b/src/test/java/org/folio/dew/BaseBatchTest.java
index cbd47e5cc..4c4e9016e 100644
--- a/src/test/java/org/folio/dew/BaseBatchTest.java
+++ b/src/test/java/org/folio/dew/BaseBatchTest.java
@@ -175,7 +175,7 @@ public static HttpHeaders defaultHeaders() {
}
@BeforeEach
- void setUp() {
+ protected void setUp() {
okapiHeaders = new LinkedHashMap<>();
okapiHeaders.put(XOkapiHeaders.TENANT, TENANT);
okapiHeaders.put(XOkapiHeaders.TOKEN, TOKEN);
diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/MappingOrdersToEdifactTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/MappingOrdersToEdifactTest.java
index d8f1cec5c..2d92ef9ee 100644
--- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/MappingOrdersToEdifactTest.java
+++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/MappingOrdersToEdifactTest.java
@@ -1,5 +1,7 @@
package org.folio.dew.batch.acquisitions.edifact;
+import static org.folio.dew.domain.dto.ExportType.CLAIMS;
+import static org.folio.dew.domain.dto.ExportType.EDIFACT_ORDERS_EXPORT;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -16,101 +18,133 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.stream.Stream;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.IOUtils;
-import org.folio.dew.BaseBatchTest;
import org.folio.dew.batch.acquisitions.edifact.services.ConfigurationService;
import org.folio.dew.batch.acquisitions.edifact.services.ExpenseClassService;
import org.folio.dew.batch.acquisitions.edifact.services.HoldingService;
import org.folio.dew.batch.acquisitions.edifact.services.IdentifierTypeService;
import org.folio.dew.batch.acquisitions.edifact.services.LocationService;
import org.folio.dew.batch.acquisitions.edifact.services.MaterialTypeService;
+import org.folio.dew.config.JacksonConfiguration;
import org.folio.dew.domain.dto.CompositePurchaseOrder;
+import org.folio.dew.domain.dto.ExportType;
+import org.folio.dew.domain.dto.Piece;
+import org.folio.dew.domain.dto.PieceCollection;
import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig;
-import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.mock.mockito.MockBean;
@Log4j2
@ExtendWith(MockitoExtension.class)
-class MappingOrdersToEdifactTest extends BaseBatchTest {
- @Autowired
+class MappingOrdersToEdifactTest {
+
+ private static final Map EXPORT_EDI_PATHS = Map.of(
+ EDIFACT_ORDERS_EXPORT,"edifact/acquisitions/edifact_orders_result.edi",
+ CLAIMS, "edifact/acquisitions/edifact_claims_result.edi"
+ );
+
private ObjectMapper objectMapper;
- @Autowired
private PurchaseOrdersToEdifactMapper purchaseOrdersToEdifactMapper;
- @MockBean
+
+ @Mock
private IdentifierTypeService identifierTypeService;
- @MockBean
+ @Mock
private MaterialTypeService materialTypeService;
- @MockBean
+ @Mock
private ExpenseClassService expenseClassService;
- @MockBean
+ @Mock
private LocationService locationService;
- @MockBean
+ @Mock
private HoldingService holdingService;
- @MockBean
+ @Mock
private ConfigurationService configurationService;
- @Test
- void convertOrdersToEdifact() throws Exception {
+ @BeforeEach
+ void setUp() {
+ var compositePOLineConverter = new CompositePOLineConverter(identifierTypeService, materialTypeService, expenseClassService, locationService, holdingService);
+ var compositePOConverter = new CompositePOConverter(compositePOLineConverter, configurationService);
+ purchaseOrdersToEdifactMapper = new PurchaseOrdersToEdifactMapper(compositePOConverter);
+ objectMapper = new JacksonConfiguration().objectMapper();
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = ExportType.class, names = {"EDIFACT_ORDERS_EXPORT", "CLAIMS"})
+ void convertOrdersToEdifact(ExportType type) throws Exception {
String jobName = "123456789012345";
String fileIdExpected = "23456789012345";
- List compPOs = getTestOrdersFromJson();
+ List compPOs = getTestOrdersFromJson(type);
+ List pieces = getTestPiecesFromJson(type);
serviceMocks();
- String ediOrder = purchaseOrdersToEdifactMapper.convertOrdersToEdifact(compPOs, getTestEdiConfig(), jobName);
- log.info(ediOrder);
+ String ediOrder;
+ if (type == EDIFACT_ORDERS_EXPORT) {
+ ediOrder = purchaseOrdersToEdifactMapper.convertOrdersToEdifact(compPOs, getTestEdiConfig(), jobName);
+ } else {
+ var piecePoLineIds = pieces.stream().map(Piece::getPoLineId).toList();
+ compPOs = compPOs.stream()
+ .peek(po -> po.getCompositePoLines().stream().filter(line -> piecePoLineIds.contains(line.getId())).toList())
+ .filter(po -> !po.getCompositePoLines().isEmpty())
+ .toList();
+ ediOrder = purchaseOrdersToEdifactMapper.convertOrdersToEdifact(compPOs, pieces, getTestEdiConfig(), jobName);
+ }
+
assertFalse(ediOrder.isEmpty());
- validateEdifactOrders(ediOrder, fileIdExpected);
+ validateEdifactOrders(type, ediOrder, fileIdExpected);
}
- @Test
- void convertOrdersToEdifactByteArray() throws Exception {
+ @ParameterizedTest
+ @EnumSource(value = ExportType.class, names = {"EDIFACT_ORDERS_EXPORT", "CLAIMS"})
+ void convertOrdersToEdifactByteArray(ExportType type) throws Exception {
String jobName = "12345";
- List compPOs = getTestOrdersFromJson();
+ List compPOs = getTestOrdersFromJson(type);
+ List pieces = getTestPiecesFromJson(type);
serviceMocks();
- byte[] ediOrder = purchaseOrdersToEdifactMapper.convertOrdersToEdifactArray(compPOs, getTestEdiConfig(), jobName);
+ byte[] ediOrder;
+ if (type == EDIFACT_ORDERS_EXPORT) {
+ ediOrder = purchaseOrdersToEdifactMapper.convertOrdersToEdifact(compPOs, getTestEdiConfig(), jobName).getBytes(StandardCharsets.UTF_8);
+ } else {
+ ediOrder = purchaseOrdersToEdifactMapper.convertOrdersToEdifact(compPOs, pieces, getTestEdiConfig(), jobName).getBytes(StandardCharsets.UTF_8);
+ }
+
assertNotNull(ediOrder);
- String ediOrderString = new String(ediOrder);
- log.info(ediOrderString);
- validateEdifactOrders(ediOrderString, jobName);
+ validateEdifactOrders(type, new String(ediOrder), jobName);
}
private VendorEdiOrdersExportConfig getTestEdiConfig() throws IOException {
return objectMapper.readValue(getMockData("edifact/acquisitions/vendorEdiOrdersExportConfig.json"), VendorEdiOrdersExportConfig.class);
}
- private List getTestOrdersFromJson() throws IOException {
-
- CompositePurchaseOrder compPo = objectMapper.readValue(getMockData("edifact/acquisitions/composite_purchase_order.json"), CompositePurchaseOrder.class);
-
- CompositePurchaseOrder comprehensiveCompPo = objectMapper.readValue(getMockData("edifact/acquisitions/comprehensive_composite_purchase_order.json"), CompositePurchaseOrder.class);
-
- CompositePurchaseOrder minimalisticCompPo = objectMapper.readValue(getMockData("edifact/acquisitions/minimalistic_composite_purchase_order.json"), CompositePurchaseOrder.class);
-
- CompositePurchaseOrder compPoWithEmptyVendorAccount = objectMapper.readValue(getMockData("edifact/acquisitions/purchase_order_empty_vendor_account.json"), CompositePurchaseOrder.class);
-
- CompositePurchaseOrder compPoWithNonEANProductIds = objectMapper.readValue(getMockData("edifact/acquisitions/purchase_order_non_ean_product_ids.json"), CompositePurchaseOrder.class);
-
- CompositePurchaseOrder compPoTitleWithEscapeChars = objectMapper.readValue(getMockData("edifact/acquisitions/purchase_order_title_with_escape_chars.json"), CompositePurchaseOrder.class);
-
+ private List getTestOrdersFromJson(ExportType type) throws IOException {
List compPOs = new ArrayList<>();
- compPOs.add(compPo);
- compPOs.add(comprehensiveCompPo);
- compPOs.add(minimalisticCompPo);
- compPOs.add(compPoWithEmptyVendorAccount);
- compPOs.add(compPoWithNonEANProductIds);
- compPOs.add(compPoTitleWithEscapeChars);
+ compPOs.add(objectMapper.readValue(getMockData("edifact/acquisitions/composite_purchase_order.json"), CompositePurchaseOrder.class));
+ compPOs.add(objectMapper.readValue(getMockData("edifact/acquisitions/comprehensive_composite_purchase_order.json"), CompositePurchaseOrder.class));
+ compPOs.add(objectMapper.readValue(getMockData("edifact/acquisitions/minimalistic_composite_purchase_order.json"), CompositePurchaseOrder.class));
+ if (type == EDIFACT_ORDERS_EXPORT) {
+ compPOs.add(objectMapper.readValue(getMockData("edifact/acquisitions/purchase_order_empty_vendor_account.json"), CompositePurchaseOrder.class));
+ compPOs.add(objectMapper.readValue(getMockData("edifact/acquisitions/purchase_order_non_ean_product_ids.json"), CompositePurchaseOrder.class));
+ compPOs.add(objectMapper.readValue(getMockData("edifact/acquisitions/purchase_order_title_with_escape_chars.json"), CompositePurchaseOrder.class));
+ }
return compPOs;
}
+
+ private List getTestPiecesFromJson(ExportType type) throws IOException {
+ return type == EDIFACT_ORDERS_EXPORT
+ ? List.of()
+ : objectMapper.readValue(getMockData("edifact/acquisitions/pieces_collection_mixed.json"), PieceCollection.class).getPieces();
+ }
+
public static String getMockData(String path) throws IOException {
try (InputStream resourceAsStream = MappingOrdersToEdifactTest.class.getClassLoader().getResourceAsStream(path)) {
if (resourceAsStream != null) {
@@ -142,10 +176,9 @@ private void serviceMocks() {
.thenReturn("Bockenheimer Landstr. 134-13");
}
- private void validateEdifactOrders(String ediOrder, String fileId) throws IOException {
- String ediOrderExpected = getMockData("edifact/acquisitions/edifact_orders_result.edi")
- .replaceAll("\\{fileId}", fileId);
-
+ private void validateEdifactOrders(ExportType type, String ediOrder, String fileId) throws IOException {
+ log.info("Generated EDI file:\n{}", ediOrder);
+ String ediOrderExpected = getMockData(EXPORT_EDI_PATHS.get(type)).replaceAll("\\{fileId}", fileId);
String ediOrderWithRemovedDateTime = ediOrder.replaceFirst("\\d{6}:\\d{4}\\+", "ddmmyy:hhmm+");
assertEquals(ediOrderExpected, ediOrderWithRemovedDateTime);
}
diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/OrderServiceTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/OrderServiceTest.java
index 591bc41f5..a00df7761 100644
--- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/OrderServiceTest.java
+++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/OrderServiceTest.java
@@ -70,12 +70,12 @@ void getPurchaseOrdersByIdsTest() {
orderList1.add(order);
query1Builder.append(id);
if (i != 49)
- query1Builder.append(" OR ");
+ query1Builder.append(" or ");
} else {
orderList2.add(order);
query2Builder.append(id);
if (i != 59)
- query2Builder.append(" OR ");
+ query2Builder.append(" or ");
}
}
query1Builder.append(")");
diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTaskletTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTaskletTest.java
index 38578ac6d..db9a1e071 100644
--- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTaskletTest.java
+++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTaskletTest.java
@@ -12,6 +12,7 @@
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.annotation.DirtiesContext;
@@ -26,6 +27,7 @@
class ExportHistoryTaskletTest extends BaseBatchTest {
@Autowired
+ @Qualifier("edifactOrdersExportJob")
private Job edifactExportJob;
@MockBean
private OrganizationsService organizationsService;
diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTaskletTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTaskletTest.java
new file mode 100644
index 000000000..5f05296cb
--- /dev/null
+++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTaskletTest.java
@@ -0,0 +1,118 @@
+package org.folio.dew.batch.acquisitions.edifact.jobs;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.folio.dew.utils.QueryUtils.convertIdsToCqlQuery;
+import static org.folio.dew.utils.TestUtils.getMockData;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.folio.dew.domain.dto.Piece;
+import org.folio.dew.domain.dto.PieceCollection;
+import org.folio.dew.domain.dto.PoLine;
+import org.folio.dew.domain.dto.PoLineCollection;
+import org.folio.dew.domain.dto.PurchaseOrder;
+import org.folio.dew.domain.dto.PurchaseOrderCollection;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.batch.core.BatchStatus;
+import org.springframework.batch.core.ExitStatus;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.test.JobLauncherTestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import lombok.SneakyThrows;
+
+class MapToEdifactClaimsTaskletTest extends MapToEdifactTaskletAbstractTest {
+
+ private static final String SAMPLE_PIECES_PATH = "edifact/acquisitions/pieces_collection.json";
+
+ @Autowired
+ Job edifactClaimsExportJob;
+
+ private List orders;
+ private List poLines;
+ private List pieces;
+ private List pieceIds;
+
+ @BeforeEach
+ @SneakyThrows
+ @Override
+ protected void setUp() {
+ super.setUp();
+ edifactExportJob = edifactClaimsExportJob;
+ orders = objectMapper.readValue(getMockData(SAMPLE_PURCHASE_ORDERS_PATH), PurchaseOrderCollection.class).getPurchaseOrders();
+ poLines = objectMapper.readValue(getMockData(SAMPLE_PO_LINES_PATH), PoLineCollection.class).getPoLines();
+ pieces = objectMapper.readValue(getMockData(SAMPLE_PIECES_PATH), PieceCollection.class).getPieces();
+
+ pieceIds = pieces.stream().map(Piece::getId).toList();
+ doReturn(pieces).when(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.LATE);
+ }
+
+ @Test
+ void testEdifactClaimsExport() throws Exception {
+ JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
+ String poLineQuery = convertIdsToCqlQuery(pieces.stream().map(Piece::getPoLineId).toList());
+
+ doReturn(poLines).when(ordersService).getPoLinesByQuery(poLineQuery);
+ doReturn(orders).when(ordersService).getPurchaseOrdersByIds(anyList());
+ doReturn("test1").when(purchaseOrdersToEdifactMapper).convertOrdersToEdifact(any(), any(), anyString());
+
+ var exportConfig = getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT, pieceIds);
+ JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(exportConfig));
+
+ assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
+ verify(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.LATE);
+ verify(ordersService).getPoLinesByQuery(poLineQuery);
+ verify(ordersService).getPurchaseOrdersByIds(anyList());
+ }
+
+ @Test
+ void testEdifactClaimsExportNoPiecesFound() throws Exception {
+ JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
+ doReturn(List.of()).when(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.LATE);
+
+ var exportConfig = getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT, pieceIds);
+ JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(exportConfig));
+
+ assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode());
+ assertThat(jobExecution.getExitStatus().getExitDescription()).contains("Entities not found: Piece");
+ verify(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.LATE);
+ }
+
+ @Test
+ void testEdifactClaimsExportMissingRequiredFields() throws Exception {
+ JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
+ var exportConfig = getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT, List.of());
+
+ JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(exportConfig));
+ var status = new ArrayList<>(jobExecution.getStepExecutions()).get(0).getStatus();
+
+ assertEquals(BatchStatus.FAILED, status);
+ assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode());
+ assertThat(jobExecution.getExitStatus().getExitDescription()).contains("Export configuration is incomplete, missing required fields: [claimPieceIds]");
+ }
+
+ @Override
+ protected ObjectNode getEdifactExportConfig(String path) throws IOException {
+ return getEdifactExportConfig(path, pieceIds);
+ }
+
+ protected ObjectNode getEdifactExportConfig(String path, List pieceIds) throws IOException {
+ var exportConfig = super.getEdifactExportConfig(path);
+ var arr = exportConfig.putArray("claimPieceIds");
+ pieceIds.forEach(arr::add);
+ return exportConfig;
+ }
+
+}
diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrderTaskletTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrderTaskletTest.java
new file mode 100644
index 000000000..cbd9b14ba
--- /dev/null
+++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrderTaskletTest.java
@@ -0,0 +1,129 @@
+package org.folio.dew.batch.acquisitions.edifact.jobs;
+
+import static org.folio.dew.utils.TestUtils.getMockData;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.assertj.core.api.Assertions;
+import org.folio.dew.domain.dto.ExportConfigCollection;
+import org.folio.dew.domain.dto.PoLine;
+import org.folio.dew.domain.dto.PoLineCollection;
+import org.folio.dew.domain.dto.PurchaseOrder;
+import org.folio.dew.domain.dto.PurchaseOrderCollection;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.batch.core.ExitStatus;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.test.JobLauncherTestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import lombok.SneakyThrows;
+
+class MapToEdifactOrderTaskletTest extends MapToEdifactTaskletAbstractTest {
+
+ private static final String DATA_EXPORT_CONFIGS_PATH = "edifact/dataExportConfigs.json";
+
+ @Autowired
+ Job edifactOrdersExportJob;
+
+ private List orders;
+ private List poLines;
+
+ @BeforeEach
+ @SneakyThrows
+ @Override
+ protected void setUp() {
+ super.setUp();
+ edifactExportJob = edifactOrdersExportJob;
+ orders = objectMapper.readValue(getMockData(SAMPLE_PURCHASE_ORDERS_PATH), PurchaseOrderCollection.class).getPurchaseOrders();
+ poLines = objectMapper.readValue(getMockData(SAMPLE_PO_LINES_PATH), PoLineCollection.class).getPoLines();
+
+ }
+
+ @Test
+ void testEdifactOrdersExport() throws Exception {
+ JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
+ String cqlString = "(purchaseOrder.workflowStatus==Open)" +
+ " AND (purchaseOrder.vendor==d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1)" +
+ " AND (cql.allRecords=1 NOT purchaseOrder.manualPo==true)" +
+ " AND (automaticExport==true)" +
+ " AND (cql.allRecords=1 NOT lastEDIExportDate=\"\")" +
+ " AND (acquisitionMethod==(\"306489dd-0053-49ee-a068-c316444a8f55\"))" +
+ " AND (vendorDetail.vendorAccount==(\"BRXXXXX-01\"))";
+ doReturn(poLines).when(ordersService).getPoLinesByQuery(cqlString);
+ doReturn(orders).when(ordersService).getPurchaseOrdersByIds(anyList());
+ doReturn("test1").when(purchaseOrdersToEdifactMapper).convertOrdersToEdifact(any(), any(), anyString());
+
+ JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT)));
+
+ Assertions.assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
+ verify(ordersService).getPoLinesByQuery(cqlString);
+ verify(ordersService).getPurchaseOrdersByIds(anyList());
+ }
+
+ @Test
+ void testEdifactOrdersExportDefaultConfig() throws Exception {
+ JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
+ String cqlString = "(purchaseOrder.workflowStatus==Open)" +
+ " AND (purchaseOrder.vendor==d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1)" +
+ " AND (cql.allRecords=1 NOT purchaseOrder.manualPo==true)" +
+ " AND (automaticExport==true)" +
+ " AND (cql.allRecords=1 NOT lastEDIExportDate=\"\")" +
+ " AND (acquisitionMethod==(\"306489dd-0053-49ee-a068-c316444a8f55\"))";
+ String configSql = "configName==EDIFACT_ORDERS_EXPORT_d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1*";
+ ExportConfigCollection exportConfigCollection = new ExportConfigCollection();
+ exportConfigCollection.setTotalRecords(1);
+ poLines.get(0).getVendorDetail().setVendorAccount(null);
+ doReturn(poLines).when(ordersService).getPoLinesByQuery(cqlString);
+ doReturn(exportConfigCollection).when(dataExportSpringClient).getExportConfigs(configSql);
+ doReturn(orders).when(ordersService).getPurchaseOrdersByIds(anyList());
+ doReturn("test1").when(purchaseOrdersToEdifactMapper).convertOrdersToEdifact(any(), any(), anyString());
+
+ var exportConfig = getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT, true);
+ JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(exportConfig));
+
+ Assertions.assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
+ verify(ordersService).getPoLinesByQuery(cqlString);
+ verify(ordersService).getPurchaseOrdersByIds(anyList());
+ }
+
+ @Test
+ void testEdifactOrdersExportDefaultConfigWithTwoExportConfigs() throws Exception {
+ JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
+ String cqlString = "(purchaseOrder.workflowStatus==Open)" +
+ " AND (purchaseOrder.vendor==d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1)" +
+ " AND (cql.allRecords=1 NOT purchaseOrder.manualPo==true)" +
+ " AND (automaticExport==true)" +
+ " AND (cql.allRecords=1 NOT lastEDIExportDate=\"\")" +
+ " AND (acquisitionMethod==(\"306489dd-0053-49ee-a068-c316444a8f55\"))" +
+ " AND (cql.allRecords=1 NOT vendorDetail.vendorAccount==(\"org1\" or \"org2\"))";
+ String configSql = "configName==EDIFACT_ORDERS_EXPORT_d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1*";
+ ExportConfigCollection exportConfigCollection = objectMapper.readValue(getMockData(DATA_EXPORT_CONFIGS_PATH), ExportConfigCollection.class);
+ poLines.get(0).getVendorDetail().setVendorAccount(null);
+ doReturn(poLines).when(ordersService).getPoLinesByQuery(cqlString);
+ doReturn(exportConfigCollection).when(dataExportSpringClient).getExportConfigs(configSql);
+ doReturn(orders).when(ordersService).getPurchaseOrdersByIds(anyList());
+ doReturn("test1").when(purchaseOrdersToEdifactMapper).convertOrdersToEdifact(any(), any(), anyString());
+
+ var exportConfig = getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT, true);
+ JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(exportConfig));
+
+ Assertions.assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
+ verify(ordersService).getPoLinesByQuery(cqlString);
+ verify(ordersService).getPurchaseOrdersByIds(anyList());
+ }
+
+ protected ObjectNode getEdifactExportConfig(String path, boolean isDefaultConfig) throws IOException {
+ return getEdifactExportConfig(path).put("isDefaultConfig", isDefaultConfig);
+ }
+
+}
diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTaskletAbstractTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTaskletAbstractTest.java
new file mode 100644
index 000000000..00a9b0753
--- /dev/null
+++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTaskletAbstractTest.java
@@ -0,0 +1,101 @@
+package org.folio.dew.batch.acquisitions.edifact.jobs;
+
+import static org.folio.dew.utils.TestUtils.getMockData;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.folio.dew.BaseBatchTest;
+import org.folio.dew.batch.acquisitions.edifact.PurchaseOrdersToEdifactMapper;
+import org.folio.dew.batch.acquisitions.edifact.services.OrdersService;
+import org.folio.dew.client.DataExportSpringClient;
+import org.junit.jupiter.api.Test;
+import org.springframework.batch.core.BatchStatus;
+import org.springframework.batch.core.ExitStatus;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.JobParameters;
+import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.test.JobLauncherTestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+abstract class MapToEdifactTaskletAbstractTest extends BaseBatchTest {
+
+ protected static final String SAMPLE_PURCHASE_ORDERS_PATH = "edifact/acquisitions/purchase_order_collection.json";
+ protected static final String SAMPLE_PO_LINES_PATH = "edifact/acquisitions/po_line_collection.json";
+ protected static final String MAP_TO_EDIFACT_STEP = "mapToEdifactStep";
+
+ protected static final String SAMPLE_EDI_ORDERS_EXPORT = "edifact/edifactOrdersExport.json";
+ private static final String SAMPLE_EDI_ORDERS_EXPORT_MISSING_FIELDS = "edifact/edifactOrdersExportWithoutRequiredFields.json";
+ private static final String SAMPLE_EDI_ORDERS_EXPORT_MISSING_PORT = "edifact/edifactOrdersExportWithoutPort.json";
+
+ @MockBean
+ protected OrdersService ordersService;
+ @MockBean
+ protected DataExportSpringClient dataExportSpringClient;
+ @MockBean
+ protected PurchaseOrdersToEdifactMapper purchaseOrdersToEdifactMapper;
+ @Autowired
+ protected ObjectMapper objectMapper;
+ protected Job edifactExportJob;
+
+ @Test
+ void testEdifactExportMissingRequiredFields() throws Exception {
+ JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
+ JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT_MISSING_FIELDS)));
+ var status = new ArrayList<>(jobExecution.getStepExecutions()).get(0).getStatus();
+
+ assertEquals(BatchStatus.FAILED, status);
+ assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode());
+ assertThat(jobExecution.getExitStatus().getExitDescription()).contains("Export configuration is incomplete, missing library EDI code/Vendor EDI code");
+ }
+
+ @Test
+ void testEdifactExportMissingFtpPort() throws Exception {
+ JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
+ JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT_MISSING_PORT)));
+ var status = new ArrayList<>(jobExecution.getStepExecutions()).get(0).getStatus();
+
+ assertEquals(BatchStatus.FAILED, status);
+ assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode());
+ assertThat(jobExecution.getExitStatus().getExitDescription()).contains("Export configuration is incomplete, missing FTP/SFTP Port");
+ }
+
+ @Test
+ void testEdifactExportPurchaseOrdersNotFound() throws Exception {
+ JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
+ doReturn(List.of()).when(ordersService).getPoLinesByQuery(anyString());
+
+ JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT)));
+
+ assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode());
+ assertThat(jobExecution.getExitStatus().getExitDescription()).contains("Entities not found: PurchaseOrder");
+ verify(ordersService).getPoLinesByQuery(anyString());
+ verify(ordersService).getPurchaseOrdersByIds(anyList());
+ }
+
+ protected ObjectNode getEdifactExportConfig(String path) throws IOException {
+ return (ObjectNode) objectMapper.readTree(getMockData(path));
+ }
+
+ protected JobParameters getJobParameters(ObjectNode edifactExport) {
+ return new JobParametersBuilder()
+ .addString("jobId", UUID.randomUUID().toString())
+ .addString("edifactOrdersExport", edifactExport.toString())
+ .addString("jobName", "000015")
+ .toJobParameters();
+ }
+
+}
diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTaskletTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTaskletTest.java
deleted file mode 100644
index 7e205293c..000000000
--- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTaskletTest.java
+++ /dev/null
@@ -1,213 +0,0 @@
-package org.folio.dew.batch.acquisitions.edifact.jobs;
-
-import static org.folio.dew.utils.TestUtils.getMockData;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyList;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.assertj.core.api.Assertions;
-import org.folio.dew.BaseBatchTest;
-import org.folio.dew.batch.acquisitions.edifact.PurchaseOrdersToEdifactMapper;
-import org.folio.dew.batch.acquisitions.edifact.services.OrdersService;
-import org.folio.dew.client.DataExportSpringClient;
-import org.folio.dew.domain.dto.ExportConfigCollection;
-import org.folio.dew.domain.dto.PoLine;
-import org.folio.dew.domain.dto.PoLineCollection;
-import org.folio.dew.domain.dto.PurchaseOrder;
-import org.folio.dew.domain.dto.PurchaseOrderCollection;
-import org.junit.jupiter.api.Test;
-import org.springframework.batch.core.ExitStatus;
-import org.springframework.batch.core.Job;
-import org.springframework.batch.core.JobExecution;
-import org.springframework.batch.core.JobParameters;
-import org.springframework.batch.core.JobParametersBuilder;
-import org.springframework.batch.test.JobLauncherTestUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.mock.mockito.MockBean;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-class MapToEdifactTaskletTest extends BaseBatchTest {
-
- @MockBean
- private OrdersService ordersService;
- @MockBean
- DataExportSpringClient dataExportSpringClient;
- @MockBean
- private PurchaseOrdersToEdifactMapper purchaseOrdersToEdifactMapper;
-
- @Autowired
- protected ObjectMapper objectMapper;
- @Autowired
- Job edifactExportJob;
-
- @Test
- void edifactExportJobTestSuccess() throws Exception {
- JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
- List orders = objectMapper.readValue(getMockData(
- "edifact/acquisitions/purchase_order_collection.json"), PurchaseOrderCollection.class).getPurchaseOrders();
- List poLines = objectMapper.readValue(getMockData("edifact/acquisitions/po_line_collection.json"),
- PoLineCollection.class).getPoLines();
- String cqlString = "purchaseOrder.workflowStatus==Open" +
- " AND purchaseOrder.vendor==d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1" +
- " AND (cql.allRecords=1 NOT purchaseOrder.manualPo==true)" +
- " AND automaticExport==true" +
- " AND (cql.allRecords=1 NOT lastEDIExportDate=\"\")" +
- " AND acquisitionMethod==(\"306489dd-0053-49ee-a068-c316444a8f55\")" +
- " AND vendorDetail.vendorAccount==(\"BRXXXXX-01\")";
- doReturn(poLines).when(ordersService).getPoLinesByQuery(cqlString);
- doReturn(orders).when(ordersService).getPurchaseOrdersByIds(anyList());
- doReturn("test1").when(purchaseOrdersToEdifactMapper).convertOrdersToEdifact(any(), any(), anyString());
-
- JobExecution jobExecution = testLauncher.launchStep("mapToEdifactStep", getJobParameters(false));
-
- Assertions.assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
- verify(ordersService).getPoLinesByQuery(cqlString);
- verify(ordersService).getPurchaseOrdersByIds(anyList());
- }
-
- @Test
- void testShouldReturnEdifactExceptionBecauseRequiredFieldsIsNull() throws Exception {
- JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
- JobExecution jobExecution = testLauncher.launchStep("mapToEdifactStep", getJobParametersWithoutRequiredFields());
- String expectedMessage = "Export configuration is incomplete, missing library EDI code/Vendor EDI code";
- var status = new ArrayList<>(jobExecution.getStepExecutions()).get(0).getStatus().name();
-
- assertTrue(jobExecution.getExitStatus().getExitDescription().contains(expectedMessage));
- assertEquals("FAILED", status);
- }
-
- @Test
- void testShouldReturnEdifactExceptionBecauseFtpPortIsNull() throws Exception {
- JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
- JobExecution jobExecution = testLauncher.launchStep("mapToEdifactStep", getJobParametersWithoutPort());
- String expectedMessage = "Export configuration is incomplete, missing FTP/SFTP Port";
- var status = new ArrayList<>(jobExecution.getStepExecutions()).get(0).getStatus().name();
-
- assertTrue(jobExecution.getExitStatus().getExitDescription().contains(expectedMessage));
- assertEquals("FAILED", status);
- }
-
- @Test
- void edifactExportJobIfDefaultConfigTestSuccess() throws Exception {
- JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
- List orders = objectMapper.readValue(getMockData(
- "edifact/acquisitions/purchase_order_collection.json"), PurchaseOrderCollection.class).getPurchaseOrders();
- List poLines = objectMapper.readValue(getMockData("edifact/acquisitions/po_line_collection.json"),
- PoLineCollection.class).getPoLines();
- String cqlString = "purchaseOrder.workflowStatus==Open" +
- " AND purchaseOrder.vendor==d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1" +
- " AND (cql.allRecords=1 NOT purchaseOrder.manualPo==true)" +
- " AND automaticExport==true" +
- " AND (cql.allRecords=1 NOT lastEDIExportDate=\"\")" +
- " AND acquisitionMethod==(\"306489dd-0053-49ee-a068-c316444a8f55\")";
- String configSql = "configName==EDIFACT_ORDERS_EXPORT_d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1*";
- ExportConfigCollection exportConfigCollection = new ExportConfigCollection();
- exportConfigCollection.setTotalRecords(1);
- poLines.get(0).getVendorDetail().setVendorAccount(null);
- doReturn(poLines).when(ordersService).getPoLinesByQuery(cqlString);
- doReturn(exportConfigCollection).when(dataExportSpringClient).getExportConfigs(configSql);
- doReturn(orders).when(ordersService).getPurchaseOrdersByIds(anyList());
- doReturn("test1").when(purchaseOrdersToEdifactMapper).convertOrdersToEdifact(any(), any(), anyString());
-
- JobExecution jobExecution = testLauncher.launchStep("mapToEdifactStep", getJobParameters(true));
-
- Assertions.assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
- verify(ordersService).getPoLinesByQuery(cqlString);
- verify(ordersService).getPurchaseOrdersByIds(anyList());
- }
-
- @Test
- void edifactExportJobIfDefaultConfigNotOneTestSuccess() throws Exception {
- JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
- List orders = objectMapper.readValue(getMockData(
- "edifact/acquisitions/purchase_order_collection.json"), PurchaseOrderCollection.class).getPurchaseOrders();
- List poLines = objectMapper.readValue(getMockData("edifact/acquisitions/po_line_collection.json"),
- PoLineCollection.class).getPoLines();
- String cqlString = "purchaseOrder.workflowStatus==Open" +
- " AND purchaseOrder.vendor==d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1" +
- " AND (cql.allRecords=1 NOT purchaseOrder.manualPo==true)" +
- " AND automaticExport==true" +
- " AND (cql.allRecords=1 NOT lastEDIExportDate=\"\")" +
- " AND acquisitionMethod==(\"306489dd-0053-49ee-a068-c316444a8f55\")" +
- " AND cql.allRecords=1 NOT vendorDetail.vendorAccount==(\"org1\" OR \"org2\")";
- String configSql = "configName==EDIFACT_ORDERS_EXPORT_d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1*";
- ExportConfigCollection exportConfigCollection = objectMapper.readValue(getMockData("edifact/dataExportConfigs.json"), ExportConfigCollection.class);
- poLines.get(0).getVendorDetail().setVendorAccount(null);
- doReturn(poLines).when(ordersService).getPoLinesByQuery(cqlString);
- doReturn(exportConfigCollection).when(dataExportSpringClient).getExportConfigs(configSql);
- doReturn(orders).when(ordersService).getPurchaseOrdersByIds(anyList());
- doReturn("test1").when(purchaseOrdersToEdifactMapper).convertOrdersToEdifact(any(), any(), anyString());
-
- JobExecution jobExecution = testLauncher.launchStep("mapToEdifactStep", getJobParameters(true));
-
- Assertions.assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
- verify(ordersService).getPoLinesByQuery(cqlString);
- verify(ordersService).getPurchaseOrdersByIds(anyList());
- }
-
- @Test
- void purchaseOrdersNotFound() throws Exception {
- JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob);
- List poLines = List.of();
- String cqlString = "purchaseOrder.workflowStatus==Open" +
- " AND purchaseOrder.vendor==d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1" +
- " AND (cql.allRecords=1 NOT purchaseOrder.manualPo==true)" +
- " AND automaticExport==true" +
- " AND (cql.allRecords=1 NOT lastEDIExportDate=\"\")" +
- " AND acquisitionMethod==(\"306489dd-0053-49ee-a068-c316444a8f55\")" +
- " AND vendorDetail.vendorAccount==(\"BRXXXXX-01\")";
- doReturn(poLines).when(ordersService).getPoLinesByQuery(cqlString);
-
- JobExecution jobExecution = testLauncher.launchStep("mapToEdifactStep", getJobParameters(false));
-
- assertThat(jobExecution.getExitStatus().getExitDescription(), containsString("Orders for export not found"));
- verify(ordersService).getPoLinesByQuery(cqlString);
- verify(ordersService).getPurchaseOrdersByIds(anyList());
- }
-
- private JobParameters getJobParameters(boolean isDefaultConfig) throws IOException {
- JobParametersBuilder paramsBuilder = new JobParametersBuilder();
- var edifactOrdersExportJson = (ObjectNode) objectMapper.readTree(getMockData("edifact/edifactOrdersExport.json"));
- edifactOrdersExportJson.put("isDefaultConfig", isDefaultConfig);
-
- paramsBuilder.addString("jobId", UUID.randomUUID().toString());
- paramsBuilder.addString("edifactOrdersExport", edifactOrdersExportJson.toString());
- paramsBuilder.addString("jobName", "000015");
-
- return paramsBuilder.toJobParameters();
- }
-
- private JobParameters getJobParametersWithoutRequiredFields() throws IOException {
- JobParametersBuilder paramsBuilder = new JobParametersBuilder();
- var edifactOrdersExportJson = (ObjectNode) objectMapper.readTree(getMockData("edifact/edifactOrdersExportWithoutRequiredFields.json"));
- edifactOrdersExportJson.put("isDefaultConfig", false);
-
- paramsBuilder.addString("jobId", UUID.randomUUID().toString());
- paramsBuilder.addString("edifactOrdersExport", edifactOrdersExportJson.toString());
- paramsBuilder.addString("jobName", "000015");
-
- return paramsBuilder.toJobParameters();
- }
-
- private JobParameters getJobParametersWithoutPort() throws IOException {
- JobParametersBuilder paramsBuilder = new JobParametersBuilder();
-
- paramsBuilder.addString("edifactOrdersExport", getMockData("edifact/edifactOrdersExportWithoutPort.json"));
- paramsBuilder.addString("jobId", UUID.randomUUID().toString());
-
- return paramsBuilder.toJobParameters();
- }
-}
diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTaskletTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTaskletTest.java
index 7dac5161b..1bd2fcbeb 100644
--- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTaskletTest.java
+++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTaskletTest.java
@@ -29,6 +29,7 @@
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.annotation.DirtiesContext;
@@ -36,6 +37,7 @@
class SaveToFileStorageTaskletTest extends BaseBatchTest {
@Autowired
+ @Qualifier("edifactOrdersExportJob")
private Job edifactExportJob;
@MockBean
private SFTPObjectStorageRepository sftpObjectStorageRepository;
diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletFailTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletFailTest.java
index ec0b6f130..281569784 100644
--- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletFailTest.java
+++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletFailTest.java
@@ -23,6 +23,7 @@
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.annotation.DirtiesContext;
@@ -30,6 +31,7 @@
class SaveToMinioTaskletFailTest extends BaseBatchTest {
@Autowired
+ @Qualifier("edifactOrdersExportJob")
private Job edifactExportJob;
@MockBean
private OrganizationsService organizationsService;
@@ -78,4 +80,4 @@ protected JobLauncherTestUtils createTestLauncher(Job job) {
testLauncher.setJobRepository(jobRepository);
return testLauncher;
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletTest.java
index 907bea346..daa6111ef 100644
--- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletTest.java
+++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletTest.java
@@ -20,6 +20,7 @@
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.annotation.DirtiesContext;
@@ -27,6 +28,7 @@
class SaveToMinioTaskletTest extends BaseBatchTest {
@Autowired
+ @Qualifier("edifactOrdersExportJob")
private Job edifactExportJob;
@MockBean
private OrganizationsService organizationsService;
@@ -69,4 +71,4 @@ protected JobLauncherTestUtils createTestLauncher(Job job) {
testLauncher.setJobRepository(jobRepository);
return testLauncher;
}
-}
\ No newline at end of file
+}
diff --git a/src/test/resources/edifact/acquisitions/composite_purchase_order.json b/src/test/resources/edifact/acquisitions/composite_purchase_order.json
index 621fbcc33..f5e6a3d7b 100644
--- a/src/test/resources/edifact/acquisitions/composite_purchase_order.json
+++ b/src/test/resources/edifact/acquisitions/composite_purchase_order.json
@@ -86,7 +86,12 @@
"vendorDetail": {
"instructions": "",
"vendorAccount": "BRXXXXX-01",
- "referenceNumbers": []
+ "referenceNumbers": [
+ {
+ "refNumber": "ORD1000",
+ "refNumberType": "Vendor order reference number"
+ }
+ ]
},
"metadata": {
"createdDate": "2021-12-28T08:17:02.171+00:00",
diff --git a/src/test/resources/edifact/acquisitions/comprehensive_composite_purchase_order.json b/src/test/resources/edifact/acquisitions/comprehensive_composite_purchase_order.json
index 1e40c4d3c..46c7f7365 100644
--- a/src/test/resources/edifact/acquisitions/comprehensive_composite_purchase_order.json
+++ b/src/test/resources/edifact/acquisitions/comprehensive_composite_purchase_order.json
@@ -147,7 +147,12 @@
"vendorDetail": {
"instructions": "",
"vendorAccount": "BRXXXXX-01",
- "referenceNumbers": []
+ "referenceNumbers": [
+ {
+ "refNumber": "ORD1001",
+ "refNumberType": "Vendor order reference number"
+ }
+ ]
},
"metadata": {
"createdDate": "2021-12-28T08:17:02.171+00:00",
@@ -251,6 +256,10 @@
"refNumber": "1234",
"refNumberType": "Vendor title number",
"vendorDetailsSource": "OrderLine"
+ },
+ {
+ "refNumber": "ORD1002",
+ "refNumberType": "Vendor order reference number"
}
]
},
diff --git a/src/test/resources/edifact/acquisitions/edifact_claims_result.edi b/src/test/resources/edifact/acquisitions/edifact_claims_result.edi
new file mode 100644
index 000000000..13f2fab02
--- /dev/null
+++ b/src/test/resources/edifact/acquisitions/edifact_claims_result.edi
@@ -0,0 +1,109 @@
+UNA:+.? '
+UNB+UNOC:3+901494200:014+0142948:31B+ddmmyy:hhmm+{fileId}'
+UNH+10000+ORDERS:D:96A:UN:EAN008'
+BGM+220+10000+9'
+DTM+137::102'
+NAD+BY+901494200::014++Bockenheimer Landstr. 134-13'
+NAD+SU+0142948::31B'
+RFF+API:BRXXXXX-01'
+CUX+2:USD:9'
+LIN+1++9783319643991:EN'
+PIA+5+9783319643991:IS'
+IMD+L+009+:::Moutinho, Luiz'
+IMD+L+050+:::Futures, biometrics and neuroscience research Luiz Moutinho, Mladen So'
+IMD+L+050+:::kele, editors'
+IMD+L+080+:::S1?:C1?:E1'
+IMD+L+080+:::S2?:C2'
+IMD+L+109+:::Palgrave Macmillan'
+QTY+21:1'
+PRI+AAF:1.8'
+PRI+AAB:USD 2.00'
+CUX+2:USD:9'
+RFF+LI:10000-1'
+RFF+BFN:USHIST'
+RFF+SNA:ORD1000'
+LOC+7+KU/CC/DI/M::92'
+UNS+S'
+CNT+1:1'
+CNT+2:1'
+UNT+27+10000'
+UNH+10001+ORDERS:D:96A:UN:EAN008'
+BGM+224+10001+9'
+DTM+137::102'
+NAD+BY+901494200::014++Bockenheimer Landstr. 134-13'
+NAD+SU+0142948::31B'
+RFF+API:BRXXXXX-01'
+CUX+2:USD:9'
+LIN+1++9783319643991:EN'
+PIA+5+9783319643991:IM'
+IMD+L+009+:::Moutinho, Luiz'
+IMD+L+050+:::Futures, biometrics and neuroscience research Luiz Moutinho, Mladen So'
+IMD+L+050+:::kele, editors'
+IMD+L+080+:::S3?:E3'
+IMD+L+109+:::Palgrave Macmillan'
+IMD+L+170+:::[2017]'
+QTY+21:1'
+PRI+AAF:1.8'
+PRI+AAB:USD 2.00'
+CUX+2:USD:9'
+RFF+LI:10001-1'
+RFF+BFN:USHIST'
+RFF+BFN:USHIST2'
+RFF+BFN:USHIST3'
+RFF+BFN:USHIST4'
+RFF+BFN:USHIST5'
+RFF+BFN:USHIST6'
+RFF+BFN:USHIST7'
+RFF+BFN:USHIST8'
+RFF+BFN:USHIST9'
+RFF+SNA:ORD1001'
+LOC+7+KU/CC/DI/M::92'
+LIN+2++9783319643991:EN'
+PIA+5+9783319643991:IB'
+PIA+1+OTA-1031 Otá Records:MF'
+IMD+L+009+:::Sosa, Omar'
+IMD+L+009+:::Keita, Seckou, 1977-'
+IMD+L+050+:::Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmo'
+IMD+L+050+:::d tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim'
+IMD+L+050+::: veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex '
+IMD+L+050+:::ea commodo consequat. Duis aute irure dolor in reprehenderit in volupt'
+IMD+L+050+:::ate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint '
+IMD+L+050+:::occaecat cupidatat non proident, sunt in culpa qui officia deserunt mo'
+IMD+L+050+:::llit anim id est laborum.'
+IMD+L+080+:::C4?:E4'
+IMD+L+109+:::Otá Records, '
+IMD+L+180+:::Book'
+QTY+21:3'
+PRI+AAF:1.5'
+PRI+AAB:USD 1.65'
+CUX+2:USD:9'
+FTX+LIN++::+Test vendor instruction text.'
+RFF+LI:10001-2'
+RFF+BFN:USHIST'
+RFF+BFN:AFRICAHIST?:Elec'
+RFF+SNA:ORD1002'
+RFF+SLI:1234'
+LOC+7+KU/CC/DI/M::92'
+LOC+7+KU/CC/DI/M::92'
+UNS+S'
+CNT+1:4'
+CNT+2:2'
+UNT+62+10001'
+UNH+10002+ORDERS:D:96A:UN:EAN008'
+BGM+220+10002+9'
+DTM+137::102'
+NAD+BY+901494200::014++Bockenheimer Landstr. 134-13'
+NAD+SU+0142948::31B'
+CUX+2:USD:9'
+LIN+1++:'
+IMD+L+050+:::empty'
+IMD+L+080+:::S5?:E5'
+IMD+L+080+:::C5'
+QTY+21:0'
+CUX+2:USD:9'
+RFF+LI:10002-1'
+UNS+S'
+CNT+1:0'
+CNT+2:1'
+UNT+17+10002'
+UNZ+3+{fileId}'
diff --git a/src/test/resources/edifact/acquisitions/edifact_orders_result.edi b/src/test/resources/edifact/acquisitions/edifact_orders_result.edi
index 14572e280..a9fb8d228 100644
--- a/src/test/resources/edifact/acquisitions/edifact_orders_result.edi
+++ b/src/test/resources/edifact/acquisitions/edifact_orders_result.edi
@@ -19,11 +19,12 @@ PRI+AAB:USD 2.00'
CUX+2:USD:9'
RFF+LI:10000-1'
RFF+BFN:USHIST'
+RFF+SLI:ORD1000'
LOC+7+KU/CC/DI/M::92'
UNS+S'
CNT+1:1'
CNT+2:1'
-UNT+24+10000'
+UNT+25+10000'
UNH+10001+ORDERS:D:96A:UN:EAN008'
BGM+224+10001+9'
DTM+137::102'
@@ -76,12 +77,13 @@ RFF+LI:10001-2'
RFF+BFN:USHIST'
RFF+BFN:AFRICAHIST?:Elec'
RFF+SLI:1234'
+RFF+SLI:ORD1002'
LOC+7+KU/CC/DI/M::92'
LOC+7+KU/CC/DI/M::92'
UNS+S'
CNT+1:4'
CNT+2:2'
-UNT+58+10001'
+UNT+59+10001'
UNH+10002+ORDERS:D:96A:UN:EAN008'
BGM+220+10002+9'
DTM+137::102'
diff --git a/src/test/resources/edifact/acquisitions/pieces_collection.json b/src/test/resources/edifact/acquisitions/pieces_collection.json
new file mode 100644
index 000000000..1564e88b3
--- /dev/null
+++ b/src/test/resources/edifact/acquisitions/pieces_collection.json
@@ -0,0 +1,38 @@
+{
+ "pieces": [
+ {
+ "id": "5e317dc2-deeb-4429-b2a1-91e5cd0fd5f7",
+ "displaySummary": "Tutorial Volume 5",
+ "comment": "Special Edition",
+ "format": "Electronic",
+ "itemId": "522a501a-56b5-48d9-b28a-3a8f02482d97",
+ "bindItemId": "779b89f7-6c0f-42d0-bf50-4b0e30958d2d",
+ "locationId": "53cf956f-c1df-410b-8bea-27f712cca7c0",
+ "poLineId": "d471d766-8dbb-4609-999a-02681dea6c22",
+ "titleId": "9a665b22-9fe5-4c95-b4ee-837a5433c95d",
+ "receivingTenantId": "diku",
+ "displayOnHolding": false,
+ "displayToPublic": false,
+ "enumeration": "Volume 1",
+ "chronology": "2021",
+ "barcode": "0987654111",
+ "accessionNumber": "2015.1",
+ "callNumber": "BF2050 .M335 1999",
+ "discoverySuppress": false,
+ "copyNumber": "94753",
+ "receivingStatus": "Late",
+ "supplement": true,
+ "isBound": true,
+ "receiptDate": "2018-10-11T00:00:00.000Z",
+ "claimingInterval": 0,
+ "internalNote": "Internal note for Send Claim action",
+ "externalNote": "External note for Send Claim action to share with Vendor",
+ "statusUpdatedDate": "2018-10-11T00:00:00.000Z",
+ "metadata": {
+ "createdDate": "2018-08-19T00:00:00.000+0000",
+ "createdByUserId": "28d1057c-d137-11e8-a8d5-f2801f1b9fd1"
+ }
+ }
+
+ ]
+}
diff --git a/src/test/resources/edifact/acquisitions/pieces_collection_mixed.json b/src/test/resources/edifact/acquisitions/pieces_collection_mixed.json
new file mode 100644
index 000000000..8ab9d88a2
--- /dev/null
+++ b/src/test/resources/edifact/acquisitions/pieces_collection_mixed.json
@@ -0,0 +1,235 @@
+{
+ "pieces": [
+ {
+ "id": "5e317dc2-deeb-4429-b2a1-91e5cd0fd5f1",
+ "poLineId": "f7cebba9-900d-4e46-ac96-9dfdb6da214e",
+ "receivingStatus": "Late",
+ "displaySummary": "S1",
+ "chronology": "C1",
+ "enumeration": "E1",
+ "comment": "Special Edition",
+ "format": "Electronic",
+ "itemId": "522a501a-56b5-48d9-b28a-3a8f02482d97",
+ "bindItemId": "779b89f7-6c0f-42d0-bf50-4b0e30958d2d",
+ "locationId": "53cf956f-c1df-410b-8bea-27f712cca7c0",
+ "titleId": "9a665b22-9fe5-4c95-b4ee-837a5433c95d",
+ "receivingTenantId": "diku",
+ "displayOnHolding": false,
+ "displayToPublic": false,
+ "barcode": "0987654111",
+ "accessionNumber": "2015.1",
+ "callNumber": "BF2050 .M335 1999",
+ "discoverySuppress": false,
+ "copyNumber": "94753",
+ "supplement": true,
+ "isBound": true,
+ "receiptDate": "2018-10-11T00:00:00.000Z",
+ "claimingInterval": 0,
+ "internalNote": "Internal note for Send Claim action",
+ "externalNote": "External note for Send Claim action to share with Vendor",
+ "statusUpdatedDate": "2018-10-11T00:00:00.000Z",
+ "metadata": {
+ "createdDate": "2018-08-19T00:00:00.000+0000",
+ "createdByUserId": "28d1057c-d137-11e8-a8d5-f2801f1b9fd1"
+ }
+ },
+ {
+ "id": "5e317dc2-deeb-4429-b2a1-91e5cd0fd5f2",
+ "poLineId": "f7cebba9-900d-4e46-ac96-9dfdb6da214e",
+ "receivingStatus": "Late",
+ "displaySummary": "S2",
+ "chronology": "C2",
+ "enumeration": "",
+ "comment": "Special Edition",
+ "format": "Electronic",
+ "itemId": "522a501a-56b5-48d9-b28a-3a8f02482d97",
+ "bindItemId": "779b89f7-6c0f-42d0-bf50-4b0e30958d2d",
+ "locationId": "53cf956f-c1df-410b-8bea-27f712cca7c0",
+ "titleId": "9a665b22-9fe5-4c95-b4ee-837a5433c95d",
+ "receivingTenantId": "diku",
+ "displayOnHolding": false,
+ "displayToPublic": false,
+ "barcode": "0987654111",
+ "accessionNumber": "2015.1",
+ "callNumber": "BF2050 .M335 1999",
+ "discoverySuppress": false,
+ "copyNumber": "94753",
+ "supplement": true,
+ "isBound": true,
+ "receiptDate": "2018-10-11T00:00:00.000Z",
+ "claimingInterval": 0,
+ "internalNote": "Internal note for Send Claim action",
+ "externalNote": "External note for Send Claim action to share with Vendor",
+ "statusUpdatedDate": "2018-10-11T00:00:00.000Z",
+ "metadata": {
+ "createdDate": "2018-08-19T00:00:00.000+0000",
+ "createdByUserId": "28d1057c-d137-11e8-a8d5-f2801f1b9fd1"
+ }
+ },
+ {
+ "id": "5e317dc2-deeb-4429-b2a1-91e5cd0fd5f3",
+ "poLineId": "81b6503d-1f50-404d-a663-794b7f726ffd",
+ "receivingStatus": "Late",
+ "displaySummary": "S3",
+ "chronology": "",
+ "enumeration": "E3",
+ "comment": "Special Edition",
+ "format": "Electronic",
+ "itemId": "522a501a-56b5-48d9-b28a-3a8f02482d97",
+ "bindItemId": "779b89f7-6c0f-42d0-bf50-4b0e30958d2d",
+ "locationId": "53cf956f-c1df-410b-8bea-27f712cca7c0",
+ "titleId": "9a665b22-9fe5-4c95-b4ee-837a5433c95d",
+ "receivingTenantId": "diku",
+ "displayOnHolding": false,
+ "displayToPublic": false,
+ "barcode": "0987654111",
+ "accessionNumber": "2015.1",
+ "callNumber": "BF2050 .M335 1999",
+ "discoverySuppress": false,
+ "copyNumber": "94753",
+ "supplement": true,
+ "isBound": true,
+ "receiptDate": "2018-10-11T00:00:00.000Z",
+ "claimingInterval": 0,
+ "internalNote": "Internal note for Send Claim action",
+ "externalNote": "External note for Send Claim action to share with Vendor",
+ "statusUpdatedDate": "2018-10-11T00:00:00.000Z",
+ "metadata": {
+ "createdDate": "2018-08-19T00:00:00.000+0000",
+ "createdByUserId": "28d1057c-d137-11e8-a8d5-f2801f1b9fd1"
+ }
+ },
+ {
+ "id": "5e317dc2-deeb-4429-b2a1-91e5cd0fd5f3",
+ "poLineId": "c8d7a6a8-89b9-4711-9e1e-694b3e5ada72",
+ "receivingStatus": "Late",
+ "displaySummary": null,
+ "chronology": "C4",
+ "enumeration": "E4",
+ "comment": "Special Edition",
+ "format": "Electronic",
+ "itemId": "522a501a-56b5-48d9-b28a-3a8f02482d97",
+ "bindItemId": "779b89f7-6c0f-42d0-bf50-4b0e30958d2d",
+ "locationId": "53cf956f-c1df-410b-8bea-27f712cca7c0",
+ "titleId": "9a665b22-9fe5-4c95-b4ee-837a5433c95d",
+ "receivingTenantId": "diku",
+ "displayOnHolding": false,
+ "displayToPublic": false,
+ "barcode": "0987654111",
+ "accessionNumber": "2015.1",
+ "callNumber": "BF2050 .M335 1999",
+ "discoverySuppress": false,
+ "copyNumber": "94753",
+ "supplement": true,
+ "isBound": true,
+ "receiptDate": "2018-10-11T00:00:00.000Z",
+ "claimingInterval": 0,
+ "internalNote": "Internal note for Send Claim action",
+ "externalNote": "External note for Send Claim action to share with Vendor",
+ "statusUpdatedDate": "2018-10-11T00:00:00.000Z",
+ "metadata": {
+ "createdDate": "2018-08-19T00:00:00.000+0000",
+ "createdByUserId": "28d1057c-d137-11e8-a8d5-f2801f1b9fd1"
+ }
+ },
+ {
+ "id": "5e317dc2-deeb-4429-b2a1-91e5cd0fd5f4",
+ "poLineId": "31af5b5c-251e-4582-9896-2c9cef360284",
+ "receivingStatus": "Late",
+ "displaySummary": "S5",
+ "chronology": null,
+ "enumeration": "E5",
+ "comment": "Special Edition",
+ "format": "Electronic",
+ "itemId": "522a501a-56b5-48d9-b28a-3a8f02482d97",
+ "bindItemId": "779b89f7-6c0f-42d0-bf50-4b0e30958d2d",
+ "locationId": "53cf956f-c1df-410b-8bea-27f712cca7c0",
+ "titleId": "9a665b22-9fe5-4c95-b4ee-837a5433c95d",
+ "receivingTenantId": "diku",
+ "displayOnHolding": false,
+ "displayToPublic": false,
+ "barcode": "0987654111",
+ "accessionNumber": "2015.1",
+ "callNumber": "BF2050 .M335 1999",
+ "discoverySuppress": false,
+ "copyNumber": "94753",
+ "supplement": true,
+ "isBound": true,
+ "receiptDate": "2018-10-11T00:00:00.000Z",
+ "claimingInterval": 0,
+ "internalNote": "Internal note for Send Claim action",
+ "externalNote": "External note for Send Claim action to share with Vendor",
+ "statusUpdatedDate": "2018-10-11T00:00:00.000Z",
+ "metadata": {
+ "createdDate": "2018-08-19T00:00:00.000+0000",
+ "createdByUserId": "28d1057c-d137-11e8-a8d5-f2801f1b9fd1"
+ }
+ },
+ {
+ "id": "5e317dc2-deeb-4429-b2a1-91e5cd0fd5f4",
+ "poLineId": "31af5b5c-251e-4582-9896-2c9cef360284",
+ "receivingStatus": "Late",
+ "displaySummary": null,
+ "chronology": "C5",
+ "enumeration": null,
+ "comment": "Special Edition",
+ "format": "Electronic",
+ "itemId": "522a501a-56b5-48d9-b28a-3a8f02482d97",
+ "bindItemId": "779b89f7-6c0f-42d0-bf50-4b0e30958d2d",
+ "locationId": "53cf956f-c1df-410b-8bea-27f712cca7c0",
+ "titleId": "9a665b22-9fe5-4c95-b4ee-837a5433c95d",
+ "receivingTenantId": "diku",
+ "displayOnHolding": false,
+ "displayToPublic": false,
+ "barcode": "0987654111",
+ "accessionNumber": "2015.1",
+ "callNumber": "BF2050 .M335 1999",
+ "discoverySuppress": false,
+ "copyNumber": "94753",
+ "supplement": true,
+ "isBound": true,
+ "receiptDate": "2018-10-11T00:00:00.000Z",
+ "claimingInterval": 0,
+ "internalNote": "Internal note for Send Claim action",
+ "externalNote": "External note for Send Claim action to share with Vendor",
+ "statusUpdatedDate": "2018-10-11T00:00:00.000Z",
+ "metadata": {
+ "createdDate": "2018-08-19T00:00:00.000+0000",
+ "createdByUserId": "28d1057c-d137-11e8-a8d5-f2801f1b9fd1"
+ }
+ },
+ {
+ "id": "5e317dc2-deeb-4429-b2a1-91e5cd0fd5f4",
+ "poLineId": "31af5b5c-251e-4582-9896-2c9cef360284",
+ "receivingStatus": "Late",
+ "displaySummary": null,
+ "chronology": null,
+ "enumeration": null,
+ "comment": "Special Edition",
+ "format": "Electronic",
+ "itemId": "522a501a-56b5-48d9-b28a-3a8f02482d97",
+ "bindItemId": "779b89f7-6c0f-42d0-bf50-4b0e30958d2d",
+ "locationId": "53cf956f-c1df-410b-8bea-27f712cca7c0",
+ "titleId": "9a665b22-9fe5-4c95-b4ee-837a5433c95d",
+ "receivingTenantId": "diku",
+ "displayOnHolding": false,
+ "displayToPublic": false,
+ "barcode": "0987654111",
+ "accessionNumber": "2015.1",
+ "callNumber": "BF2050 .M335 1999",
+ "discoverySuppress": false,
+ "copyNumber": "94753",
+ "supplement": true,
+ "isBound": true,
+ "receiptDate": "2018-10-11T00:00:00.000Z",
+ "claimingInterval": 0,
+ "internalNote": "Internal note for Send Claim action",
+ "externalNote": "External note for Send Claim action to share with Vendor",
+ "statusUpdatedDate": "2018-10-11T00:00:00.000Z",
+ "metadata": {
+ "createdDate": "2018-08-19T00:00:00.000+0000",
+ "createdByUserId": "28d1057c-d137-11e8-a8d5-f2801f1b9fd1"
+ }
+ }
+ ]
+}