Thursday, July 30, 2015

Batch Input - BDC via custom class tool set - call of LX47 example

I think, no SAP developer can avoid to touch batch input transaction calls. There is a few constantly repeated steps, which could be a bit easier if you would use some custom set of functions, the best way a custom class. So, let's take a look at my solution, maybe it becomes handy for you.

Good source to start is here.

Hot to get BDC commands?

If you are new here, definitely take a look at transaction SHDB (Batch Input Transaction Recorder). It can save you lot of pains when you would be searching for proper BDC commands. Sometime even dynamically generated screens have one dynpro number in foreground and different one in background call. It means, without tool as SHDB, you are absolutely lost.

In my case, I imitated manual usage of transaction LX47. The result of such a SHDB recording can looks like this.



Ok, we have a list of commands needed for background call. Now we can setup our tool set. There is some abstract base class suitable to be an ancestor of some real controller class.

The class definition


*----------------------------------------------------------------------*
*       CLASS lcl_ctrl_bapi DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_ctrl_base DEFINITION ABSTRACT.
  PUBLIC SECTION.
    METHODS
             bdc_call_transaction
                IMPORTING iv_transaction TYPE bdc_prog
                          iv_mode        TYPE c1
                          iv_dynrbegin   TYPE bdc_start
                RAISING lcx_msg_exception,
             bdc_add_dynpro
                IMPORTING iv_program   TYPE bdc_prog
                          iv_dynpro    TYPE bdc_dynr
                          iv_dynrbegin TYPE bdc_start,
             bdc_add_param
                IMPORTING iv_name      TYPE fnam_____4
                          iv_value     TYPE bdc_fval,
             bdc_init,
             bdc_send_enter,
             bdc_send_execute,
             bdc_send_end,
             bdc_send_update,
             bdc_send_select_all.


  PRIVATE SECTION.
    DATAt_bdcdata TYPE STANDARD TABLE OF bdcdata,
          s_bdcdata LIKE LINE OF t_bdcdata.   " Structure type of bdcdata

ENDCLASS.                    "lcl_ctrl_base DEFINITION



The class implementation


*----------------------------------------------------------------------*
*       CLASS lcl_ctrl_bapi IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_ctrl_base IMPLEMENTATION.


  METHOD bdc_call_transaction.

    DATAlt_msg TYPE TABLE OF bdcmsgcoll.   " Collecting Error messages
    FIELD-SYMBOLS<fs_msg> TYPE bdcmsgcoll.

    CALL TRANSACTION
      iv_transaction
      USING  t_bdcdata
      MODE   iv_mode
      UPDATE 'S' " Synchronous/Asynchronous/Local update task
      MESSAGES INTO lt_msg.

    IF sy-subrc NE 0.
*    Error Found - fire the first one...
      LOOP AT lt_msg INTO <fs_msg> WHERE msgtyp EQ 'E'.
        RAISE EXCEPTION TYPE lcx_msg_exception
          EXPORTING
            is_msg_bdcmsgoll <fs_msg>.
      ENDLOOP.
    ENDIF.
  ENDMETHOD.                    "bdc_call_transaction

  METHOD bdc_add_dynpro.
    CLEAR s_bdcdata.
    s_bdcdata-program  iv_program.
    s_bdcdata-dynpro   iv_dynpro.
    s_bdcdata-dynbegin iv_dynrbegin.
    APPEND s_bdcdata TO t_bdcdata.
  ENDMETHOD.                    "bdc_add_dynpro

  METHOD bdc_add_param.
    CLEAR s_bdcdata.
    s_bdcdata-fnam iv_name.
    s_bdcdata-fval iv_value.
    CONDENSE s_bdcdata-fval.
    APPEND s_bdcdata TO t_bdcdata.
  ENDMETHOD.                    "bdc_add_field

  METHOD bdc_init.
    CLEAR   s_bdcdata.
    REFRESH t_bdcdata.
  ENDMETHOD.                    "bdc_init

  METHOD bdc_send_enter.
    me->bdc_add_param(
      EXPORTING
        iv_name  'BDC_OKCODE'
        iv_value '/00').
  ENDMETHOD.                    "bdc_enter

  METHOD bdc_send_execute.
    me->bdc_add_param(
      EXPORTING
        iv_name  'BDC_OKCODE'
        iv_value '=ONLI').
  ENDMETHOD.                    "bdc_send_execute

  METHOD bdc_send_end.
    me->bdc_add_param(
      EXPORTING
        iv_name  'BDC_OKCODE'
        iv_value '/EECAN').   "iv_value = '/EENDE').
  ENDMETHOD.                    "bdc_send_end

  METHOD bdc_send_update.
    me->bdc_add_param(
      EXPORTING
        iv_name  'BDC_OKCODE'
        iv_value '=COMMIT').
  ENDMETHOD.                    "bdc_send_update

  METHOD bdc_send_select_all.
    me->bdc_add_param(
      EXPORTING
        iv_name  'BDC_OKCODE'
        "iv_value = '/EENDE').
        iv_value '=&ALL').
  ENDMETHOD.                    "bdc_send_select_all

ENDCLASS.                    "lcl_ctrl_base IMPLEMENTATION




And now, how to use it?


Imagine, you have some your own controller class inheriting from abstract base class above. So its method can looks as follows.

  METHOD bdc_call_lx47.

    " Input check
    IF iv_lgnum IS INITIAL OR iv_tanum IS INITIAL ).
      RETURN.
    ENDIF.

    DATA lv_value TYPE bdc_fval.

    me->bdc_init).

    me->bdc_add_dynpro(
      EXPORTING
        iv_program   'RLLX4700'
        iv_dynpro    '1000'
        iv_dynrbegin 'X'
      ).

    MOVE iv_lgnum TO lv_value.
    me->bdc_add_param(
      EXPORTING
        iv_name      'PV_LGNUM'     " warehouse num.
        iv_value     lv_value
      ).

    CLEAR lv_value.
    MOVE iv_tanum TO lv_value.
    me->bdc_add_param(
      EXPORTING
        iv_name      'PR_TANUM-LOW' " transfer order num.
        iv_value     lv_value
      ).

    me->bdc_add_param(
      EXPORTING
        iv_name      'PV_MARK'      " set active, the checkbox
        iv_value     'X'
      ).

    me->bdc_send_execute)" emulate click on first screen

    " Select all
    me->bdc_add_dynpro(
      EXPORTING
        iv_program   'SAPMSSY0'
        iv_dynpro    '0120'
        iv_dynrbegin 'X'
      ).
    me->bdc_send_select_all).

    " Update
    me->bdc_add_dynpro(
      EXPORTING
        iv_program   'SAPMSSY0'
        iv_dynpro    '0120'
        iv_dynrbegin 'X'
      ).
    me->bdc_send_update).

    me->bdc_call_transaction(
      EXPORTING
        iv_transaction 'LX47'
        iv_mode        'N' " A - foreground / E - errors only / N - no display
        iv_dynrbegin   abap_true
    ).

  ENDMETHOD.                    "bdc_call_lx47


To be exact in case of LX47 I had to check if any data exists before calling of second screen number 120, that why I used below check before actual BDC call. So this part is just for those who are interested in full LX47 call :)

METHOD bdc_check_call_lx47.

    " Input check
    IF iv_lgnum IS INITIAL OR iv_tanum IS INITIAL ).
      RETURN.
    ENDIF.

    " LX47 data check
    " Read data from the transfer order header table
    DATAlv_mark  TYPE lvs_selkz,
          lv_item  TYPE lvs_selkz,
          lt_ltodn TYPE STANDARD TABLE OF ltodn,
          lt_tanum TYPE STANDARD TABLE OF range_n10,
          ls_tanum LIKE LINE OF lt_tanum.

    lv_mark  abap_true.
    ls_tanum-sign   'I'.
    ls_tanum-option 'EQ'.
    ls_tanum-low   iv_tanum.
    APPEND ls_tanum TO lt_tanum.

    CALL FUNCTION 'L_TO_DN_READ'
      EXPORTING
        iv_lgnum        iv_lgnum
        iv_mark         lv_mark
        iv_item         lv_item
      TABLES
        et_ltodn        lt_ltodn
        ir_tanum        lt_tanum
                                      "ir_vbeln        = pr_vbeln
      EXCEPTIONS
        not_found       1
        not_found_to_dn 2
        not_found_dn    3.

    IF sy-subrc NE 0.
      RETURN" no data for BDC LX47 call
    ENDIF.

    me->bdc_call_lx47(
      EXPORTING
        iv_lgnum iv_lgnum
        iv_tanum iv_tanum
    ).

  ENDMETHOD.                    "bdc_check_call_lx47