XSLT can handle complex and
tricky requirements specially when copying patterns and manipulating hierarchies. I came across a tricky requirement in my project. It was an IDoc to File scenario where source IDoc xml was to be converted into a third party specific format.
Here we have an invoice Idoc (INVOIC.INVOIC02)
which needs to be converted into a specific format. This transformation is required for a correction
invoice Idoc, which means that Idoc would have even number of E1EDP01 segments in it. For n number of original E1EDP01 segments, there will be another n number of
correction E1EDP01 segments. Source structure of the idoc is as follows:
IDOC
EDI_DC40
E1EDKxx segments
E1EDP01 segments (Even number, n original, n correction)
E1EDSxx segments
Third party requires the
Idoc xml but in a bit different format as explained below, Let us introduce some notations:
*E1EDKxx_Original* - These are E1EDKxx
segments which are present in the source idoc.So lets call them the E1EDKxx_Original
segments.
*E1EDKxx_Changed * - This is modified version of
source E1EDKxx segments. One of the segment in E1EDKxx segment
would be changed.
*Trailer * - Copy of
E1EDSxx, this would be used as trailer record
Target structure required
by third party --
IDOC
EDI_DC40
E1EDKxx_Original (As is original E1EDKxx).
E1EDP01 (item number 1, original item)
E1EDSxx (Trailer)
E1EDKxx_Changed
E1EDP01 (item number 1,
correction item)
E1EDSxx (Trailer)
E1EDKxx_Original (As is original E1EDKxx)
E1EDP01 (item number 2, original item)
E1EDSxx (Trailer)
E1EDKxx_Changed
E1EDP01 (item number 2, correction item)
E1EDSxx (Trailer)
……………………………..
……………………………..
We have a relation here
between original and correction item (E1EDP01).
Original
E1EDP01-POSEX = Correction E1EDP01-HIPOS.
Given the above
information, we need to convert the source IDoc xml into target structure as
explained above. Achieving this transformation via Graphical, Java or ABAP mapping
would be quite difficult. So we will be discussing XSLT transformation here.
We would approach the target xml as mentioned below:
Loop on all E1EDP01
If ( Current E1EDP01-POSEX = Any of other E1EDP01-HIPOS)
write E1EDKxx_Changed;
write current E1EDP01;(original item)
write trailer;
write E1EDKxx_Original;
write correction E1EDP01; (Correction Item)
write trailer;
End If.
Endloop
First we have to copy the
mulitple E1EDKxx segments in a variable. We will do that using an XSLT function
- *starts-with(name(), 'E1EDK') *in
a loop.
<!--Copying all the E1EDKxx segments from the idoc, these will work as header for original E1EDP01 ><br /> <xsl:variable name="e1edkxx_original"><br /> <xsl:for-each select="//IDOC/*"><br /> <!Copying the E1EDKxx segments as they are > <! Copying all the E1EDKxx segments from the idoc with a change E1EDK14-ORGID = 'G2O', these will work as header for corrected E1EDP01 ><br /> <xsl:variable name="e1edkxx_changed"><br /> <xsl:for-each select="//IDOC/*"><br /> <!Copying other E1EDKxx segments as they are ><br /> <xsl:if test="not (starts-with(name(), 'E1EDK14') and QUALF = '015') and (starts-with(name(), 'E1EDK'))"><br /> <xsl:copy-of select="."/><br /> </xsl:if><br /> <!Changing E1EDK14-ORGID where qualifier = '015' > <!Copying the E1EDPxx segments as they are ><br /> <xsl:variable name="all_e1edp01"><br /> <xsl:for-each select="//IDOC/*"><br /> <!Copying the E1EDKxx segments as they are ><br /> <xsl:if test="starts-with(name(), 'E1EDP') "><br /> <xsl:copy-of select="."/><br /> </xsl:if><br /> </xsl:for-each><br /> </xsl:variable></p></div></td></tr></tbody></table><p> </p><p>E1EDSxx segments would be used as trailer. A variable trailer will hold all the E1EDSxx segments.</p><table border="1" width="710" height="100"><tbody><tr style="height: 100px; background-color: #ffffff;" valign="middle"><td style="width: 600px;"><div style="overflow: auto; width: 700px; height: 90px;"> <xsl:variable name="trailer"><br /> <!Trailer Record E1EDSxx><br /> <xsl:for-each select="//IDOC/*"><br /> <!Copying the E1EDSxx segments as they are ><br /> <xsl:if test="starts-with(name(), 'E1EDS') "><br /> <xsl:copy-of select="."/><br /> </xsl:if><br /> </xsl:for-each><br /> <! Finishing the trailer part here-->
We now have E1EDKxx_original, E1EDKxx_changed, all_e1edp01 and trailer records ready. Other requirement remains for relating the original E1EDP01 segments and correction E1EDP01 segments. We have been given following condition for relating original E1EDP01 segments and correction E1EDP01 segments.
HIPOS of correction E1EDP01 segments = POSEX of original E1EDP01 segments
This can be done using two for loops. But we will be using an XSLT *key()* function. The key() function returns a node-set from the document, using
the index specified by an <!-- Forming the required structure here><br /> <IDOC><br /> <xsl:copy-of select="$controlRecord"/> <! Control Record ><br /> <xsl:for-each select="$all_e1edp01/*"> <! Manupulating the E1EDPxx segments here ><br /> <! Checking if the value of Posex in the current E1EDP01 segment is equal to the HIPOS of any of E1EDP01 segment ><br /> <! Assumption is that HIPOS is populated only in case of corrected E1EDP01 items and is equal to POSEX of the original E1EDP01 ><br /> <xsl:if test="POSEX = key('E1EDPXX_HIPOS', POSEX)/HIPOS "><br /> <xsl:copy-of select="$e1edkxx_changed"/> <! Copying E1EDKxx Changed ><br /> <xsl:copy-of select="."/> <! Copying E1EDP01 segment (original)> <br /> <xsl:copy-of select="$trailer"/> <!Copying E1EDSxx Segment><br /> <xsl:copy-of select="$e1edkxx_original"/> <! Copying E1EDKxx Original><br /> <xsl:copy-of select="key('E1EDPXX_HIPOS', POSEX)"/> <! Copying E1EDP01 segment for correction>*****************************************************************************************************************************************************************************************<br />Structure as required by Third Party<br /><br /> IDOC<br /> EDI_DC40<br /> E1EDKxx_changed<br /> E1EDP01 (item number 1, original item) <br /> E1EDSxx ( trailer)<br /> E1EDKxx_original <br /> E1EDP01 (item number 1, corrected item) <br /> E1EDSxx ( trailer)<br /> E1EDKxx_changed<br /> E1EDP01 (item number 2, original item)<br /> E1EDSxx ( trailer) <br /> E1EDKxx_original<br /> E1EDP01 (item number 2, corrected item) PSTYV=ZL2N<br /> E1EDSxx ( trailer)<br /> ................................................<br /> ................................................<br /><br /> Original E1EDP01 segments would be related to corrected E1EDP01 segments by E1EDP01-POSEX and E1EDP01-HIPOS.<br /> POSEX of original E1EDP01 = HIPOS of corrected E1EDP01. HIPOS is populated by custom coding in ECC only for correction line items.<br /> This XSLT does the required transformation.<br />> <!--Defining key for HIPOS, HIPOS of corrected E1EDP01 = POSEX of original E1EDP01 ><br /> <xsl:key name="E1EDPXX_HIPOS" match="E1EDP01" use="HIPOS"/><br /> <xsl:template match="/"><br /> <!Copying the EDI_DC40 segment ><br /> <xsl:variable name="controlRecord" select="//IDOC/EDI_DC40"/><br /> <!Copying all the E1EDKxx segments from the idoc, these will work as header for original E1EDP01 -->
<!--Copying the E1EDKxx segments as they are ><br /> <xsl:if test="starts-with(name(), 'E1EDK')"><br /> <xsl:copy-of select="."/><br /> </xsl:if><br /> </xsl:for-each><br /> </xsl:variable><br /> <! Copying all the E1EDKxx segments from the idoc with a change E1EDK14-ORGID = 'G2O', these will work as header for corrected E1EDP01 ><br /> <xsl:variable name="e1edkxx_changed"><br /> <xsl:for-each select="//IDOC/*"><br /> <!Copying other E1EDKxx segments as they are ><br /> <xsl:if test="not (starts-with(name(), 'E1EDK14') and QUALF = '015') and (starts-with(name(), 'E1EDK'))"><br /> <xsl:copy-of select="."/><br /> </xsl:if><br /> <!Changing E1EDK14-ORGID where qualifier = '015' ><br /> <xsl:if test="starts-with(name(), 'E1EDK14') and QUALF = '015'"><br /> <E1EDK14><br /> <xsl:attribute name="SEGMENT"><xsl:value-of select="1"/></xsl:attribute><br /> <QUALF>015</QUALF><br /> <ORGID><br /> <xsl:value-of select="'G2O'"/><br /> </ORGID><br /> </E1EDK14><br /> </xsl:if><br /> </xsl:for-each><br /> </xsl:variable><br /> <xsl:variable name="trailer"><br /> <!Trailer Record E1EDSxx><br /> <xsl:for-each select="//IDOC/*"><br /> <!Copying the E1EDSxx segments as they are ><br /> <xsl:if test="starts-with(name(), 'E1EDS') "><br /> <xsl:copy-of select="."/><br /> </xsl:if><br /> </xsl:for-each><br /> <! Finishing the trailer part here><br /> </xsl:variable><br /> <!Copying the E1EDPxx segments as they are ><br /> <xsl:variable name="all_e1edp01"><br /> <xsl:for-each select="//IDOC/*"><br /> <!Copying the E1EDKxx segments as they are ><br /> <xsl:if test="starts-with(name(), 'E1EDP') "><br /> <xsl:copy-of select="."/><br /> </xsl:if><br /> </xsl:for-each><br /> </xsl:variable><br /> <! Forming the required structure here><br /> <IDOC><br /> <xsl:copy-of select="$controlRecord"/> <! Control Record ><br /> <xsl:for-each select="$all_e1edp01/*"> <! Manupulating the E1EDPxx segments here ><br /> <! Checking if the value of Posex in the currentE1EDP01 segment is equal to the HIPOS of any of E1EDP01 segment ><br /> <! Assumption is that HIPOS is populated only in case of corrected E1EDP01 items and is equal to POSEX of the original E1EDP01 ><br /> <xsl:if test="POSEX = key('E1EDPXX_HIPOS', POSEX)/HIPOS "><br /> <xsl:copy-of select="$e1edkxx_changed"/> <! Copying E1EDKxx Changed ><br /> <xsl:copy-of select="."/> <! Copying E1EDP01 segment (original)> <br /> <xsl:copy-of select="$trailer"/> <!Copying E1EDSxx Segment><br /> <xsl:copy-of select="$e1edkxx_original"/> <! Copying E1EDKxx Original><br /> <xsl:copy-of select="key('E1EDPXX_HIPOS', POSEX)"/> <! Copying E1EDP01 segment for correction-->