Before start
Dilono’s primary goals are:
-
Consume and produce EDIFACT messages with Java, in absolutely XML-free way.
-
Be a lightweight library with few dependencies so that any application can embed it.
-
Best development experience by enabling comprehensive autocompletion for segments, components, fields, etc.
-
Take care of a range of non-functional features like segment counting, numbers formatting, etc.
What does Dilono NOT suppose to:
-
Support other formats than EDIFACT.
-
Do any kind of communication protocols like AS2, SFTP, etc.
-
Implement Enterprise Integration Patterns EIP like routing, spilling, streaming, etc.
Getting started
Prerequisites
-
Java JDK 8+
-
A valid access token. Can be requested here.
-
Maven, Gradle or anything else to manage dependencies.
-
Dilono uses
slf4j-api
for logging. Any implementation of it has to be defined as a dependency to the application.
Declare maven dependencies
<servers>
<server>
<id>dilono-maven-public</id>
<username>${dilono.maven.user.name}</username>
<password>${dilono.maven.user.password}</password>
</server>
</servers>
<repositories>
<repository>
<id>dilono-maven-public</id>
<url>https://maven.dilono.com/releases</url>
</repository>
</repositories>
<!-- client to communicate with dilono cloud -->
<dependency>
<groupId>com.dilono</groupId>
<artifactId>dilono-edifact-client</artifactId>
<version>${versions.dilono-edifact}</version>
</dependency>
<!-- d96a models -->
<dependency>
<groupId>com.dilono</groupId>
<artifactId>dilono-edifact-d96a</artifactId>
<version>${versions.dilono-edifact}</version>
</dependency>
<!-- dilono test framework -->
<dependency>
<groupId>com.dilono</groupId>
<artifactId>dilono-edifact-test</artifactId>
<version>${versions.dilono-edifact}</version>
<scope>test</scope>
</dependency>
Configure Dilono client
@Bean
ECSClient ecsClient(@Value("${dilono.server.url}") final URL url,
@Value("${dilono.server.token.id}") final String tokenId,
@Value("${dilono.server.token.secret}") final String tokenSecret) {
return new ECSClientBuilder()
.withBaseUrl(url)
.withCredentials(ECSClientCredentials.token(tokenId, tokenSecret))
.build();
}
Note
|
Spring annotations are used in this example, but they don’t have to be used. Dilono does not depend on any frameworks. |
Consuming EDIFACT
package com.dilono.sample.basic;
import com.dilono.edifact.client.ECSClient;
import com.dilono.edifact.d96a.D96A;
import com.dilono.edifact.toolkit.DTMUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.util.List;
@Component
public class EdifactOrdersReader {
private final ECSClient client;
EdifactOrdersReader(ECSClient client) {
this.client = client;
}
List<Order> fromEdifact(final InputStream edifact) throws Exception {
return D96A.reader(client, IOUtils.toByteArray(edifact))
.orders(() -> new Order(), (orders, myOrder) -> orders
.bgm(bgm -> bgm
.data(bgm_ -> bgm_
.e1004DocumentMessageNumber(e1004 -> myOrder.setOrderNr(e1004))))
.dtm(dtm -> dtm
.must(dtm_ -> dtm_
.c507DateTimePeriod(c507 -> c507
.e2005DateTimePeriodQualifier(e2005 -> e2005.isEqualTo("137"))))
.data(dtm_ -> dtm_
.c507DateTimePeriod(c507 -> myOrder.setOrderCreatedAt(DTMUtils.parse(
c507.e2380DateTimePeriod(),
c507.e2379DateTimePeriodFormatQualifier())))))
.sg2(sg2 -> sg2
.must(sg2_ -> sg2_
.nad(nad -> nad
.e3035PartyQualifier(e3035 -> e3035.isEqualTo("SU"))))
.data(sg2_ -> sg2_
.nad(nad -> nad.data(nad_ -> nad_
.c082PartyIdentificationDetails(c082 -> c082
.e3039PartyIdIdentification(e3039 -> myOrder.setSupplierNr(e3039)))))))
.sg25(sg25 -> sg25.data(() -> newLineItem(myOrder), (sg25_, myLineItem) -> sg25_
.lin(lin -> lin.data(lin_ -> lin_
.e1082LineItemNumber(e1082 -> myLineItem.setPosition(e1082.intValue()))
.c212ItemNumberIdentification(c212 -> c212
.e7140ItemNumber(e7140 -> myLineItem.setSku(e7140)))))
.qty(qty -> qty
.must(qty_ -> qty_
.c186QuantityDetails(c186 -> c186
.e6063QuantityQualifier(e6063 -> e6063.isEqualTo("21"))))
.data(qty_ -> qty_.c186QuantityDetails(c186 -> c186
.e6060Quantity(e6060 -> myLineItem.setQty(e6060)))))
.sg28(sg28 -> sg28.data(sg28_ -> sg28_
.pri(pri -> pri
.must(pri_ -> pri_
.c509PriceInformation(c509 -> c509
.e5125PriceQualifier(e5126 -> e5126.isEqualTo("AAA"))))
.data(pri_ -> pri_
.c509PriceInformation(c509 -> c509
.e5118Price(e5118 -> myLineItem.setPiecePriceNet(e5118)))))
.pri(pri -> pri
.can(pri_ -> pri_
.c509PriceInformation(c509 -> c509.e5125PriceQualifier(e5126 -> e5126.isEqualTo("AAB"))))
.data(pri_ -> pri_
.c509PriceInformation(c509 -> c509
.e5118Price(e5118 -> myLineItem.setPiecePriceGross(e5118))))))))));
}
private Order.LineItem newLineItem(Order myOrder) {
final Order.LineItem LineItem = new Order.LineItem();
myOrder.getLineItems().add(LineItem);
return LineItem;
}
}
Producing EDIFACT
package com.dilono.sample.basic;
import com.dilono.edifact.client.ECSClient;
import com.dilono.edifact.d96a.D96A;
import com.dilono.edifact.toolkit.DTMUtils;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.List;
@Component
public class EdifactInvoicWriter {
private final ECSClient ecsClient;
public EdifactInvoicWriter(ECSClient ecsClient) {
this.ecsClient = ecsClient;
}
String toEdifact(final List<Invoice> invoices) throws Exception {
return D96A.writer(ecsClient)
.unb(unb -> unb
.unoc()
.version3()
.sender(sender -> sender
.id("0000000000001")
.codeQualifier("14"))
.recipient(recipient -> recipient
.id("0000000000002")
.codeQualifier("14"))
.interchangeId("ABCD123")
.interchangeTimestamp(now())
.eancom())
.invoic(() -> invoices.stream(), (invoice, invoic) -> invoic
.unh(unh -> unh.invoic().d96a().un().ean008())
.bgm(bgm -> bgm
.data(bgm_ -> bgm_
.c002DocumentMessageName(c002 -> c002
.e1001DocumentMessageNameCoded("380")) // Commercial invoice
.e1004DocumentMessageNumber(invoice.getInvoiceNr())))
.dtm(dtm -> dtm.data(dtm_ -> dtm_
.c507DateTimePeriod(c507 -> c507
.e2005DateTimePeriodQualifier("137") // Document/message date/time
.e2379DateTimePeriodFormatQualifier("102")
.e2380DateTimePeriod(DTMUtils.format(invoice.getInvoiceCreatedAt(), "102")))))
.dtm(dtm -> dtm.data(dtm_ -> dtm_
.c507DateTimePeriod(c507 -> c507
.e2005DateTimePeriodQualifier("35") // Delivery date/time, actual
.e2379DateTimePeriodFormatQualifier("102")
.e2380DateTimePeriod(DTMUtils.format(invoice.getDeliveryDateActual(), "102")))))
.sg1(sg1 -> sg1.data(sg1_ -> sg1_
.rff(rff -> rff.data(rff_ -> rff_
.c506Reference(c506 -> c506
.e1153ReferenceQualifier("ON") // Order number (purchase)
.e1154ReferenceNumber(invoice.getOrderNr()))))
.dtm(dtm -> dtm.data(dtm_ -> dtm_
.c507DateTimePeriod(c507 -> c507
.e2005DateTimePeriodQualifier("4") // Order date/time
.e2379DateTimePeriodFormatQualifier("102")
.e2380DateTimePeriod(DTMUtils.format(invoice.getOrderCreatedAt(), "102")))))))
.sg2(sg2 -> sg2.data(sg2_ -> sg2_
.nad(nad -> nad.data(nad_ -> nad_
.e3035PartyQualifier("SU") // Supplier
.c082PartyIdentificationDetails(c082 -> c082
.e3039PartyIdIdentification(invoice.getSupplierNr())
.e3055CodeListResponsibleAgencyCoded("9")))) // GLN
.sg3(sg3 -> sg3.data(sg3_ -> sg3_
.rff(rff -> rff.data(rff_ -> rff_
.c506Reference(c506 -> c506
.e1153ReferenceQualifier("VA") // VAT ID
.e1154ReferenceNumber(invoice.getSupplierVatId()))))))))
.sg2(sg2 -> sg2.data(sg2_ -> sg2_
.nad(nad -> nad.data(nad_ -> nad_
.e3035PartyQualifier("DP") // Ship To
.c082PartyIdentificationDetails(c082 -> c082
.e3039PartyIdIdentification(invoice.getShipToNr())
.e3055CodeListResponsibleAgencyCoded("9")))))) // GLN
.sg2(sg2 -> sg2.data(sg2_ -> sg2_
.nad(nad -> nad.data(nad_ -> nad_
.e3035PartyQualifier("IV") // Invoicee
.c082PartyIdentificationDetails(c082 -> c082
.e3039PartyIdIdentification(invoice.getInvoiceeNr())
.e3055CodeListResponsibleAgencyCoded("9")))) // GLN
.sg3(sg3 -> sg3.data(sg3_ -> sg3_
.rff(rff -> rff.data(rff_ -> rff_
.c506Reference(c506 -> c506
.e1153ReferenceQualifier("VA") // VAT ID
.e1154ReferenceNumber(invoice.getSupplierVatId()))))))))
.sg7(sg7 -> sg7.data(sg7_ -> sg7_
.cux(cux -> cux.data(cux_ -> cux_
.c5041CurrencyDetails(c504 -> c504
.e6347CurrencyDetailsQualifier("2") // Reference currency
.e6345CurrencyCoded("EUR"))))))
.sg8(sg8 -> sg8.data(sg8_ -> sg8_
.pat(pat -> pat.data(pat_ -> pat_
.e4279PaymentTermsTypeQualifier("1") // Basic
.c112TermsTimeInformation(c112 -> c112
.e2475PaymentTimeReferenceCoded("5") // Date of invoice
.e2151TypeOfPeriodCoded("D") // Day
.e2152NumberOfPeriods(30)))))) // 30 days
.sg25(() -> invoice.getLineItems().stream(), (lineItem, sg25) -> sg25.data(sg25_ -> sg25_
.lin(lin -> lin.data(lin_ -> lin_
.e1082LineItemNumber(lineItem.getPosition())
.c212ItemNumberIdentification(c212 -> c212
.e7140ItemNumber(lineItem.getSku())
.e7143ItemNumberTypeCoded("EN")))) // EAN
.qty(qty -> qty.data(qty_ -> qty_
.c186QuantityDetails(c186 -> c186
.e6063QuantityQualifier("47") // Invoiced quantity
.e6060Quantity(lineItem.getInvoicedQty().intValue()))))
.sg26(sg26 -> sg26.data(sg26_ -> sg26_
.moa(moa -> moa.data(moa_ -> moa_
.c516MonetaryAmount(c516 -> c516
.e5025MonetaryAmountTypeQualifier("203") // Line item amount
.e5004MonetaryAmount(lineItem.sumNetAmount())
.e6345CurrencyCoded("EUR")
.e6343CurrencyQualifier("4")))))) // Invoicing currency
.sg28(sg28 -> sg28.data(sg28_ -> sg28_
.pri(pri -> pri.data(pri_ -> pri_
.c509PriceInformation(c509 -> c509
.e5125PriceQualifier("AAA") // Calculation net
.e5118Price(lineItem.getPiecePriceNetAmount())
.e6411MeasureUnitQualifier(lineItem.getPiecePriceNetUnit())
.e5375PriceTypeCoded("CT") // Contract
.e5387PriceTypeQualifier("NTP")))))) // Net unit price
.sg33(sg33 -> sg33.data(sg33_ -> sg33_
.tax(tax -> tax.data(tax_ -> tax_
.e5283DutyTaxFeeFunctionQualifier("7") // Tax
.c241DutyTaxFeeType(c241 -> c241
.e5153DutyTaxFeeTypeCoded("VAT")) // Value added tax
.c243DutyTaxFeeDetail(c243 -> c243
.e5278DutyTaxFeeRate(String.valueOf(lineItem.getVatRate().intValue())))
.e5305DutyTaxFeeCategoryCoded("S"))))))) // Standard rate
.uns(uns -> uns.data(uns_ -> uns_
.e0081("S"))) // Detail/summary section separation
.cnt(cnt -> cnt.data(cnt_ -> cnt_
.c270Control(c270 -> c270
.e6069ControlQualifier("2") // Number of line items in message
.e6066ControlValue(invoice.getLineItems().size()))))
.sg48(sg48 -> sg48
.data(sg48_ -> sg48_
.moa(moa -> moa.data(moa_ -> moa_.c516MonetaryAmount(c516 -> c516
.e5025MonetaryAmountTypeQualifier("77") // Invoice amount
.e5004MonetaryAmount(invoice.sumAmount()))))))
.sg48(sg48 -> sg48
.data(sg48_ -> sg48_
.moa(moa -> moa.data(moa_ -> moa_.c516MonetaryAmount(c516 -> c516
.e5025MonetaryAmountTypeQualifier("125") // taxable amount
.e5004MonetaryAmount(invoice.sumTaxableAmount()))))))
.unt())
.unz()
.validate()
.writeToString();
}
private Date now() {
return Date.from(LocalDateTime.of(2020, 12, 31, 23, 20, 0).toInstant(ZoneOffset.UTC));
}
}
How does Dilono work?
High-Level Overview
Dilono is client-server model application. The server takes care of actual EDIFACT processing and supplies a model to the client. The client operates on that model like on an object graph. For instance, it reads certain segments and may ignore the rest if it’s not needed. In case of write operation, the client populates the model and sends to the server to convert to EDIFACT.
Consuming EDIFACT
Dilono declares operations in functional way through lambdas - consumers, suppliers, functions. Lambdas get evaluated under certain conditions. Let’s consider an example how Dilono converts a D96A ORDERS message to a POJO:
public List<Order> fromEdifact(byte[] edifact) throws Exception {
final List<Order> orderPojos = D96A.reader(client, edifact)
.orders(() -> new Order(), (orders, myOrder) -> orders // (1)
.bgm(bgm -> bgm // (2)
.data(bgm_ -> bgm_ // (3)
.e1004DocumentMessageNumber(e1004 -> myOrder.setOrderNr(e1004)))) // (4)
// ...
);
return orderPojos;
}
-
We start with the factory method
.orders()
whereas() → new Order()
a factory for POJOs being mapped to and(orders, myOrder) → …
is the actual mapping function with two arguments. An interchange may contain more than one message and the factory will create a new pojo for each message. The first argumentorders
is aReader
of the actual EDIFACT message and the second onemyOrder
is a new instance of POJO created by the factory() → new Order()
. -
The
.bgm()
method accepts a consumerbgm
of typeBGMBeginningOfMessageQuery
to query BGM segments of the message. -
The
bgm
query has a method.data()
that accepts a consumerbgm_
of typeBGMBeginningOfMessageStdReader
. -
The reader provides all methods, according to the subset specification, to read the data from the segment, its components and fields. So the
e1004 → myOrder.setOrderNr(e1004)
consumer sets the order number to themyOrder
POJO.
Now, let’s query NAD segments for buyer’s number conditionally:
public List<Order> fromEdifact(byte[] edifact) throws Exception {
final List<Order> orderPojos = D96A.reader(client, edifact)
.orders(() -> new Order(), (orders, myOrder) -> orders //
// ...
.sg2(sg2 -> sg2 // (1)
.must(sg2_ -> sg2_ // (2)
.nad(nad -> nad // (3)
.e3035PartyQualifier(e3035 -> e3035.isEqualTo("BY")))) // (4)
.data(sg2_ -> sg2_
.nad(nad -> nad.data(nad_ -> nad_
.c082PartyIdentificationDetails(c082 -> c082
.e3039PartyIdIdentification(e3039 -> myOrder.setBuyerNr(e3039))))))) // (5)
.sg2(sg2 -> sg2
.can(sg2_ -> sg2_ // (6)
.nad(nad -> nad
.e3035PartyQualifier(e3035 -> e3035.isEqualTo("SU"))))
.data(sg2_ -> sg2_
.nad(nad -> nad.data(nad_ -> nad_
.c082PartyIdentificationDetails(c082 -> c082
.e3039PartyIdIdentification(e3039 -> myOrder.setSupplierNr(e3039))))))));
//...
return orderPojos;
}
-
We query the Segment Group 2 of the ORDERS message by calling
.sg2()
on theorders
. -
The
sg2
query has a method.must()
that accepts a consumersg2_
of typeSegmentGroup2Condition
. -
The
sg2
query also provides access to all nested conditions, in this caseNADNameAndAddressCondition
. -
So we traverse until the field
e3035PartyQualifier
and eventually build a predicate for thesg2.data()
method. -
If
.must()
is evaluated totrue
, only then thesg2.data()
consumer will be evaluated. Otherwise, an exception is thrown. -
Declaration of
.can()
and.must()
predicates is exactly the same. However, the result is different - the.can()
doesn’t throw, and it makes.data()
optional.
Now, let’s loop over Segment Group 25 and map all line items:
public List<Order> fromEdifact(byte[] edifact) throws Exception {
final List<Order> orderPojos = D96A.reader(client, edifact)
.orders(() -> new Order(), (orders, myOrder) -> orders
// ...
.sg25(sg25 -> sg25.data(() -> newLineItem(myOrder), (sg25_, myLineItem) -> sg25_ // (1)
.lin(lin -> lin.data(lin_ -> lin_ // (2)
.e1082LineItemNumber(e1082 -> myLineItem.setPosition(e1082.intValue()))
.c212ItemNumberIdentification(c212 -> c212
.e7140ItemNumber(e7140 -> myLineItem.setSku(e7140)))))))); // (3)
// ...
return orderPojos;
}
-
The Segment Group 25 may occur many times and each time it there must be a new POJO that represents a line item. Because of this, the
.data()
is a consumer of two arguments. The first() → newLineItem(myOrder)
is a factory for POJO and the second(sg25_, myLineItem) → …
is the actual mapping function with two arguments - from and to. -
The
.lin()
query is evaluated for each LIN segment for each segment group. -
The value of the
e7140
field is set to POJO assku
field.
Note that all names of methods, variables, etc. correspond to the EDIFACT specification. This significantly increases readability of the code.
The source code for this example is available on GitHub.
Producing EDIFACT
Dilono produces EDIFACT in similar to consuming, functional way. Let’s consider an example how Dilono converts a POJO to D96A INVOIC message:
public String toEdifact(final List<Invoice> invoices) throws Exception {
return D96A.writer(client)
.una(una -> una.defaults()) // (1)
.unb(unb -> unb // (2)
.unoc()
.version3()
.sender(sender -> sender
.id("0000000000001")
.codeQualifier("14"))
.recipient(recipient -> recipient
.id("0000000000002")
.codeQualifier("14"))
.interchangeId("ABCD123")
.interchangeTimestamp(now())
.eancom())
.invoic(() -> invoices.stream(), (invoice, invoic) -> invoic // (3)
.unh(unh -> unh.invoic().d96a().un().ean008()) // (4)
.bgm(bgm -> bgm // (5)
.data(bgm_ -> bgm_
.c002DocumentMessageName(c002 -> c002
.e1001DocumentMessageNameCoded("380")) // Commercial invoice
.e1004DocumentMessageNumber(invoice.getInvoiceNr()))) // (6)
.dtm(dtm -> dtm.data(dtm_ -> dtm_
.c507DateTimePeriod(c507 -> c507
.e2005DateTimePeriodQualifier("137") // Document/message date/time
.e2379DateTimePeriodFormatQualifier("102")
.e2380DateTimePeriod(DTMUtils.format(invoice.getInvoiceCreatedAt(), "102"))))))
//...
.dumpToString();
}
-
Define default interchange delimiters.
-
Then the interchange header - sender, recipient, their code qualifiers, etc.
-
An interchange may contain more than one message. Therefore, the
.invoic()
method has two arguments. The first one() → invoices.stream()
is the source or from producer, and the second(invoice, invoic) → …
is actual mapping function. Dilono will create exactuly the same number of INVOIC message as the size of theinvoices
list. -
The
.unh()
method defined the header of the message. -
The
.bgm()
method accepts a consumerbgm
of typeBGMBeginningOfMessageWrite
to write data to the BGM segment has a method.data()
that accepts a consumerbgm_
of typeBGMBeginningOfMessageStdWriter
. -
The writer provides all methods, according to the subset specification, to write the data the segment, its components and fields. The returned value of
invoice.getInvoiceNr()
method will be set toe1004
field.
The example above produces the following:
UNA:+.? '
UNB+UNOB:2+0000000000001:14+0000000000002:14+210101:0020+ABCD123+++++EANCOM'
UNH+1+INVOIC:D:96A:UN:EAN008'
BGM+380+INV123456'
DTM+137:20201227:102'
Now, let’s add information about delivery party and invoicee.
For that, .sg2()
has to be repeated two times
public String toEdifact(final List<Invoice> invoices) throws Exception {
return D96A.writer(client)
.invoic(() -> invoices.stream(), (invoice, invoic) -> invoic
//...
.sg2(sg2 -> sg2.data(sg2_ -> sg2_ // (1)
.nad(nad -> nad.data(nad_ -> nad_
.e3035PartyQualifier("DP") // Ship To
.c082PartyIdentificationDetails(c082 -> c082
.e3039PartyIdIdentification(invoice.getShipToNr())
.e3055CodeListResponsibleAgencyCoded("9")))))) // GLN
.sg2(sg2 -> sg2.data(sg2_ -> sg2_ // (2)
.nad(nad -> nad.data(nad_ -> nad_
.e3035PartyQualifier("IV") // Invoicee
.c082PartyIdentificationDetails(c082 -> c082
.e3039PartyIdIdentification(invoice.getInvoiceeNr())
.e3055CodeListResponsibleAgencyCoded("9")))) // GLN
.sg3(sg3 -> sg3.data(sg3_ -> sg3_
.rff(rff -> rff.data(rff_ -> rff_ // (3)
.c506Reference(c506 -> c506
.e1153ReferenceQualifier("VA") // VAT ID
.e1154ReferenceNumber(invoice.getSupplierVatId()))))))))
//...
.unt())
.dumpToString();
}
-
The fist write operation
.sg2()
declares delivery party and its GLN. -
The second write operation
.sg2()
declares invoicee and its GLN. -
Additionally, the VAT ID for invoicee has to be provided. So, a nested
.sg3()
is declared.
The example above produces the following:
UNH+1+INVOIC:D:96A'
NAD+DP+0000000000004::9'
NAD+IV+0000000000004::9'
RFF+VA:DE000000B'
UNT+4+1'
Let’s move on and add line items.
public String toEdifact(final List<Invoice> invoices) throws Exception {
return D96A.writer(client)
.invoic(() -> invoices.stream(), (invoice, invoic) -> invoic
// ...
.sg25(() -> invoice.getLineItems().stream(), (lineItem, sg25) -> sg25.data(sg25_ -> sg25_ // (1)
.lin(lin -> lin.data(lin_ -> lin_
.e1082LineItemNumber(lineItem.getPosition())
.c212ItemNumberIdentification(c212 -> c212
.e7140ItemNumber(lineItem.getSku())
.e7143ItemNumberTypeCoded("EN")))) // EAN
.qty(qty -> qty.data(qty_ -> qty_
.c186QuantityDetails(c186 -> c186
.e6063QuantityQualifier("47") // Invoiced quantity
.e6060Quantity(lineItem.getInvoicedQty().intValue()))))
.sg28(sg28 -> sg28.data(sg28_ -> sg28_
.pri(pri -> pri.data(pri_ -> pri_
.c509PriceInformation(c509 -> c509
.e5125PriceQualifier("AAA") // Calculation net
.e5118Price(lineItem.getPiecePriceNetAmount())
.e6411MeasureUnitQualifier(lineItem.getPiecePriceNetUnit())
.e5375PriceTypeCoded("CT") // Contract
.e5387PriceTypeQualifier("NTP")))))))) // Net unit price
// ...
.unt())
.dumpToString();
}
-
The line items are declared in the
.sg25()
. The() → invoice.getLineItems().stream()
producer will create exact amount of Segment Group 25 as theinvoice.getLineItems()
list has. The mapping function(lineItem, sg25) → …
maps the POJO to the segment.
The example above produces the following:
UNH+1+INVOIC:D:96A'
LIN+1++9783898307529:EN'
QTY+47:5'
PRI+AAA:27.5:CT:NTP::PCE'
LIN+2++9783898307539:EN'
QTY+47:1'
PRI+AAA:10.87:CT:NTP::PCE'
LIN+3++97838983938472:EN'
QTY+47:5'
PRI+AAA:3.85:CT:NTP::PCE'
UNT+10+1'
The source code for this example is available on GitHub.
Supported subsets
-
Currently supported subsets and their message types
{ "d01b": [ "DELFOR", "DESADV", "IFTMIN", "INVOIC", "INVRPT", "ORDERS", "ORDRSP", "PRICAT", "SLSRPT" ], "d04a": [ "DELFOR", "DESADV", "IFTMIN", "INVOIC", "INVRPT", "ORDERS", "ORDRSP", "PRICAT", "SLSRPT" ], "d07a": [ "DELFOR", "DESADV", "IFTMIN", "INVOIC", "INVRPT", "ORDERS", "ORDRSP", "PRICAT", "SLSRPT" ], "d96a": [ "DELFOR", "DESADV", "IFTMIN", "INVOIC", "INVRPT", "ORDERS", "ORDRSP", "PRICAT", "SLSRPT" ], "d96b": [ "DELFOR", "DESADV", "IFTMIN", "INVOIC", "INVRPT", "ORDERS", "ORDRSP", "PRICAT", "SLSRPT" ], "d99a": [ "DELFOR", "DESADV", "IFTMIN", "INVOIC", "INVRPT", "ORDERS", "ORDRSP", "PRICAT", "SLSRPT" ] }
Note
|
Support for a subset or a message can be added by request. |
Integration scenarios
Checkout our repository on GitHub with various integration scenarios.