DataXstream OMS+

How To Implement Field-Level HRMD_A Reduction

How To Implement Field-Level HRMD_A Reduction

I love ALE.  It is super-powerful and, once you get the hang of it, is a snap to configure.  Recently, I was setting up an HRMD_A interface for my client.  Everything was going smoothly until I ran into a requirement to filter out the social security number (PERID), birthdate (GBDAT), and gender (GESCH) for privacy reasons.  All of these fields are in segment E1P0002.  Initially, I thought that this requirement was easy enough to accomplish.  I just created a IDOC reduction in transaction BD53 and filtered out the three fields.  I soon found out that while entire segments were getting reduced from the IDOC as configured, the individually reduced fields were still showing up in the IDOC.  What’s going on?!?
Well, after some digging, I found OSS Note 381766.  This note’s cause section states:

The reduction at segment level (see also Note 301223) was facilitated in order to be able to copy message type HRMD_A. This involved setting the reduction at field level, which was not taken into account in the code because it is not possible to centrally determine which fields are mandatory and which are dependent for all country versions. In addition, for one info type there may several records for different periods of time, which may also cause a problem when you are reducing fields.

So, in essence, field-level reduction isn’t supported for HRMD_A.  The solution supplied in the OSS note suggests creating user-exit code to filter out the unwanted fields.  I already have implemented BADI HRALE00OUTBOUND_IDOC to add some other processing logic (if you’re lucky I’ll cover that logic in this blog, too), so I have a module in which I can place the code. And while the code to loop through an IDOC and clear out all instances of E1P0002-PERID, E1P0002-GBDAT, and E1P0002-GESCH is very easy, I can’t help but think I can make the code more useful.  You see, I hate unitasking code.  I can see it now.  If I take the shortcut path and wrote the unitasking code to clear those 3 fields, the code won’t be in production for more than a month before the requirements change and now there is another field that needs to be filtered out as well.  With unitasking code, I would have to crack open the code, create a transport, unit test, transport to Q, integration test, wait for a transport window, blah, blah, blah.  Ugh!  What a waste of time for a minor change.  We can do better.

I decided to create a multi-tasking module that will work for HRMD_A message types.  The fields to be cleared will be stored in TVARV in the format <SEGMENT>-<FIELD> (e.g. E1P0002-PERID).  That way, whenever the HR functional team wanted to filter out a new field, all they had to do was to update a TVARV variable.

To implement the solution, I created a new method called CLEAR_IDOC_FIELDS in my class implementation for BADI HRALE00OUTBOUND_IDOC.  CLEAR_IDOC_FIELDS has the same method signature as IF_EX_HRALE00OUTBOUND_IDOC~IDOC_DATA_FOR_RECEIVER_MODIFY:

The code is pretty straight forward.  To allow multiple interfaces to coexist in method CLEAR_IDOC_FIELDS, the TVARV variable that stores the fields to clear is in the format ‘ZHR_CLR_’ concatenated with the IDOC message type.

METHOD CLEAR_IDOC_FIELDS .
*----------------------------------------------------------------------*
* Class:     ZCL_IM_HRALE00OUTBOUNDIDOC                                *
* Method:    CLEAR_IDOC_FIELDS                                         *
* Author:    Craig Stasila                                             *
*----------------------------------------------------------------------*

  TYPES: BEGIN OF TY_FILTER,
           TABNAME   TYPE DFIES-TABNAME,
           FIELDNAME TYPE DFIES-FIELDNAME,
           POSITION  TYPE DFIES-POSITION,
           OFFSET    TYPE DFIES-OFFSET,
           LENG      TYPE DFIES-LENG,
        END OF TY_FILTER.

  DATA: WA_FILTER      TYPE TY_FILTER,
        LT_FILTER      TYPE TABLE OF TY_FILTER,
        LS_TABNAME     TYPE DDOBJNAME,
        LS_SAV_TABNAME TYPE DDOBJNAME.

  DATA: LS_NAME   TYPE TVARV-NAME,
        LT_TVARVC TYPE TABLE OF TVARVC,
        WA_TVARVC TYPE TVARVC.

  DATA: LT_DFIES TYPE TABLE OF DFIES,
        WA_DFIES TYPE DFIES.

  DATA: LR_SEGNAM TYPE RANGE OF EDIDD-SEGNAM,
        WR_SEGNAM LIKE LINE OF LR_SEGNAM.

  FIELD-SYMBOLS: <FS> TYPE LINE OF EDIDD_TT.

* Fields to clear are in variable
  CONCATENATE 'ZHR_CLR_' MESTYP INTO LS_NAME.

* Get list of fields to clear from TVARV
  SELECT * FROM TVARVC
    INTO TABLE LT_TVARVC
   WHERE NAME = LS_NAME
     AND TYPE = 'S'.

  CHECK SY-SUBRC = 0.

* Build list of fields to clear
  LOOP AT LT_TVARVC INTO WA_TVARVC.
    CLEAR WA_FILTER.
    SPLIT WA_TVARVC-LOW AT '-' INTO WA_FILTER-TABNAME WA_FILTER-FIELDNAME.
    CHECK SY-SUBRC = 0.

    COLLECT WA_FILTER INTO LT_FILTER.
  ENDLOOP.

  SORT LT_FILTER BY TABNAME.
  CLASS CL_ABAP_CHAR_UTILITIES DEFINITION LOAD.

* Get field offsets
  LOOP AT LT_FILTER INTO WA_FILTER.
    LS_TABNAME = WA_FILTER-TABNAME.

    IF LS_TABNAME NE LS_SAV_TABNAME.

      CALL FUNCTION 'DDIF_NAMETAB_GET'
        EXPORTING
          TABNAME   = LS_TABNAME
        TABLES
          DFIES_TAB = LT_DFIES
        EXCEPTIONS
          NOT_FOUND = 1
          OTHERS    = 2.

      IF SY-SUBRC <> 0.
        CONTINUE.
      ENDIF.

      SORT LT_DFIES BY TABNAME FIELDNAME.

    ENDIF.

    LS_SAV_TABNAME = LS_TABNAME.

    READ TABLE LT_DFIES INTO WA_DFIES
         WITH KEY TABNAME   = WA_FILTER-TABNAME
                  FIELDNAME = WA_FILTER-FIELDNAME
         BINARY SEARCH.

    CHECK SY-SUBRC = 0.

    WA_FILTER-POSITION = WA_DFIES-POSITION.
    WA_FILTER-OFFSET   = WA_DFIES-OFFSET / CL_ABAP_CHAR_UTILITIES=>CHARSIZE.
    WA_FILTER-LENG     = WA_DFIES-LENG.

    MODIFY LT_FILTER FROM WA_FILTER.

* Build segment name range
    IF SY-SUBRC = 0.
      WR_SEGNAM-SIGN   = 'I'.
      WR_SEGNAM-OPTION = 'EQ'.
      WR_SEGNAM-LOW    = WA_FILTER-TABNAME.
      COLLECT WR_SEGNAM INTO LR_SEGNAM.
    ENDIF.
  ENDLOOP.

* Loop at IDOC to clear fields
  LOOP AT IDOC_DATA ASSIGNING <FS>.
    IF <FS>-SEGNAM IN LR_SEGNAM.
      LOOP AT LT_FILTER INTO WA_FILTER WHERE TABNAME = <FS>-SEGNAM.
        CLEAR <FS>-SDATA+WA_FILTER-OFFSET(WA_FILTER-LENG).
      ENDLOOP.
    ENDIF.
  ENDLOOP.
ENDMETHOD.

I also added the following code to IF_EX_HRALE00OUTBOUND_IDOC~IDOC_DATA_FOR_RECEIVER_MODIFY:

* Clear IDOC fields
CALL METHOD ME->CLEAR_IDOC_FIELDS
  EXPORTING
     IDOC_CONTROL = IDOC_CONTROL
     RECEIVER     = RECEIVER
     MESTYP       = IDOC_CONTROL-MESTYP
  CHANGING
     IDOC_DATA    = IDOC_DATA.

Finally, I maintained the following TVARV variable:

That’s all it takes!  Now individual field values can be easily be filtered from outbound HRMD_A IDOCs!.

UPDATE: My colleague informed me that instead of using TVARV, I should continue to use BD53 to manage the IDOC reduction and change my code to look up the field reductions in TBD24.  This would be a great idea, but my full HRMD_A solution uses custom filter objects and a custom IDOC formatting function module.  And BD53 does not play well with these customizations.

4 Comments

  1. BD53 Doesn’t Play Well With Others : SAP Integration Experts – DataXstream
    February 25, 2010 at 11:03 am · Reply

    […]  My solution leveraged TVARV as a repository for the fields to clear.  Read the whole solution here. A colleague of mine was very quick to point out that instead of using TVARV as the method for […]

  2. Avatar for Doug
    Doug
    April 1, 2010 at 1:14 pm · Reply

    Great blog Craig,

    I am a novice but need to implement something very similar. I’m not getting the idea of adding a method to my impleemntation of the BADI interface. Doesn’t this go agains the whole idea of an interface. Would I do t hat in se19 or elsewhere?

    Thanks,
    Doug

  3. Avatar for Craig Stasila
    Craig Stasila
    April 12, 2010 at 10:42 am · Reply

    Doug,

    The new methods that I created were set to private. I used private methods for the main processing logic for two reasons:

    1. Using private methods improves the readability of the code
    2. The more important reason for using private methods is because BADI HRALE00OUTBOUND_IDOC is a single use BADI. That means if your SAP system has multiple IDOC interfaces based on HRMD_A, they will all use this BADI. I used private methods to separate the logic used in this interface from the logic that may be used in other interfaces.

    Lastly, the use of private methods in BADI classes that implement and interface is a textbook example of why Object-Oriented ABAP is a good thing. The BADI interface specifies which methods MUST be implemented. The private methods are used for proper code modularization and separation.

  4. Avatar for Michele González
    Michele González
    April 22, 2010 at 7:15 pm · Reply

    Hi Craig

    Thanks a lot for sharing your experience =)
    I do not have any experience with ALE, I have been reading and searching since I have a requirement of HR Master Data distribution via ALE, I have configured the distribution model, with standard filters provided within the HTMD_A message type, the partner profile everything and the distribution is working, the issue is when I tried to filtered by Company Code (BUKRS), I have followed the steps provided in the IMG guide:
    1. Define the filters as ALE object types.
    2. Assign the filters to the notification type HRMD_A.
    3. Add the filters to the distribution model.

    I am using T001T table with BUKRS field but it is not working, when I use that filter no data is being distributed even when some records fullfill all the filters…do you know what else do I have to do? =)

    Best Regards
    Michele

Leave a reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.