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.
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
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('
', $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
- 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.
- You and Your Trading Partner have agreed that you will bundle your EDI Messages but without Functional Acknowledgements
- 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 )
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)
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.
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
- Trading Partner Management – Part 1 – IDoc to EDI Flow(s)
- Trading Partner Management – Part 2 – EDI to IDoc Flows(s)
- Trading Partner Management – Part 3 – EDI over AS2 to IDoc Flows(s)
- Trading Partner Management – Part 4 – IDoc to EDI over AS2 Flow(s)
- Trading Partner Management – Part 5 – Custom IDoc Flow
- Trading Partner Management – Part 6 – Custom Search Attributes
- Trading Partner Management – Part 7 – EDI Functional Acknowledgements for Inbound EDI Messages
- Trading Partner Management – Part 8 – EDI Functional Acknowledgements for Outbound EDI Messages
- Trading Partner Management – Part 9 – Outgoing IDoc Bundling
- Trading Partner Management – Part 10 – Outgoing IDoc Bundling With EDI Bundling
- Trading Partner Management – Part 11 – Handling Parameters
- B2B on SAP Integration Suite – Part 12 – Migrating SAP PI / PO B2B Mappings without TPM
- Trading Partner Management – Part 13 – Migrating SAP PI / PO B2B Mappings with TPM
- Trading Partner Management – Part 14 – Handling Bundled Incoming EDIs
- Trading Partner Management – Part 15 – Handling Message Retries
- Trading Partner Management – Part 16 – B2B Failed Message Alerting
- Trading Partner Management – Part 17 – TPM Naming Convention Guideline
- B2BB2BMonitoringCPIEDIIntegration