Skip to Content
Technical Articles
Author's profile photo Fatih Pense

Groovy EDI Segment Counter (also known as $B2B_SEG_COUNTER)

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.

 

Assigned Tags

      Be the first to leave a comment
      You must be Logged on to comment or reply to a post.