Multiple Value Attributes

This blog will describe how to use multiple value attributes to transport complex data from the CRM application to the pricing engine.

Background

The scenario is that a customer specific rounding rule is implemented in a custom (Z) table. The rounding rule goes something like “Up to EUR 50, round to full 1 EUR; up to EUR 150, round to full 5 EUR; up to EUR 500, round to full 10 EUR; up to EUR 1000, round to full 50 EUR”.

Since these rounding ranges depend on the net price which is calculated in the pricing engine, it is not possible to preselect the correct rounding rule before pricing is executed – the selection has to happen within the pricing exection.

To enable this, we will use multiple value attributes to transport the entries from that table to the pricing engine and select the correct one there during execution in a value user exit. 1*) 2*)

Multivalue Attributes

Mulivalue attributes are attributes in the field catalogue which can have more than one value. You can imagine them like an array of values. A typical example for a multivalue attribute is the CAMPAIGN_GUID in SAP standard, which can have more than one value if multiple campaigns apply to the item.

You can use multivalue attributes like campaigns do, to apply multiple different instances of the same condition type to an item. In our example however we use the multivalue attributes in the background just to transport data from CRM to the pricing engine.

Customizing

We will have a multivalue attribute, and every value from that attirbute will represent one line in the table we want to make available to a user exit. As the data element we use a simple character 50 field for a “line” from the database table.

Initially, multivalue attributes are maintained exactly the same way like any other field in the field catalogue:
fieldcatalogue_init.PNG

The setting for multivalue attributes can be found in table /SAPCND/T681FA. There is no maintenance view for this table, so this has to be maintained directly in the table by a user with priviliged access.

t81famaintain00.PNG

t81famaintain.PNG

Setting the IS_MULTI_VALUED field to X makes YPRC01_ROUNDING a multivalue field.

Populating values

Multivalue attributes are populated similarly to normal attributes: In BAdI CRM_COND_COM_BADI.

method IF_EX_CRM_COND_COM_BADI~HEADER_NAME_VALUE.

data:   ls_YVA1PR000001 type YVA1PR000001,
        lt_YVA1PR000001 type table of YVA1PR000001,
        ls_name_value_pair type PRCT_ATTR_NAME_VALUE,
        lv_temp_value type PRCT_ATTR_VALUE,
        lv_temp_curr01 type char15,
        lv_temp_curr02 type char15,
        lv_temp_decimal type char12
      .
constants: lc_seperator type char1 value ';'.

select * from YVA1PR000001 into table lt_YVA1PR000001.

  loop at lt_yva1pr000001 into ls_yva1pr000001.
    clear: ls_name_value_pair,
           lv_temp_value.

    move ls_yva1pr000001-zzamountto to lv_temp_curr01.
    move ls_yva1pr000001-zzamountfrom to lv_temp_curr02.
    move ls_yva1pr000001-zzdecimal  to lv_temp_decimal.

    concatenate lv_temp_curr01
                lc_seperator
                lv_temp_curr02
                lc_seperator
                ls_yva1pr000001-zzroundrule
                lc_seperator
                lv_temp_decimal
    into lv_temp_value.

    ls_name_value_pair-seq_number = sy-tabix .
    ls_name_value_pair-attr_name = 'YPRC01_ROUNDING'.
    ls_name_value_pair-attr_value = lv_temp_value.

    insert ls_name_value_pair into table ct_item_name_value  .

  endloop.
 
endmethod.

Multivalue attributes can be populated in the two BAdI methods HEADER_NAME_VALUE and ITEM_NAME_VALUE. The interface to both is different from the more common ITEM_COMMUNICATION_STRUCTURE method. Since we only need to transport the values to the pricing engine once we use HEADER_NAME_VALUE.

HEADER_NAME_VALUE lets the implementation change a table of name-value pairs for every attribute. The name value pairs are held in structure PRCT_ATTR_NAME_VALUE:

namevaluepairs.PNG

For the system to work you have to populate a number between 01 and 99 into the SEQ_NUMBER field. Unfortunately this also means you cannot have more than 99 values for one attribute.

In the coding sample we concatenate all the fields from one line from the customer database table into a single line, comma-separated, and assign a sequence number equal to the tabular index of the loop. Again, this works as long as there are not more than 99 entries in the customer database table, but will break for more entries.

Once BAdI and field catalogue are activated, the database table is now transported into the pricing engine via a multiple value attribute.

Reading the values from a Userexit

finally, we can use the method getAttributeValues(String name) from the IPricingItemUserExit interface to retrieve the different values for an attribute in a String array. The coding below reads all the entries, explodes the comm-separated lines into individual columns, and identifies the correct line to read the rounding rule from based on the net value:

public class YPRC01_Rounding extends ValueFormulaAdapter {
    
     private static UserexitLogger log = new UserexitLogger(YPRC01_Rounding.class);
     private int decimals;
    
     public BigDecimal overwriteConditionValue(IPricingItemUserExit pricingItem,
               IPricingConditionUserExit pricingCondition) {
         
          this.determineRoundingRules(pricingItem);
         
          return this.doRounding(pricingItem);
     }
    
     /**
      * Read multi value attribute YPRC01_ROUNDING to get
      * the rounding rules per net value interval.
      */
     private void determineRoundingRules(IPricingItemUserExit pricingItem) {
         
          String[] yva1pr000001 = pricingItem.getAttributeValues("YPRC01_ROUNDING");
         
          log.writeLogDebug("Found " + yva1pr000001.length + " entries in YPRC01_ROUNDING");
          BigDecimal valueTo = null;
          BigDecimal valueFrom = null;
          BigDecimal netValue;
          String[] columns;
         
          netValue = pricingItem.getNetValueAsBigDecimal();
         
          for (int i = 0; i < yva1pr000001.length; i++) {
              
               columns = yva1pr000001[i].split(";");

               try {
                    valueTo = new BigDecimal( columns[0].trim());
                    valueFrom = new BigDecimal(columns[1].trim());
                   
                    // find the decimals to round to from the right interval
                    if (netValue.compareTo(valueFrom) > 0 && netValue.compareTo(valueTo) <= 0) {
                         decimals = Integer.parseInt(columns[2]);
                         return;
                    }
               catch (NumberFormatException e) {
                    log.writeLogError(e.getMessage());
               }
          }
     }
         
     private BigDecimal doRounding(IPricingItemUserExit pricingItem) {
          // implement rounding logic
          return null;
     }
}
     

Summary

This was a simply introduction on how to use multivalue attributes to transport data more complex than single fields to Java userexit. This technique can be helpful in many situations and in my opinion is preferable to adding multiple fields to the communication strructure, or reading from the databse directly.

Notes:

1*) It is possible to directly read database tables from the pricing engine using the db object. I would strongly discourage that though.

2*) A cleaner solution to this example would probably be to maintain the rounding rules in a scale, and have the pricing engine determine the correct range in a statistical pricing procedure entry, and use that in a userexit.

To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

  1. J. Nijholt

    Thanks for sharing this! I tried to use this in the past but I got stuck because I did not know how to set the attribute to multi value. In stead I passed the values in pairs in a long field with a separator but then run into an IPC limitation of 51 characters…

    (0) 

Leave a Reply