EDIFACT messages require a total segment count in the segment UNT. X12 messages require the same information in segment SE data element 96.
In SAP Process Integration/Orchestration(PI/PO) this requirement was handled by mapping the constant $B2B_SEG_COUNTER to the relevant field. SAP Cloud Integration doesn’t have this feature yet, but we can create a Groovy script that replicates exactly the same behavior.
You can put this script just before the “XML to EDI converter” step, or after “EDIFACT Post-processing” step. It is flexible and it can be used when the message body has the XML root element starting with M_, or the root element is Interchange.
You can map $B2B_SEG_COUNTER to any field. You can use the constant function in Integration Advisor.
Script
Note that groovy.util.Node returns String in Groovy IDE and a QName object in Cloud Integration. This behavior is flexible in the Groovy spec. “getLocalName” function handles this difference.
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
String magicKeyword = '$B2B_SEG_COUNTER'
def body = message.getBody(java.io.Reader)
def parser = new XmlParser()
def ediXmlDoc = parser.parse(body)
Integer segmentCount = 0
//find message and count segments inside
ediXmlDoc.breadthFirst().findAll { possibleMessageNode ->
// GroovyIDE returns String, Cloud Integration returns QName here.
// println possibleMessageNode.name().getClass()
// The implementation is flexible in the Class spec. .name() function returns java.lang.Object.
// It is possible that since this input has no namespace, it returns string.
// it is a message node
if ( getLocalName(possibleMessageNode).startsWith("M_") ){
possibleMessageNode.breadthFirst().findAll { possibleSegmentNode ->
// it is a segment node
if ( getLocalName(possibleSegmentNode).startsWith("S_") ){
// println possibleSegmentNode.name()
segmentCount ++
}
}
}
}
// find nodes to be filled
ediXmlDoc.breadthFirst().findAll { node ->
if (node.localText().size() > 0 && node.localText().first() == magicKeyword ){
node.value = segmentCount
}
}
// Write document to body
def sw = new StringWriter()
def xmlNodePrinter = new XmlNodePrinter(new PrintWriter(sw))
xmlNodePrinter.with {
preserveWhitespace = true
}
xmlNodePrinter.print(ediXmlDoc)
String result = sw.toString()
message.setBody(result)
return message;
}
def String getLocalName( groovy.util.Node node ){
def name = node.name() // can be java.lang.String or groovy.xml.QName or another object!
String localName = ""
if (name instanceof groovy.xml.QName) {
groovy.xml.QName qname = (groovy.xml.QName) name
localName = qname.getLocalPart()
}
else {
localName = name.toString()
}
return localName
}
GitHub Repository for Contributions
You can see this example & others at the GitHub repository. You can star⭐ the project or even watch👀 for all future updates!
Contributions are welcome! I can help with the description or the code if you want.
Related Blog Posts
This blog post by Vikas Kumar Singh shares the XSLT version of a similar solution. You should use it before the “Envelope Handling” step.