Additional Blogs by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
claus_wallacher
Active Participant

One common mapping problem is the need to replicate nodes, for example when a deep structure must be converted into a flat structure. SAP provides the node function useOneAsMany to solve this problem (see for example this help) . Even though this is a very useful function, it still has some limitations. This weblog shows for different scenarios how to overcome these limitation using solely the features provided by the graphical mapping tool (no user exits).

The Basic Problem

We have an IDoc ORDERS05 with purchase order information where each purchase order line item (represented by segment .<E1EDP01>) can contain multiple schedule lines (represented by segment <E1EDP20>). For example, we have 2 line items with 3, resp. 2 schedule lines. Then the corresponding portion of message ORDERS05 can look as follows:

   ...

   <E1EDP01>

      <POSEX>00010</POSEX>

      <E1EDP20 SEGMENT="1">

         <WMENG>15</WMENG>

      </E1EDP20>

      <E1EDP20 SEGMENT="1">

         <WMENG>44</WMENG>

      </E1EDP20>

      <E1EDP20 SEGMENT="1">

         <WMENG>3</WMENG>

      </E1EDP20>

   </E1EDP01>

   <E1EDP01>

      <POSEX>00020</POSEX>

      <E1EDP20 SEGMENT="1">

         <WMENG>32</WMENG>

      </E1EDP20>

      <E1EDP20 SEGMENT="1">

         <WMENG>12</WMENG>

      </E1EDP20>

   </E1EDP01>

   ...

We want to map the information of the IDoc ORDERS05 to the message OrderCreate of the CIDX v4.0 standard. This message uses a more flat

structure where the purchase order line items and the corresponding schedule lines are on the same level. The corresponding portion of the

message OrderCreate (for the same information as in the IDoc) looks as follows:

   ...

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00010</PurchaseOrderLineItemNumber>

      <ProductQuantity>15</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00010</PurchaseOrderLineItemNumber>

      <ProductQuantity>44</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00010</PurchaseOrderLineItemNumber>

      <ProductQuantity>3</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00020</PurchaseOrderLineItemNumber>

      <ProductQuantity>32</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00020</PurchaseOrderLineItemNumber>

      <ProductQuantity>12</ProductQuantity>

   </OrderCreateProductLineItem>

   ...

The data in red correspond to data of the segment <E1EDP01>, while the information in blue correspond to data of the segment <E1EDP20> of the original IDoc. Note that the data in red need to be duplicated based on the occurence of segment <E1EDP20>.

Solution 1

A simple solution of this mapping problem exists under the assumption that in the IDoc ORDERS05 each segment <E1EDP01> contains at least one segment <E1EDP20>. Luckily this assumption holds true for IDocs created via the purchase order creation process in ERP.

1. Mapping of <OrderCreateProductLineItem>

The segment <OrderCreateProductLineItem> of the target structure needs to be created once for each occurrence of segment <E1EDP20> of our source structure. This can easily be achieved via the following mapping:

Remember to change the context to IDOC.

2. Mapping of <PurchaseOrderLineItemNumber>

Since the information for the node <PurchaseOrderLineItemNumber> comes from the IDoc segment <E1EDP01>, this node needs to be replicated based on the occurrence of segment <E1EDP20> under each segment <E1EDP01>. This can be achieved using the node function useOneAsMany as follows:

I will not describe the functionality of the node function useOneAsMany in detail since there is a lot of excellent documentation available on this function, for example in the SAP help . In our scenario you only need to change the context of the second input parameter to E1EDP01.

With the sample input from above, the function useOneAsMany will give the following result:

3. Mapping of <ProductQuantity>

Since the <ProductQuantity> is derived from a value of the IDoc segment <E1EDP20> the mapping for this field is trivial (remember that <ProductQuantity> must occur once per <E1EDP20> segment):

The Extended Problem

Now let's play around with an extended input XML that looks as follows (the data in italics have been added to the original input XML):

   ...

   <E1EDP01>

      <POSEX>00010</POSEX>

      <E1EDP20 SEGMENT="1">

         <WMENG>15</WMENG>

      </E1EDP20>

      <E1EDP20 SEGMENT="1">

         <WMENG>44</WMENG>

      </E1EDP20>

      <E1EDP20 SEGMENT="1">

         <WMENG>3</WMENG>

      </E1EDP20>

   </E1EDP01>

   <E1EDP01>

      <POSEX>00015</POSEX>

      <MENGE>67</MENGE>

   </E1EDP01>

   <E1EDP01>

      <POSEX>00020</POSEX>

      <E1EDP20 SEGMENT="1">

         <WMENG>32</WMENG>

      </E1EDP20>

      <E1EDP20 SEGMENT="1">

         <WMENG>12</WMENG>

      </E1EDP20>

   </E1EDP01>

   ...

If we try to apply the mapping we defined above, it will fail with a runtime error since the node function useOneAsMany requires at least one segment <E1EDP20> within each segment <E1EDP01> (the added segment <E1EDP01> does not contain this sub-segment).

In the remainder of the weblog I will show different possibilities (depending on the desired outcome) on how to solve this problem.

Solution 2

In this scenario we assume that segment <OrderCreateProductLineItem> should only be created if there is a corresponding segment <E1EDP20> in the source structure. In this case the result document is identical to the result of our basic problem (the added segment <E1EDP01> is ignored).

1. Mapping of <OrderCreateProductLineItem>

Since the segment <OrderCreateProductLineItem> of the target structure should only be created if there is a corresponding <E1EDP20> segment in the source, the mapping is identical to the one in solution 1.

2. Mapping of <PurchaseOrderLineItemNumber>

Since the use of the node function useOneAsMany is not possible in this scenario we need to find a different solution for this mapping. Here we take advantage of a neat feature of the text function concat. This function concatenates two input streams. If an input value is missing in one queue, this function simply continues to use the last value that it read from the queue, for example:

Using this feature we can use the following mapping for our problem:

This mapping consists of two main parts:

  1. Find the segments <E1EDP01> that need to be supressed since they don't contain a sub-segment <E1EDP20> and retrieve the value of the field POSEX of the remaining segments <E1EDP01>
  2. Replicate the field POSEX according to the number of occurrences of segment <E1EDP20>

The first part is taken care of by the circled portion of the mapping. First we check if segment <E1EDP01> contains a segment <E1EDP20> ignoring the number of its occurrences (using node function collapseContexts). Then we retrieve the values of the field POSEX for the relevant segments only.

The second part is taken care of by the remainder of the mapping (taking advantage of the features of the text function concat).

3. Mapping of <ProductQuantity>

The mapping of the field <ProductQuantity> is identical to the mapping in solution 1.

Solution 3

In this scenario we assume that the segment <OrderCreateProductLineItem> should be created

  • for segments <E1EDP01> that contain a sub-segment <E1EDP20> once for each occurrence of segment <E1EDP20>
  • once for each segment <E1EDP01> without any sub-segment <E1EDP20>

In addition, the <ProductQuantity> is only filled if a value is available in segment <E1EDP20> (field WMENG).

Based on the sample source document from above this results into the following target message (the part in italics is new compared to the original target message):

   ...

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00010</PurchaseOrderLineItemNumber>

      <ProductQuantity>15</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00010</PurchaseOrderLineItemNumber>

      <ProductQuantity>44</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00010</PurchaseOrderLineItemNumber>

      <ProductQuantity>3</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00015</PurchaseOrderLineItemNumber>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00020</PurchaseOrderLineItemNumber>

      <ProductQuantity>32</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00020</PurchaseOrderLineItemNumber>

      <ProductQuantity>12</ProductQuantity>

   </OrderCreateProductLineItem>

   ...

1. Mapping of <OrderCreateProductLineItem>

The segment <OrderCreateProductLineItem> now needs to be created as well when there is no corresponding <E1EDP20> segment available. Therefore the mapping for this field has to be changed as follows:

Note that the values used in the constants are completely arbitrary and not used anywhere. The values I use only help in the debugging tool (Display Queue) to determine if the particular occurrence is based on the existence of segment <E1EDP20> or not.

2. Mapping of <PurchaseOrderLineItemNumber>

Similar to the mapping in solution 2 we take advantage of the features in the text function concat. The mapping for this solution looks as follows:

3. Mapping of <ProductQuantity>

In the mapping of the <ProductQuantity> we need to take care of the fact that this field needs to be skipped if the segment <OrderCreateProductLineItem> is created without the existence of a corresponding segment <E1EDP20>. This can be achieved with the following mapping:

Note that the property value keepss of the function ifWithoutElse is set to true. With this setting the return value SUPPRESS is returned if the condition is not met.

Solution 4

In our final scenario we assume that the segment <OrderCreateProductLineItem> should be created

  • for segments <E1EDP01> that contain a sub-segment <E1EDP20> once for each occurrence of segment <E1EDP20>
  • once for each segment <E1EDP01> without any sub-segment <E1EDP20>

In addition, the <ProductQuantity> should be filled

  • from the value of field WMENG of segment <E1EDP20> if this segment exists underneath the corresponding segment <E1EDP01>
  • from the value of field MENGE of segment <E1EDP01> if this segment does not contain a sub-segment <E1EDP20>

Based on the sample source document from above this results into the following target message (the part in italics is new compared to the original target message):

   ...

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00010</PurchaseOrderLineItemNumber>

      <ProductQuantity>15</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00010</PurchaseOrderLineItemNumber>

      <ProductQuantity>44</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00010</PurchaseOrderLineItemNumber>

      <ProductQuantity>3</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00015</PurchaseOrderLineItemNumber>

      <ProductQuantity>67</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00020</PurchaseOrderLineItemNumber>

      <ProductQuantity>32</ProductQuantity>

   </OrderCreateProductLineItem>

   <OrderCreateProductLineItem>

      <PurchaseOrderLineItemNumber>00020</PurchaseOrderLineItemNumber>

      <ProductQuantity>12</ProductQuantity>

   </OrderCreateProductLineItem>

   ...

1. Mapping of <OrderCreateProductLineItem>

The mapping of segment <OrderCreateProductLineItem> is identical to the mapping in solution 3.

2. Mapping of <PurchaseOrderLineItemNumber>

The mapping of field <PurchaseOrderLineItemNumber> is identical to the mapping in solution 3.

3. Mapping of <ProductQuantity>

The <ProductQuantity> comes from two different sources:

  • field WMENG of segment <E1EDP20> if this segment exists
  • field MENGE of segment <E1EDP01> if no corresponding segment <E1EDP20> exists

This can be realized with the following mapping:

Remember to set the context of the field WMENG to E1EDP01.

3 Comments