Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MODEXPW-528] Generate EDI file for claims #601

Merged
merged 17 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
<opencsv.version>5.7.1</opencsv.version>
<feign-jackson.version>12.1</feign-jackson.version>
<marc4j.version>2.9.2</marc4j.version>
<streamex.version>0.8.3</streamex.version>

<!-- Test properties-->
<junit-extensions.version>2.4.0</junit-extensions.version>
Expand Down Expand Up @@ -205,7 +206,7 @@
<dependency>
<groupId>io.xlate</groupId>
<artifactId>staedi</artifactId>
<version>1.24.0</version>
<version>1.25.2</version>
</dependency>

<dependency>
Expand Down Expand Up @@ -250,6 +251,12 @@
<type>pom</type>
</dependency>

<dependency>
<groupId>one.util</groupId>
<artifactId>streamex</artifactId>
<version>${streamex.version}</version>
</dependency>

<!-- Test dependencies -->

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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<String, List<Piece>> poLineToPieces, EdiFileConfig ediFileConfig) throws EDIStreamException {
int messageSegmentCount = 0;

messageSegmentCount++;
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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<Piece> pieces, EDIStreamWriter writer, int currentLineNumber, int quantityOrdered) throws EDIStreamException {
int messageSegmentCount = 0;

Map<String, ProductIdentifier> productTypeProductIdentifierMap = new HashMap<>();
Expand All @@ -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);
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -457,6 +512,21 @@ private String getExpenseClassCode(FundDistribution fundDistribution) {
return "";
}

private List<ReferenceNumberItem> getVendorReferenceNumbers(CompositePoLine poLine) {
return Optional.ofNullable(poLine.getVendorDetail())
.map(VendorDetail::getReferenceNumbers)
.orElse(new ArrayList<>());
}

private ReferenceNumberItem getAndRemoveVendorOrderNumber(List<ReferenceNumberItem> referenceNumberItems) {
var vendorOrderNumber = referenceNumberItems.stream()
.filter(r -> r.getRefNumberType() == ORDER_REFERENCE_NUMBER)
.findFirst()
.orElse(null);
referenceNumberItems.remove(vendorOrderNumber);
return vendorOrderNumber;
}

private List<Location> getLocations(CompositePoLine poLine) {
if (poLine.getLocations() != null) {
return poLine.getLocations();
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand All @@ -23,6 +26,10 @@ public PurchaseOrdersToEdifactMapper(CompositePOConverter compositePOConverter)
}

public String convertOrdersToEdifact(List<CompositePurchaseOrder> compPOs, VendorEdiOrdersExportConfig ediExportConfig, String jobName) throws EDIStreamException {
return convertOrdersToEdifact(compPOs, List.of(), ediExportConfig, jobName);
}

public String convertOrdersToEdifact(List<CompositePurchaseOrder> compPOs, List<Piece> pieces, VendorEdiOrdersExportConfig ediExportConfig, String jobName) throws EDIStreamException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();

EDIOutputFactory factory = EDIOutputFactory.newFactory();
Expand All @@ -44,9 +51,10 @@ public String convertOrdersToEdifact(List<CompositePurchaseOrder> 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++;
}

Expand All @@ -57,10 +65,6 @@ public String convertOrdersToEdifact(List<CompositePurchaseOrder> compPOs, Vendo
return stream.toString();
}

public byte[] convertOrdersToEdifactArray(List<CompositePurchaseOrder> 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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down

This file was deleted.

Loading
Loading