Introduction

We continue on our journey of looking at some special use cases on SAP Trading Partner Management on Integration Suite. The principle for this series and this post continues to be to leverage SAP Standard TPM Content and add workarounds where needed with options to fit back to Standard when some of the missing pieces are made available from SAP. In that spirit, we will look at how IDoc Packages are handled in Integration Suite when you get IDoc Bundles / Packages from ERP.

Problem Statement

SAP ERP triggers IDocs as a Package ( Collect IDocs with Package Size ). See below example of an IDoc XML. For example, the below XML shows how when ERP sends a IDoc and you have Collect IDocs enabled in the Partner Profile, you will have 1 message in Integration Suite with multiple IDoc Tags.

Sample IDoc with IDoc Package Size greater than 1

Unfortunately, if you process a IDoc with Packet Size greater than 1 in Integration Suite with the standard TPM Flows, the message is rejected as TPM currently only supports a Packet Size of 1. Push an IDoc from ERP with Packet Size >1 and you will see TPM will not be able to handle this ( or rather than Standard Flows of SAP Trading Partner Management Package).

When you trigger such an IDoc to the Standard TPM Endpoint for IDocs to the IFlow: Step 1 – Sender IDOC Communication Flow V2, you will notice the message fails in the Step 1b – Write Message to Message queue. The reason for the error is because the Standard SAP PD XSLT Mapping : EXTRACT_XSLT which is used to extract the IDoc Control Record details cannot handle the multiple EDI_DC40.This makes sense as well, as the B2B Monitoring typically uses the IDoc Number for Monitoring purposes and if you have more than 1 IDoc Tag in the Payload, your B2B Monitoring cannot support that in the current implementation! You can see in the screenshot below the Trace of the Step 1b – Write Message to Message queue where the Message fails in the execution step of EXTRACT_XSLT

Standard TPM Integration Flows Fail when IDocs have packet size greater than 1
Step 1b - Write Message to Message queue where the Message fails in the execution step of EXTRACT_XSLT

Check the XSL Mapping for EXTRACT_XSLT

You can always extract the XSL Mapping from the Partner Directory for TPM. I will not delve in those details here as those are documented in this post Cloud Integration – Partner Directory – Partner Dependent XML Structures and IDs. But once you Extract the XSLT mapping EXTRACT_XSLT as a part of the standard SAP TPM Code, you will realise, that there seems no way around this,i.e, for a Logical reason the way B2B Monitoring and TPM is architecture, Bundled IDocs are not supported by SAP. See below the XSL Mapping of EXTRACT_XSLT.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:fx="http://www.sap.com/xslt/functions"
    xmlns:cpi="http://sap.com/it/" exclude-result-prefixes="fx xsd xsl" version="2.0">
    <xsl:output method="xml" encoding="UTF-8" byte-order-mark="no" indent="yes"/>
    <xsl:preserve-space elements="*"/>
    <xsl:param name="exchange"/>
    <xsl:param name="SAP_TPA_SND_TimeZone"/>
    <!-- ============================================================================================= -->
    <!-- Match Template: Call templates for populate headers  
                         into camel exchange headers and copy payload -->
    <!-- ============================================================================================= -->
    <xsl:template match="/">
        <xsl:call-template name="populateHeaders"/>
        <xsl:call-template name="copy"/>
    </xsl:template>
    <!-- ============================================================================================= -->
    <!-- Match Template: Produce payload -->
    <!-- ============================================================================================= -->
    <xsl:template name="copy" match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <!-- ============================================================================================= -->
    <!-- Match Template: Write header values into camel exchange headers -->
    <!-- ============================================================================================= -->
    <xsl:template name="populateHeaders">
        <xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Client', //*:EDI_DC40/*:MANDT)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Interchange_Control_Number', //*:EDI_DC40/*:DOCNUM)"/>
        <xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Message_Number', //*:EDI_DC40/*:DOCNUM)"/>
        <xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Message_Version', fx:setVersion(//*:EDI_DC40/*:DOCREL))"/>
        <xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Status', //*:EDI_DC40/*:STATUS)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Direction', fx:setDirection(//*:EDI_DC40/*:DIRECT))"/>
        <xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Output_Mode', //*:EDI_DC40/*:OUTMOD)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Processing_Priority_Code', fx:setPriority(//*:EDI_DC40/*:EXPRSS))"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Usage_Indicator', fx:setUsage(//*:EDI_DC40/*:TEST))"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Customer_Extension', //*:EDI_DC40/*:CIMTYP)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Message_Type', fx:setMessageType(//*:EDI_DC40/*:MESTYP, //*:EDI_DC40/*:IDOCTYP, //*:EDI_DC40/*:CIMTYP))"/>
        <xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Message_Code', //*:EDI_DC40/*:MESCOD)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Message_Function', //*:EDI_DC40/*:MESFCT)"/>
        <xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Standard_Flag', //*:EDI_DC40/*:STD)"/>
        <!-- xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Standard_Version', //*:EDI_DC40/*:STDVRS)"/ -->
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Standard_Message_Type', //EDI_DC40/STDMES)"/>
        <!-- xsl:value-of select="fx:setExchangeHeader('SAP_EDI_GS_Sender_ID', //*:EDI_DC40/*:SNDPOR)"/ -->
        <!-- xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Sender_Partner_Type', fx:setPartnerType(//*:EDI_DC40/*:SNDPRT))"/ -->
        <!-- xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Sender_Partner_Function', //*:EDI_DC40/*:SNDPFC)"/ -->
        <xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Sender_ID', //*:EDI_DC40/*:SNDPRN)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Sender_Routing_Address', //*:EDI_DC40/*:SNDSAD)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Sender_Logical_Addess', //*:EDI_DC40/*:SNDLAD)"/>
        <!-- xsl:value-of select="fx:setExchangeHeader('SAP_EDI_GS_Receiver_ID', //*:EDI_DC40/*:RCVPOR)"/ -->
        <!-- xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Receiver_Partner_Type', fx:setPartnerType(//*:EDI_DC40/*:RCVPRT))"/ -->
        <!-- xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Receiver_Partner_Function', //*:EDI_DC40/*:RCVPFC)"/ -->
        <xsl:value-of select="fx:setExchangeHeader('SAP_EDI_Receiver_ID', //*:EDI_DC40/*:RCVPRN)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Receiver_Routing_Address', //*:EDI_DC40/*:RCVSAD)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Receiver_Logical_Address', //*:EDI_DC40/*:RCVLAD)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Interchange_DateTime', fx:setInterchangeCreationDateTime(//*:EDI_DC40/*:CREDAT, //*:EDI_DC40/*:CRETIM, $SAP_TPA_SND_TimeZone))"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Transmission_File', //*:EDI_DC40/*:REFINT)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Sender_Group_Reference_Number', //*:EDI_DC40/*:REFGRP)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Sender_Message_Reference_Number', //*:EDI_DC40/REFMES)"/>
        <xsl:value-of
            select="fx:setExchangeHeader('SAP_EDI_Archiving_Indicator', //*:EDI_DC40/*:ARCKEY)"/>
    </xsl:template>

    <!-- ============================================================================================= -->
    <!-- Function: Calling the setHeader function for creating a camel exchange header parameter -->
    <!-- ============================================================================================= -->

    <!-- xsl:function name="fx:setExchangeHeader">
        <xsl:param name="name"/>
        <xsl:param name="value"/>
        <xsl:if test="xsd:string($value) != ''">
            <parameter><xsl:value-of select="concat('&#xA;', $name, ' - ', xsd:string($value))"/></parameter>
        </xsl:if>
    </xsl:function -->

    <xsl:function name="fx:setExchangeHeader">
        <xsl:param name="name"/>
        <xsl:param name="value"/>
        <xsl:if test="xsd:string($value) != ''">
            <xsl:value-of select="cpi:setHeader($exchange, $name, xsd:string($value))"/>
        </xsl:if>
    </xsl:function>

    <!-- ============================================================================================= -->
    <!-- Function: Set the direction of the interchange -->
    <!-- ============================================================================================= -->
    <xsl:function name="fx:setDirection" as="xsd:string">
        <xsl:param name="arg"/>
        <xsl:sequence
            select="
                if ($arg = '1') then
                    'Outbound'
                else
                    if ($arg = '2') then
                        'Inbound'
                    else
                        ''"
        />
    </xsl:function>

    <!-- ============================================================================================= -->
    <!-- Function: Set the message version -->
    <!-- ============================================================================================= -->
    <xsl:function name="fx:setVersion" as="xsd:string">
        <xsl:param name="version"/>
        <xsl:sequence select="
            if (not(exists($version)) or $version = '') then
                '1809_FPS02'
            else
                $version"
        />
    </xsl:function>

    <!-- ============================================================================================= -->
    <!-- Function: Set the output mode of the interchange -->
    <!-- ============================================================================================= -->
    <xsl:function name="fx:setOutputMode" as="xsd:string">
        <xsl:param name="arg" as="xsd:string?"/>

        <xsl:sequence
            select="
                if ($arg = '4') then
                    'collect'
                else
                    if ($arg = '3') then
                        'collectAndSubsystem'
                    else
                        if ($arg = '1') then
                            'passSubsystem'
                        else
                            'pass'"
        />
    </xsl:function>

    <!-- ============================================================================================= -->
    <!-- Function: Set partner type -->
    <!-- ============================================================================================= -->
    <xsl:function name="fx:setPartnerType" as="xsd:string">
        <xsl:param name="arg"/>
        <xsl:sequence
            select="
                if ($arg = 'B') then
                    'Bank'
                else
                    if ($arg = 'EM') then
                        'Employer'
                    else
                        if ($arg = 'BP') then
                            'Business Partner'
                        else
                            if ($arg = 'KU') then
                                'Buyer'
                            else
                                if ($arg = 'BY') then
                                    'Buyer'
                                else
                                    if ($arg = 'LI') then
                                        'Supplier'
                                    else
                                        if ($arg = 'SE') then
                                            'Seller'
                                        else
                                            if ($arg = 'LS') then
                                                'Logical System'
                                            else
                                                if ($arg = 'US') then
                                                    'User'
                                                else
                                                    ''"
        />
    </xsl:function>

    <!-- ============================================================================================= -->
    <!-- Function: Set, if the usage is productive, test or for information -->
    <!-- ============================================================================================= -->
    <xsl:function name="fx:setUsage" as="xsd:string">
        <xsl:param name="arg"/>
        <xsl:sequence
            select="
                if ($arg = 'X') then
                    'test'
                else
                    'productive'"
        />
    </xsl:function>

    <!-- ============================================================================================= -->
    <!-- Function: Set the interchange processing priority -->
    <!-- ============================================================================================= -->
    <xsl:function name="fx:setPriority" as="xsd:string">
        <xsl:param name="arg" as="xsd:string?"/>

        <xsl:sequence
            select="
                if ($arg = 'X') then
                    'true'
                else
                    'false'"
        />
    </xsl:function>

    <!-- ============================================================================================= -->
    <!-- Function: Set interchange creation date and time -->
    <!-- ============================================================================================= -->
    <xsl:function name="fx:setInterchangeCreationDateTime" as="xsd:dateTime">
        <xsl:param name="date" as="xsd:string"/>
        <xsl:param name="time" as="xsd:string"/>
        <xsl:param name="timeZone" as="xsd:string?"/>
        <xsl:variable name="dateTime"
            select="
                xsd:dateTime(replace(concat($date, $time),
                '^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$',
                '$1-$2-$3T$4:$5:$6'))"/>
        <xsl:sequence
            select="
                if ($timeZone != '') then
                    adjust-dateTime-to-timezone($dateTime, xsd:dayTimeDuration($timeZone))
                else
                    $dateTime
                "
        />
    </xsl:function>

    <!-- ============================================================================================= -->
    <!-- Function: Set message type -->
    <!-- ============================================================================================= -->
    <xsl:function name="fx:setMessageType" as="xsd:string">
        <xsl:param name="type" as="xsd:string?"/>
        <xsl:param name="baseType" as="xsd:string?"/>
        <xsl:param name="extType" as="xsd:string?"/>
        <xsl:choose>
            <xsl:when test="$type != '' and $baseType != '' and $extType != ''">
                <xsl:sequence
                    select="
                    concat($type, '.', $baseType, '.', $extType)
                    "
                />
            </xsl:when>
            <xsl:when test="$type != '' and $baseType != ''">
                <xsl:sequence
                    select="
                        concat($type, '.', $baseType)
                        "
                />
            </xsl:when>
            <xsl:when test="$type != ''">
                <xsl:sequence select="
                        $type
                        "/>
            </xsl:when>
            <xsl:when test="$baseType != ''">
                <xsl:sequence select="
                        $baseType
                        "/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence
                    select="
                        error((), 'Message type and base type not available!')
                        "
                />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>

</xsl:stylesheet>

The Requirements and our options

It is logically clear why SAP TPM does not support IDoc Packaging. The B2B monitoring has been set up to work with 1 IDoc at a time and so has the Extract_XSLT Logic of SAP.

But, the reality of Projects, especially EDI B2B projects is that some data is batched from ERP. A classic case in Manufacturing Customers is your DELFOR and DELJIT IDocs. These are based on your MRP and typically are batched in SAP to allow multiple DELFOR and DELJIT IDocs to be triggered from SAP in a packet size due to the sheer volume of data. Of course, the easy solution is to change the Partner Profile in ERP from Collect IDocs to Trigger Immediately or to Packet Size 1 but what if,

  • You have agreed with your Trading Partner to send bundled EDI Messages.
  • You have “challenges” in changing ERP Configuration – Organizational politics are a reality!
  • You hear statements – but oh SAP PO could do this, so why does this shiny new software thats my future not support it!

From an Integration perspective you can have one of the following 3 requirements to handle Bundled IDocs

  1. Your Trading Partner does not care whether it is a single EDI Message or a Bundled EDI Message – You just want to split each IDoc into a separate message and process it over.
  2. You and Your Trading Partner have agreed that you will bundle your EDI Messages but without Functional Acknowledgements
  3. You and Your Trading Partner have agreed that you will bundle your EDI Messages but with Functional Acknowledgements

In this post, we will cover the Requirement 1, i.e., split each IDoc into a separate Message and Process it over as a EDI Message. We will cover the remaining requirements in subsequent posts.

Scenario In Scope

For this flow, we will continue to extend the scenario we have covered in Part 4, i.e., an IDoc to AS2 flow over EDI but with IDoc Packaging / Bundling.

The Solution – Use another IFlow for IDoc Bundled Sender

The principle is for us to not change any of the Standard SAP TPM IFlows. The principle is to build this extension as a plug and play so when SAP changes this and provides an out of the box support for IDoc Sender Packaging in TPM, we need not make any major changes on our end. With this as our incoming principles, we will now build a Custom IFlow that will be a copy of the Standard IFlow: Step 1 – Sender IDOC Communication Flow V2.

Go to Integration Suite ->Package : Cloud Integration – Trading Partner Management V2, and copy the IFlow: Step 1 – Sender IDOC Communication Flow V2 to another Custom Package with the name Step 1 – Sender IDOC Communication Flow V2 Bundled IDocs Process Individual ( This is just a sample name, you can use any name you want )

Go to Integration Suite ->Package : Cloud Integration - Trading Partner Management V2, and copy the IFlow: Step 1 - Sender IDOC Communication Flow V2 to another Custom Package with the name Step 1 - Sender IDOC Communication Flow V2 Bundled IDocs Process Individual ( This is just a sample name, you can use any name you want )

Make the below 2 changes to the IFlow

  • Add a General Splitter with XPATH Expression : //IDOC
  • Change the IDoc Adapter Connection to /tpm/b2b/idoc/bundled/processindividual ( or anything appropriate)
Create a New IFlow for Handling Bundled IDocs from ERP to be processed Individually.
Add a General Splitter with XPATH Expression : //IDOC
Change the IDoc Adapter Connection to /tpm/b2b/idoc/bundled/processindividual ( or anything appropriate)

Save and Deploy this IFlow!

Test Your Flow

Change your Destination in SAP ERP to this IDoc Endpoint /tpm/b2b/idoc/bundled/processindividual and trigger your Bundled / Packaged IDoc. You will now see that everything works as-is ,i.e., your IDoc is Split into multiple IDocs and the subsequent TPM Flows are called without any issues / challenges.

I triggered an IDoc with Packet Size 2 and you can see in the Monitoring that the IFlow we just built: Step 1 – Sender IDOC Communication Flow V2 Bundled IDocs Process Individual splits the IDoc and processes this over to the remaining standard TPM IFlows. Likewise the message is also processed over successfully in B2B Monitoring.

IDocs Split and processed through TPM.  Logs from Message Monitoring in SAP Cloud Integration
IDocs Split and processed through TPM.  Logs from B2B Monitoring in SAP Cloud Integration

Final Thoughts and Next Steps

TPM provides a plug and play architecture but with “some limitations” as we have now started to see in the past few posts of these series. A lot of these limitations are something that are not explicitly documented from SAP.

SAP’s TPM ‘s Architecture currently (as of Nov 2023) allows only for processing of Individual IDocs from ERP. This state is far from ideal and there exist in the real world a requirement where IDocs are bundled out from ERP. With the principle of not changing anything in the standard SAP Flows, we have introduced a new Step 1 flow to show how you can plug into SAPs TPM Architecture.

This though is just the beginning. As we have looked at in the requirements section of this post, there also exist realistic cases where we need to Bundle the Outgoing EDI Message to our Trading Partners.

Additional Blogs from this Series

Further Reading