Introduction

In Part 12 of our series, we looked at Migrating SAP PI/ PO EDI Mappings without TPM. That was a side track that we took to set the stage for this post where we look at Migrating SAP PI / PO Mappings and then using them within TPM.

If you have reached travelled this journey so far with us, you would have noticed that we have started simple and added layers to our EDI / B2B journey. A typical SAP Integration Suite customer ( well atleast in most cases) would have a SAP PO landscape on-premise and if you are running B2B on SAP PO, you will definitely need to migrate them to Integration Suite.

As we had seen in Part 12 , and remaining posts of this series, we have consciously avoided using MAGs for the pure reason that if you are a SAP Process Orchestration customer wanting to migrate to SAP Integration Suite, you want to leverage the mappings you have built on SAP PO. To quote from my previous post “Realistically, PO to CPI migrations is not a “Business Driven Initiative” at most Customers, and “budget is always limited” and hence the pragmatic approach at most Customers has been to leverage the B2B Mappings built on SAP PO on Cloud Integration. Redoing the mappings on MAGs from a time, effort and testing standpoint is something customers we have worked with have wanted to avoid. Why break something that works has been the common phrase.”

We had in Part 12 , set the context for importing the mappings from SAP PO into Integration Suite but without TPM. We will now see what adjustments we need to make if we want to leverage the mappings from SAP PO and also use TPM.

SAP PO XSD vs MAG XSD

As you would have seen in Part 12 of our series, the SAP PO EDI Message that is imported has the root tag as : DESADVD96A or similar and then contains the S_UNA, S_UNB/S_UNZ Segment and then the actual Message M_DESADV in our case. In the case of a MAG based XSD ( as per the MAG we have created), you will notice that it only contains M_DESADV. See below image that compares the SAP PO Based EDI Message and a MIG Based EDI Message

Comparison of SAP PO based EDI Message and MIG Based EDI Message on Cloud Integration

As the standard TPM Content from SAP has been built with MIGs and MAGs in mind, the Step 2 – Interchange Processing Flow V2 Iflow only works with MAG based XSDs. ( well as of Nov 2023 coz you never know when SAP introduces this limitation as a feature and makes this post redundant) . Hence, in our Custom Mapping Exit if we need to use SAP PO based message mappings, we will need to introduce a content filter that will extract the M_DESADV from your SAP POs mapping Exit.

ASSEMBLE_XSLT from Step 2 – Interchange Processing Flow V2

If you notice the Step 2- Interchange Processing Flow V2 Iflow as a part of your standard TPM package, you will notice that after your Mapping is executed, the XSLT ASSEMBLY_XSLT is triggered. This ASSEMBLY_XSLT in turn add the Interchange root tag to your XML adds the S_UNB and S_UNZ Segment. In other words what you had to handle in your SAP PO Message Mapping, the TPM handles this using the ASSEMBLE_XSLT mapping in the step Assemble Interchange.

Step 2- Interchange Processing Flow V2 . Control Numbers are set in the Message Header as per your TPM Configuration. Assembly_XSLT is called and adds the Interchange tag with  S_UNB and S_UNZ Segment

ASSEMBLE_XSLT from your Standard SAP TPM

<?xml version="1.0" encoding="UTF-8"?>
<!-- == HISTORY =================================================================
2023.06.07 [GS] - Line 141: Change the judgement condition of generating element D_0035.
============================================================================= -->
<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"
    exclude-result-prefixes="fx xsd xsl" version="2.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:preserve-space elements="*"/>

    <xsl:param name="SAP_EDI_REC_Syntax_ID" select="'UNOC'"/>
    <!-- UNB/[01]S001/[01]0001 -->
    <xsl:param name="SAP_EDI_REC_Syntax_Version_ID" select="'3'"/>
    <!-- UNB/[01]S001/[02]0002 -->
    <xsl:param name="SAP_EDI_REC_Sender_ID"/>
    <!-- UNB/[02]S002/[01]0004 -->
    <xsl:param name="SAP_EDI_REC_Sender_ID_Qualifier"/>
    <!-- UNB/[02]S002/[02]0007 -->
    <xsl:param name="SAP_EDI_REC_Sender_Routing_Address"/>
    <!-- UNB/[02]S002/[03]0008 -->
    <xsl:param name="SAP_EDI_REC_Receiver_ID"/>
    <!-- UNB/[03]S003/[01]0010 -->
    <xsl:param name="SAP_EDI_REC_Receiver_ID_Qualifier"/>
    <!-- UNB/[03]S003/[02]0007 -->
    <xsl:param name="SAP_EDI_REC_Receiver_Routing_Address"/>
    <!-- UNB/[03]S003/[03]0014 -->
    <xsl:param name="SAP_EDI_REC_Interchange_Control_Number"/>
    <!-- UNB/[05]0020 -->
    <xsl:param name="SAP_ISA_REC_Security_Information"/>
    <!-- UNB/[06]S005/[01]0022 -->
    <xsl:param name="SAP_EDI_REC_Security_Information_Qualifier"/>
    <!-- UNB/[06]S005/[02]0025 -->
    <xsl:param name="SAP_EDI_REC_Sender_System_ID"/>
    <!-- UNB/[07]0026; UNG/[03]S007/[01]0044 -->
    <xsl:param name="SAP_EDI_REC_Processing_Priority_Code"/>
    <!-- UNB/[08]0029 -->
    <xsl:param name="SAP_EDI_REC_Acknowledgement_Request"/>
    <!-- UNB/[09]0031 -->
    <xsl:param name="SAP_EDI_REC_Interchange_Agreement_ID"/>
    <!-- UNB/[10]0032 -->
    <xsl:param name="SAP_EDI_REC_Usage_Indicator"/>
    <!-- UNB/[11]0035 -->
    <xsl:param name="SAP_EDI_REC_Message_Association_Assign_Code"/>
    <!-- UNG/[07]S008/[03]0057; UNH/[02]S009/[05]0057 -->
    <xsl:param name="SAP_EDI_REC_Message_Number"/>
    <!-- UNH/[01]0062; UNT/[02]0062 -->
    <xsl:param name="SAP_EDI_REC_Message_Type"/>
    <!-- UNH/[02]S009/[01]0065 -->
    <xsl:param name="SAP_EDI_REC_Message_Version"/>
    <!-- UNH/[02]S009/[02]0052 -->
    <xsl:param name="SAP_EDI_REC_Message_Release"/>
    <!-- UNH/[02]S009/[03]0054 -->
    <xsl:param name="SAP_EDI_REC_Message_Controlling_Agency"/>
    <!-- UNH/[02]S009/[04]0051 -->
    <xsl:param name="SAP_EDI_REC_Common_Access_Reference"/>
    <!-- UNH/[03]0068 -->
    <xsl:param name="SAP_EDI_REC_Set_Timezone" select="'PT2H'"/>
    <!-- Set actual timezone-->

    <xsl:template match="/node()[starts-with(local-name(), 'M_')]">
        <ns1:Interchange xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:ns1="urn:sap.com:ica:typesystem:6:un-edifact">
            <S_UNB>
                <C_S001>
                    <D_0001>
                        <xsl:value-of select="$SAP_EDI_REC_Syntax_ID"/>
                    </D_0001>
                    <D_0002>
                        <xsl:value-of select="$SAP_EDI_REC_Syntax_Version_ID"/>
                    </D_0002>
                </C_S001>
                <C_S002>
                    <D_0004>
                        <xsl:value-of select="$SAP_EDI_REC_Sender_ID"/>
                    </D_0004>
                    <D_0007>
                        <xsl:value-of select="$SAP_EDI_REC_Sender_ID_Qualifier"/>
                    </D_0007>
                    <D_0008>
                        <xsl:value-of select="$SAP_EDI_REC_Sender_Routing_Address"/>
                    </D_0008>
                </C_S002>
                <C_S003>
                    <D_0010>
                        <xsl:value-of select="$SAP_EDI_REC_Receiver_ID"/>
                    </D_0010>
                    <D_0007>
                        <xsl:value-of select="$SAP_EDI_REC_Receiver_ID_Qualifier"/>
                    </D_0007>
                    <D_0014>
                        <xsl:value-of select="$SAP_EDI_REC_Receiver_Routing_Address"/>
                    </D_0014>
                </C_S003>
                <C_S004>
                    <D_0017>
                        <xsl:value-of
                            select="fx:setActualDate($SAP_EDI_REC_Set_Timezone, '[Y01][M01][D01]')"
                        />
                    </D_0017>
                    <D_0019>
                        <xsl:value-of
                            select="fx:setActualTime($SAP_EDI_REC_Set_Timezone, '[H01][m01]')"/>
                    </D_0019>
                </C_S004>
                <D_0020>
                    <xsl:value-of select="$SAP_EDI_REC_Interchange_Control_Number"/>
                </D_0020>
                <xsl:if test="$SAP_ISA_REC_Security_Information != ''">
                    <C_S005>
                        <D_0022>
                            <xsl:value-of select="$SAP_ISA_REC_Security_Information"/>
                        </D_0022>
                        <xsl:if test="$SAP_EDI_REC_Security_Information_Qualifier != ''">
                            <D_0025>
                                <xsl:value-of select="$SAP_EDI_REC_Security_Information_Qualifier"/>
                            </D_0025>
                        </xsl:if>
                    </C_S005>
                </xsl:if>
                <xsl:if test="$SAP_EDI_REC_Sender_System_ID != ''">
                    <D_0026>
                        <xsl:value-of select="$SAP_EDI_REC_Sender_System_ID"/>
                    </D_0026>
                </xsl:if>
                <xsl:if test="$SAP_EDI_REC_Processing_Priority_Code != ''">
                    <D_0029>
                        <xsl:value-of select="fx:setPriority($SAP_EDI_REC_Processing_Priority_Code)"
                        />
                    </D_0029>
                </xsl:if>
                <xsl:if test="$SAP_EDI_REC_Acknowledgement_Request != ''">
                    <D_0031>
                        <xsl:value-of select="fx:setAck($SAP_EDI_REC_Acknowledgement_Request)"/>
                    </D_0031>
                </xsl:if>
                <xsl:if test="$SAP_EDI_REC_Interchange_Agreement_ID != ''">
                    <D_0032>
                        <xsl:value-of select="$SAP_EDI_REC_Interchange_Agreement_ID"/>
                    </D_0032>
                </xsl:if>
                <xsl:if test="$SAP_EDI_REC_Usage_Indicator = ('test', 'providerTest')">
                    <D_0035>
                        <xsl:value-of select="fx:setUsage($SAP_EDI_REC_Usage_Indicator)"/>
                    </D_0035>
                </xsl:if>
            </S_UNB>
            <xsl:copy>
                <xsl:apply-templates select="node()"/>
            </xsl:copy>
            <S_UNZ>
                <D_0036>
                    <xsl:value-of select="1"/>
                </D_0036>
                <D_0020>
                    <xsl:value-of select="$SAP_EDI_REC_Interchange_Control_Number"/>
                </D_0020>
            </S_UNZ>
        </ns1:Interchange>
    </xsl:template>
    <!-- ============================================================================================= -->
    <!-- Template: Update the UNH segment -->
    <!-- ============================================================================================= -->
    <xsl:template match="S_UNH">
        <xsl:copy>
            <xsl:call-template name="ctCreateElement">
                <xsl:with-param name="pName" select="'D_0062'"/>
                <xsl:with-param name="pValue" select="$SAP_EDI_REC_Message_Number"/>
            </xsl:call-template>
            <xsl:apply-templates select="C_S009"/>
            <xsl:call-template name="ctCreateElement">
                <xsl:with-param name="pName" select="'D_0068'"/>
                <xsl:with-param name="pValue" select="$SAP_EDI_REC_Common_Access_Reference"/>
            </xsl:call-template>
        </xsl:copy>
    </xsl:template>
    <!-- ============================================================================================= -->
    <!-- Template: Update the UNH/S009 composite -->
    <!-- ============================================================================================= -->
    <xsl:template match="C_S009">
        <xsl:copy>
            <xsl:call-template name="ctCreateElement">
                <xsl:with-param name="pName" select="'D_0065'"/>
                <xsl:with-param name="pValue" select="$SAP_EDI_REC_Message_Type"/>
            </xsl:call-template>
            <xsl:call-template name="ctCreateElement">
                <xsl:with-param name="pName" select="'D_0052'"/>
                <xsl:with-param name="pValue" select="$SAP_EDI_REC_Message_Version"/>
            </xsl:call-template>
            <xsl:call-template name="ctCreateElement">
                <xsl:with-param name="pName" select="'D_0054'"/>
                <xsl:with-param name="pValue" select="$SAP_EDI_REC_Message_Release"/>
            </xsl:call-template>
            <xsl:call-template name="ctCreateElement">
                <xsl:with-param name="pName" select="'D_0051'"/>
                <xsl:with-param name="pValue" select="$SAP_EDI_REC_Message_Controlling_Agency"/>
            </xsl:call-template>
            <xsl:call-template name="ctCreateElement">
                <xsl:with-param name="pName" select="'D_0057'"/>
                <xsl:with-param name="pValue" select="$SAP_EDI_REC_Message_Association_Assign_Code"
                />
            </xsl:call-template>
        </xsl:copy>
    </xsl:template>
    <!-- ============================================================================================= -->
    <!-- Template: Update the UNT segment -->
    <!-- ============================================================================================= -->
    <xsl:template match="S_UNT">
        <xsl:copy>
            <xsl:apply-templates select="D_0074"/>
            <xsl:call-template name="ctCreateElement">
                <xsl:with-param name="pName" select="'D_0062'"/>
                <xsl:with-param name="pValue" select="$SAP_EDI_REC_Message_Number"/>
            </xsl:call-template>
        </xsl:copy>
    </xsl:template>
    <!-- ============================================================================================= -->
    <!-- Template: Update the UNT/D0074 data element -->
    <!-- ============================================================================================= -->
    <xsl:template match="D_0074">
        <xsl:copy>
            <xsl:value-of select="count(//*[starts-with(name(), 'S_')])"/>
        </xsl:copy>
    </xsl:template>
    <!-- ============================================================================================= -->
    <!-- Template: Consume and produce remaining nodes -->
    <!-- ============================================================================================= -->
    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>
    </xsl:template>
    <!-- ============================================================================================= -->
    <!-- Call Template: Update Element with parameter, if not exists                                   -->
    <!-- ============================================================================================= -->
    <xsl:template name="ctCreateElement">
        <xsl:param name="pName"/>
        <xsl:param name="pValue"/>
        <xsl:choose>
            <xsl:when test="./node()[local-name() = $pName] != ''">
                <xsl:element name="{$pName}">
                    <xsl:value-of select="./node()[local-name() = $pName]"/>
                </xsl:element>
            </xsl:when>
            <xsl:when test="$pValue != ''">
                <xsl:element name="{$pName}">
                    <xsl:value-of select="$pValue"/>
                </xsl:element>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
    <!-- ============================================================================================= -->
    <!-- Function: Create actual date based on UN/EDIFACT date format                                     -->
    <!-- ============================================================================================= -->
    <xsl:function name="fx:setActualDate" as="xsd:string">
        <xsl:param name="arg" as="xsd:string"/>
        <xsl:param name="format" as="xsd:string"/>

        <xsl:sequence
            select="format-date(adjust-date-to-timezone(current-date(), xsd:dayTimeDuration($arg)), $format)"
        />
    </xsl:function>
    <!-- ============================================================================================= -->
    <!-- Function: Create actual time based on UN/EDIFACT date format                                     -->
    <!-- ============================================================================================= -->
    <xsl:function name="fx:setActualTime" as="xsd:string">
        <xsl:param name="arg" as="xsd:string"/>
        <xsl:param name="format" as="xsd:string"/>

        <xsl:sequence
            select="format-time(adjust-time-to-timezone(current-time(), xsd:dayTimeDuration($arg)), $format)"
        />
    </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 != 'veryHigh') then
                    'A'
                else
                    ''"
        />
    </xsl:function>
    <!-- ============================================================================================= -->
    <!-- Function: Set, if functional acknowledgement is requested -->
    <!-- ============================================================================================= -->
    <xsl:function name="fx:setAck" as="xsd:string">
        <xsl:param name="arg" as="xsd:string?"/>

        <xsl:sequence
            select="
                if ($arg = 'true') then
                    '1'
                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" as="xsd:string?"/>

        <xsl:sequence
            select="
                if ($arg = 'test') then
                    '1'
                else
                    if ($arg = 'providerTest') then
                        '5'
                    else
                        ''"
        />
    </xsl:function>
</xsl:stylesheet>

Message After Assemble Interchange

Interchange is added to the Message along with S_UNB and S_UNZ

Interchange is added to the Message along with S_UNB and S_UNZ

Once this part becomes clear, the rest of steps becomes very simple and straightforward. TPM maps the control record of your EDI Message automatically including various Partner IDs and so on and hence, you can use your SAP PO Message Mappings as is with just adding a Additional Content Filter in your Integration flow to extract the “M_DESADV” or equivalent

Lets continue our journey to do this.

Pre-requisites

You have set up the connection between your SAP Integration Suite Cloud Integration and SAP PO. We will use the same scenario from Part 1.

Import SAP PO Mappings into Cloud Integration

These are the same steps we did in Part 12 but to make it easy, we repost them in this post as well.

We will import a Message Mapping from SAP PO to Cloud Integration. Before you can do that,

  • If you use Function Libraries that use Imported Archives in your Message Mapping, remove the use of those Function Library by copying the mapping into a Temp mapping and removing those fields and then import it into Integration Suite.
  • If you use Function Libraries without Imported Archives, ensure you follow the steps in this post to import the Function Library: SAP Integration Suite – Import PI/PO Function Library into Cloud Integration

This post starts with the assumption you have handled Function Libraries import and are aware of the limitation that Function Libraries with Imported Archives are currently not supported on Cloud Integration and is a road map item (As of Nov 2023).

Import and Deploy your EDI Message Mapping from SAP PO

You can also import the Message Mapping from your ESR directly in Integration flow but for now as we will use the Message Mapping in multiple IFlows, we have chosen the approach of importing your Message Mapping in the package.

Go to your Package ->Click on Add -> Message Mapping -> Select ES Repository ->Connect -> Select your Message Mapping and Click on Submit.

Add Message Mapping in Cloud Integration from ES Repository
Import Message Mapping from ES Repository
Select your Message Mapping and click on Submit
Your Message Mapping is imported into Cloud Integration

Note: Do not forget to Deploy your Message Mapping that you just imported!

Extend for Mapping Exit / Process Direct to use SAP PO Mapping

As we have seen in Part 1 of this series, we have used a Custom Mapping flow. In this case, we will change this Mapping Flow to use the SAP PO Based Message Mapping. If you have followed this entire series, and also used Custom Activity Parameters from Part 11 your IFlow might look like this.

Create New Integration Flow and use the SAP PO Message Mapping

To keep it simple, we will create a new Mapping IFlow and call it B2BMigrationDemo_EDIConversionWithTPM. This will have a Sender ProcessDirect Adapter with a new end point : B2BMigrationDemo_EDIConversionWithTPM

Create a New Integration Flow for Custom Mapping in TPM

Add the Message Mapping that we imported from SAP PO in the previous section

Add References Message Mapping

Select the Referenced Message Mapping from SAP PO.

Select the referenced Message Mapping from SAP PO

Add this as a Message Mapping step to your IFlow

Add the SAP PO Message Mapping Step.

Add Content Filter

Add a filter to your Iflow to remove the additional SAP PO Nodes so that your XML EDI looks exactly how your MAG based XML EDI Messages will look like. Save and deploy your Custom Integration Flow

Add a filter to your Iflow to remove the additional SAP PO Nodes.

Update your Agreement to use this new Custom Flow

As we have built a new Custom Flow, update your TPM Agreement to use this Custom Flow and then Update ( Activate) your Agreement.

Test Your Flow

Thats it, you are now all ready to test your Flow. You can now use SAP PO Mappings into your TPM. Trigger your IDoc and you will now notice that the mapping of SAP PO gets executed and then once the Filter gets executed, the message gets processed successfully.

Final Thoughts

Using SAP PO Message Mappings for EDI Trading Partner Management in Cloud Integration is very straight forward. All you need to do is import your message mappings and add a content filter to filter the additional nodes that are added in SAP PO that TPM Does not support.

Of course, it is essential to call out that Cloud Integration ONLY Supports Message Mappings with Function Libraries where no Imported Archives are used. ( SAP Integration Suite – Import PI/PO Function Library into Cloud Integration) . This is on SAPs roadmap for Cloud Integration but this is a limitation that you need to be aware of.

In our case we have chosen to disable these fields that use Function Libraries with Imported Archives , import the Message Mapping into Cloud Integration and then retrofit the code as we deem fit. Alternately there are tools like Figaf Migration Tools that are also worth considering if you do not want to manually retrofit the code in your Message Mappings.

As long as you have your Message Mapping from SAP PO imported into Integration Suite, you can then leverage them in TPM as we have seen in this post.

Additional Blogs from this Series

References