Wednesday, April 17, 2013

Preventing Hard coding in SAP CRM

In every implementation of SAP CRM we run into the some of the same challenges. One of these challenges is the prevention of hard coding in system enhancements. For instance hard coding of transaction types might cover current requirements, but maybe in the future a new transaction type will be created. In case the transaction type was hard coded, a program change is needed. So we do not like the below code since it is inflexible.



This blog describes one way of preventing hard coding, by creating a customizing table for these entries and showing how this table can be accessed by an access class. We do this by working out the following business case. For one of our customers we need to change the validity start date of a quotation only for two specific transaction types. Currently the check is hard coded as shown in above figure, the code is part of a CRM_ORDER_STATUS BAdI implementation.

 

Creation of customizing table

SAP CRM provides it own parameter customizing option via the transaction STVARV. With this transaction it is possible to define your own parameter and set a value for this parameter. In my experience it is useful to have the option to deviate per sales organisation and even distribution channel. For example maybe the Belgium business has other requirements then the Dutch Business. So in this example we have created our own table with the following key fields:
  • Usage Type – Parameter value
  • Sales Organisation – Sales Organisation
  • Distribution Channel – Distribution Channel


Next to the Key fields we use the standard fields for a range table. This will make it easier to use the values in the calling code. For our example we now need to divine a parameter and create two entries in the table, one for each transaction type.

 

Access class

To enable an easy access for calling programs we have created an access class that will build a range table. The method in this class will have the key fields of the table as import parameters. The export parameter will be a range table. One of the challenges here is that the range table should be different depending on the field that is in the value part. In our example this field should be of the same type as the field process type. We manage this by using a STANDARD TABLE and field symbols as follows.



DATA: lt_crm_customizing TYPE TABLE OF zcrm_customizing.

FIELD-SYMBOLS:
  <lfs_crm_customizing> TYPE zcrm_customizing,
  <lfs_custom_range>  TYPE any,
  <lfs_sign> TYPE any,
  <lfs_option> TYPE any,
  <lfs_low> TYPE any,
  <lfs_high> TYPE any.

CLEAR: et_custom_range.
SELECT * FROM zcrm_customizing INTO TABLE lt_crm_customizing
  WHERE usage_type = iv_usage
  AND sales_org = iv_sales_org
  AND distr_channel = iv_distr_channel.

LOOP AT lt_crm_customizing ASSIGNING <lfs_crm_customizing>.
  APPEND INITIAL LINE TO et_custom_range ASSIGNING <lfs_custom_range>.
  ASSERT sy-subrc = 0.
  ASSIGN COMPONENT 'SIGN' OF STRUCTURE <lfs_custom_range> TO <lfs_sign>.
  ASSERT sy-subrc = 0.
  ASSIGN COMPONENT 'OPTION' OF STRUCTURE <lfs_custom_range> TO <lfs_option>.
  ASSERT sy-subrc = 0.
  ASSIGN COMPONENT 'LOW' OF STRUCTURE <lfs_custom_range> TO <lfs_low>.
  ASSERT sy-subrc = 0.
  ASSIGN COMPONENT 'HIGH' OF STRUCTURE <lfs_custom_range> TO <lfs_high>.
  ASSERT sy-subrc = 0.
  <lfs_sign> = <lfs_crm_customizing>-sign.
  <lfs_option>  = <lfs_crm_customizing>-opti.
  <lfs_low> = <lfs_crm_customizing>-low.
  <lfs_high> = <lfs_crm_customizing>-high.
ENDLOOP.

ENDMETHOD.

Calling Program

The calling program needs to define a range table of the type of the expected value. In our example we expect a process type in the value so we create the range table as follows.

DATA: lt_range_proces TYPE RANGE OF crmt_orderadm_h_wrk-process_type.

We will now call the access class with this table and will tell the class what field type it should use for the range table.

zl_crm_customizing_access=>read_customizing(

  EXPORTING
    iv_usage  = 'QUOTE_VALID_START'
    iv_sales_org = lv_sales_org
    iv_distr_channel  = lv_dis_channel

  IMPORTING
    et_custom_range = lt_range_proces

).

The range table can now easily be used to check the transaction type.

CHECK ls_orderadm_h-process_type IN lt_range_proces.

If the same check should be executed for more transaction types in the future, it is only a matter of creating a new entry in customizing table and the work is done.

Other Examples

The same table will/can be used in case of the following scenario’s
  • ORDER_SAVE BAdI implementation with restriction on transaction type
  • Setting the description of an order/contract that is created via copy control.
  • Setting shipping condition based on sale organisation via CRM_SHIPPING_BADI

3 comments:

  1. Mark,
    Hope things are going well!
    Haven't we talked about this when working together and decided that it was not a good idea? :)
    These tables end up with content that nobody can change anymore, especially not business users.
    Create a decent, typed customizing table for each use case! Or constants in an ABAP interface. The latter allows you to trace back to the code where the constant is used and changes are only to the interface.
    Cheers,
    Michael

    ReplyDelete
    Replies
    1. I reckon this kind of configuration table showed in the article is a nice idea. In general, end user doesn't edit this table, only IT staff. If is necessary, it is possible create a friendly UI.

      In fact, I don't like using constants even in Abap interfaces. I know we would have difficult to track the key configurations.

      Regards,
      André

      Delete
  2. Mark,

    thanks for the clear explanation, happens to be that I need such fucntioanlity just the way u wrote it down.

    Keep up the good work.

    RIchard

    ReplyDelete