Sunday, July 31, 2016

Source code exploration using regular expressions


Once I faced a situation when I need to remove hard coded values from a huge program. There was no way to do it manually. There was really enough various combinations of these numbers. And to use "Find" from SE80 for each one was simply a nightmare. I came to a conclusion to create a small tool reading source code of any program with all its includes with ability to search them by regex. And here it comes.

Usage example


To use the tool you have to understand regular expressions a bit. The tool would be as much powerful as are your regular expressions skills.


Output could look like e.g.


Regex examples for source code exploration tool

Below you can find input regular expressions to reach in source code:

Any 4 digit number
^.+(\d{4}).+$

Any 4 digit number within apostrophes
^.+(')(\d{4})(').+$
  
Any 4 digit number within apostrophes but not in commented rows
^[^\*].+(')(\d{4})(').+$

Any 4 digit number within apostrophes but in commented rows only
^[*].+(')(\d{4})(').+$

Any word (not a number) within apostrophes – e.g. some non numeric literals
^[^\*].+(')(\w+)(').+$

Any 4 digit number 
1. within apostrophes 
2. not in commented rows 
3. avoiding words “bildschirm” or “screen” or “dynnr” preceding the digit
^[^\*]((?!bildschirm|screen|dynnr).)*(')(\d{4})(').+$
  
A four digit number 
• starting with “09”
• and continues with any two digits number
• it includes sub strings
^.+(09)(\d{2}).+$

Search for usernames 
• starting with “IT”
• and continues with any five digits number
• note: it can discover hardcoded behavior for certain users
^.+(IT)(\d{5}).+$

  

Source code 

*&---------------------------------------------------------------------*
*& Report  Y_REPORT_REGEX_SEARCH
*&
*&---------------------------------------------------------------------*
*&
*& Search source code by regex
*&---------------------------------------------------------------------*

REPORT y_report_regex_search.

TABLES: tstc, trdir.

*******************************************************************
*   SCREENS - choose transaction or program
*******************************************************************
SELECTION-SCREEN BEGIN OF BLOCK frame1 WITH FRAME TITLE text-001.
" transaction
SELECTION-SCREEN BEGIN OF LINE.
PARAMETERS: p_trans  RADIOBUTTON GROUP grp1 USER-COMMAND flagcommand. " DEFAULT 'X'.
SELECTION-SCREEN COMMENT 3(60) text-002. " Transaction
SELECTION-SCREEN END OF LINE.
" program
SELECTION-SCREEN BEGIN OF LINE.
PARAMETERS: p_prog  RADIOBUTTON GROUP grp1 DEFAULT 'X'.
SELECTION-SCREEN COMMENT 3(60) text-003. " Program
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN SKIP.
SELECT-OPTIONS so_tcode FOR tstc-tcode NO INTERVALS. " Transaction Code
SELECT-OPTIONS so_prog FOR trdir-name NO INTERVALS.  " Program name

SELECTION-SCREEN END OF BLOCK frame1.


SELECTION-SCREEN BEGIN OF BLOCK frame2 WITH FRAME TITLE text-004.
PARAMETERS p_regex TYPE c LENGTH 100 LOWER CASE. " Regular expression
SELECTION-SCREEN END OF BLOCK frame2.

*******************************************************************
AT SELECTION-SCREEN OUTPUT . " PBO action
*******************************************************************
  " Set parameter according to radio
  LOOP AT SCREEN.
    IF p_prog <> 'X'.
      IF screen-name CS 'so_prog'.
        screen-active = 0.
        MODIFY SCREEN.
        CONTINUE.
      ENDIF.
    ELSEIF p_trans <> 'X'.
      IF screen-name CS 'so_tcode'.
        screen-active = 0.
        MODIFY SCREEN.
        CONTINUE.
      ENDIF.
    ENDIF.
  ENDLOOP.


*******************************************************************
START-OF-SELECTION.
*******************************************************************

* Get program name for transaction
  CONDENSE so_tcode-low NO-GAPS.
  IF p_trans EQ 'X'.
    CLEAR so_prog.
    SELECT SINGLE pgmna
      FROM tstc
      INTO so_prog-low
      WHERE tcode = so_tcode-low.
    IF sy-subrc NE 0.
      WRITE: /, 'Transaction does not exist : ', so_tcode-low.
      EXIT.
    ENDIF.
  ELSEIF p_prog EQ 'X'.
    SELECT SINGLE name
    FROM trdir
    INTO so_prog-low
    WHERE name = so_prog-low.
    IF sy-subrc NE 0.
      WRITE: /, 'Program does not exist : ', so_prog-low.
      EXIT.
    ENDIF.
  ENDIF.

  CONDENSE so_prog-low NO-GAPS.
  IF so_prog-low IS INITIAL.
    WRITE: /, 'There is no program given.'.
    EXIT.
  ENDIF.

* Internal table holding include names
  TYPES:
  BEGIN OF ts_includes,
  prgname(40),
  END OF ts_includes.

  DATA: lt_includes TYPE STANDARD TABLE OF ts_includes,
        ls_includes LIKE LINE OF lt_includes,
        lt_lines    TYPE TABLE OF string,
        ls_line     LIKE LINE OF lt_lines,
        lv_line_num        TYPE i,
        lv_lines_total_num TYPE i.

  DATA: lc_matcher TYPE REF TO cl_abap_matcher,
        lv_match   TYPE c LENGTH 1,
        lv_regex   TYPE string,
        lv_exception_detail TYPE string,
        oref   TYPE REF TO cx_root.

* Get include list
  CALL FUNCTION 'GET_INCLUDETAB'
    EXPORTING
      progname = so_prog-low " '/FDSEU/CP01_OVERVIEW' 'SAPMM07M'
    TABLES
      incltab  = lt_includes.

  MOVE p_regex TO lv_regex.

* Add body of main program into itab to be explored
  APPEND so_prog-low TO lt_includes.

  LOOP AT lt_includes INTO ls_includes .

    REFRESH lt_lines.
    READ REPORT ls_includes-prgname INTO lt_lines.

    CLEAR lv_line_num.

    LOOP AT lt_lines INTO ls_line.

      lv_line_num = lv_line_num + 1.

      TRY.
          " Does string match the regex pattern?
          FREE lc_matcher.
          lc_matcher = cl_abap_matcher=>create(
                      pattern     = lv_regex
                      ignore_case = abap_true
                      text        = ls_line ).
          lv_match = lc_matcher->match( ).
        CATCH cx_root INTO oref.
          lv_exception_detail = oref->get_text( ).
          WRITE:/ 'Regex error occured : ', lv_exception_detail.
          STOP.
      ENDTRY.
      IF lv_match EQ abap_true.
        WRITE:/ 'INCLUDE: ', ls_includes-prgname, '| LINE: ', lv_line_num, '| CODE: ', ls_line.
        CLEAR lv_match.
      ENDIF.
    ENDLOOP. " lines

    lv_lines_total_num = lv_lines_total_num + lv_line_num.

  ENDLOOP. " includes

  WRITE:/,/ 'Total number of explored lines: ', lv_lines_total_num.



Regular expressions sources

Regular expressions are common technique used in many various programming languages. These are also included within ABAP.

Basic understanding
http://zevolving.com/2013/10/abap-regex-regular-expressions/

SAP/SDN comprehensive PDF document
http://www.sdn.sap.com/irj/scn/go/portal/prtroot/docs/library/uuid/902ce392-dfce-2d10-4ba9-b4f777843182?overridelayout=true&49533857875589

https://help.sap.com/abapdocu_70/en/ABENREGEX_SYNTAX.htm

Regex toy – Regular expressions tester 

You can test your own patterns in standard SAP program the name “DEMO_REGEX_TOY”.

Sources: http://www.sdn.sap.com/irj/scn/go/portal/prtroot/docs/library/uuid/1f9cd701-0b01-0010-87b8-f86f9b7b823f?QuickLink=index&overridelayout=true&5003637741589

For example pick up all lines not containing words “cute” or “fast”, do not care for any apostrophes.
Regex: ^((?!cute|fast).)*$




Other sources:



Tuesday, May 31, 2016

How to manually refresh an ALV (CL_SALV_TABLE) with set events


SALV is beautiful class offering many useful functions including famous automatic setup of field catalog. Everyone tries to dig his own ALV tunnel to work with grids. SALV looks as an excellent option, sure. But almost everything has its own BUT… SALV is not an exception. Let’s start my REFRESH journey.

Note: There e.g. class cl_gui_alv_grid with different behavior.

1) SALV without events

Imagine, we have straight forward setup. One screen with ALV grid inside custom container, all the stuff is  loaded within PBO section.

TOP include


Some TOP include should contain something like:

CONSTANTS gc_alv_kbed_container_name TYPE c LENGTH 11 VALUE 'CONTROL_ALV'.
DATA: gcr_container   TYPE REF TO cl_gui_custom_container,
            gcr_alv_control TYPE REF TO lcl_kbed_alv_control.


PBO ALV loader


MODULE d0100_alv_init OUTPUT.

  IF gcr_container IS NOT BOUND.
    CREATE OBJECT gcr_container
      EXPORTING
        container_name = gc_alv_kbed_container_name
      EXCEPTIONS
        others         = 1.
    IF sy-subrc NE 0.
      MESSAGE a001.
    ENDIF.

    CREATE OBJECT gcr_alv_control
      EXPORTING
        it_kbed                  = gt_kbed         " input global kbed table for ALV
        icr_container         = gcr_container
        iv_dynnr                = sy-dynnr.
    gcr_alv_control->display_output( ).
  ELSE.
    gcr_alv_control->refresh_alv( it_kbed = gt_kbed ).
  ENDIF.

ENDMODULE.                    


The constructor of our custom class could look like e.g.

  CLASS lcl_kbed_alv_control IMPLEMENTATION.

    METHOD constructor.

      " get data for ALV
      me->set_data( it_kbed ).

      " create ALV object by factory
      TRY.
          cl_salv_table=>factory(
            EXPORTING
              r_container  = icr_container
            IMPORTING
              r_salv_table = pvcr_alv
            CHANGING
              t_table      = me->pvt_kbed_alv ).
        CATCH cx_salv_msg INTO pvcx_root.
          MESSAGE pvcx_root TYPE 'E'.
      ENDTRY.

    ENDMETHOD.                   

The Refresh method 

    METHOD refresh_alv.

      me->set_data( it_kbed ).
      me->pvcr_alv->refresh( refresh_mode = if_salv_c_refresh=>full ).
      cl_gui_cfw=>flush( ).

    ENDMETHOD.           

Everything works fine. Container and ALV object are created just once in PBO. Every additional screen load causes just refresh of itab inside the ALV grid. Every sort or filter is kept. So, there is no problem so far.

2) SALV with events

Now we need to add some events, e.g. cell double click actions, e.g. adding of a new function. So some handlers are now taken into action. 

      " Register Events
      pvcr_events 
= pvcr_alv->get_event( ).
      
SET HANDLER me->on_link_click       FOR pvcr_events.
      
SET HANDLER me->on_cell_doubleclick FOR pvcr_events.
      
SET HANDLER me->on_added_function   FOR pvcr_events.

Now the game has changed. Our custom refresh methods is no longer working. Why?

We can set up a full refresh mode and there is still no effect? Even static method flush of cl_gui_cfw does not help.

      me->pvcr_alv->refresh( refresh_mode = if_salv_c_refresh=>full ).
      cl_gui_cfw=>flush( ).

So what's wrong?

In debug we can observe the different behavior:

The first picture shows scenario without events 

Everything works fine, refresh takes place.


The second one shows scenario with events 

Ups, suddenly we are kicked away from the method. No actual refresh is done at all.



How to solve refresh in this case?

We can create ALV every time screen is loaded. So PBO module could look like:

  " Refresh screen elements
  IF gcr_container IS BOUND.
    FREE gcr_alv_control.
    gcr_container->free( ).
  ENDIF.

  CREATE OBJECT gcr_container
    EXPORTING
      container_name = gc_alv_kbed_container_name
    EXCEPTIONS
      others         = 1.
  IF sy-subrc NE 0.
    MESSAGE a001.
  ENDIF.

  CREATE OBJECT gcr_alv_control
    EXPORTING
      it_kbed           = ls_kbed[]         " input global kbed table for ALV
      icr_container   = gcr_container
      iv_dynnr          = sy-dynnr.

  gcr_alv_control->display_output( ).



That's easy solution, fine, but what about keeping filters, sorting, any status of ALV object? For this case above trick does not work.



Refresh ala Naimesh 

The below code is inspired by Naimesh Patel's article "Editable SALV model". The goal is to call actual refresh method of ALV grid object. 


We need a bit rebuild the display method of upper custom ALV object.

    METHOD display_output.
      me->set_columns( ).
      me->set_alv_func_tools( ).
      me->set_layout( ).
      me->set_toolbar( ).

      DATA: lo_alv_mod TYPE REF TO cl_salv_model.

      lo_alv_mod ?= me->pvcr_alv.   
      CREATE OBJECT pvcr_salv_model.   
      pvcr_salv_model->get_model( io_model = lo_alv_mod ).


      me->pvcr_alv->display( ).   " SALV display method
    ENDMETHOD.                  



Also custom refresh_alv method needs some tuning. The only difference in comparing with Naimesh solution is type of lo_full_adap class, I used cl_salv_grid_adapter instead of cl_salv_fullscreen_adapter, otherwise it fell into casting error.

    METHOD refresh_alv.

      DATA: lo_grid   TYPE REF TO cl_gui_alv_grid,
            lo_full_adap TYPE REF TO cl_salv_grid_adapter. "cl_salv_fullscreen_adapter,


      me->set_data( it_kbed ).
      me->pvcr_salv_model->get_adapter( ).
      lo_full_adap ?= me->pvcr_salv_model->o_adapter.
      lo_grid = lo_full_adap->get_grid( ).
      lo_grid->refresh_table_display( ).


    ENDMETHOD.               


Now everything is fine. We have just one SALV object in memory keeping all sorts, filters and any status. We are just populating a new data into the grid. Everything works even with events, starting new transactions on cell double click.


Just for illustration I added some sample code how custom ALV class could look like. I removed many things to keep things simple for now. Sorry, I did not make any complete example. It's just a light version of real coding. But together with Naimesh example it is possible to catch the idea.

*----------------------------------------------------------------------*
*  Define the Local class inheriting from the CL_SALV_MODEL_LIST
*  to get an access of the model, controller and adapter which inturn
*  provides the Grid Object
*----------------------------------------------------------------------*
  CLASS lcl_salv_model DEFINITION INHERITING FROM cl_salv_model_list.
    PUBLIC SECTION.
      DATA: o_control TYPE REF TO cl_salv_controller_model,
            o_adapter TYPE REF TO cl_salv_adapter.
      METHODS:
         get_model
          IMPORTING
            io_model TYPE REF TO cl_salv_model,
         get_controller,
         get_adapter.
    PRIVATE SECTION.
      DATA: lo_model TYPE REF TO cl_salv_model.
  ENDCLASS.                    "LCL_SALV_MODEL DEFINITION



*----------------------------------------------------------------------*
* LCL_SALV_MODEL implementation
*----------------------------------------------------------------------*
  CLASS lcl_salv_model IMPLEMENTATION.
    METHOD get_model.
*   save the model
      lo_model = io_model.
    ENDMETHOD.
    METHOD get_controller.
*   save the controller
      o_control = lo_model->r_controller.
    ENDMETHOD.
    METHOD get_adapter.
*   save the adapter from controller
      o_adapter ?= lo_model->r_controller->r_adapter.
    ENDMETHOD.
  ENDCLASS.                    "LCL_SALV_MODEL IMPLEMENTATION



*----------------------------------------------------------------------*
*       CLASS lcl_kbed_alv_control DEFINITION
*----------------------------------------------------------------------*
* Main ALV class controling KBED table display
*----------------------------------------------------------------------*
  CLASS lcl_kbed_alv_control DEFINITION.
*
    PUBLIC SECTION.

      METHODS:
*     load data
        constructor
          IMPORTING
            it_kbed           TYPE ltty_kbed " ALV data table
            icr_container     TYPE REF TO cl_gui_custom_container
            iv_dynnr          TYPE sydynnr,
        refresh_alv
          IMPORTING
            it_kbed           TYPE ltty_kbed, " ALV data table
*     generate ALV output grid
        display_output.

    PRIVATE SECTION.

      DATA: pvcr_alv          TYPE REF TO cl_salv_table,
            pvcr_salv_model   TYPE REF TO lcl_salv_model,
            pvt_kbed_alv         TYPE STANDARD TABLE OF lsty_kbed_alv,
            pvcx_root               TYPE REF TO cx_root,
            pvcr_columns         TYPE REF TO cl_salv_columns_table,
            pvcr_funcs              TYPE REF TO cl_salv_functions_list,
            pvcr_events            TYPE REF TO cl_salv_events_table,
            pvcr_display           TYPE REF TO cl_salv_display_settings.

      METHODS:
        add_toolbar_function
            IMPORTING
              iv_name    TYPE c
              iv_icon    TYPE string
              iv_text    TYPE c
              iv_tooltip TYPE c,
*       transform data
          set_data
            IMPORTING it_kbed   TYPE ltty_kbed,
          set_alv_func_tools,
          set_layout,
          set_toolbar,
          set_columns,
*       event's method
          on_link_click FOR EVENT link_click OF cl_salv_events_table IMPORTING row column, " event for checkbox
          on_cell_doubleclick FOR EVENT double_click OF cl_salv_events_table IMPORTING row column, " normal double click/ hotspot is not needed in this case
          on_added_function FOR EVENT added_function OF cl_salv_events_table IMPORTING e_salv_function.
  ENDCLASS.                    " lcl_kbed_alv_control DEFINITION









Thursday, March 17, 2016

BAPI_HU_UNPACK clears LGN parameter or How to debug SPA-GPA memory parameters

BAPI_HU_UNPACK LGN parameter trap


Sometime standard function modules, BAPIs can cause a bit surprise. In my case it was BAPI_HU_UNPACK. Let's imagine we have a menu transaction which is sharing memory parameters across all called transactions. One of these SPA-GPA parameters was 'LGN' set by standard statement e.g.

    SET PARAMETER ID 'LGN'                 FIELD  gs_rfsuserpara-lgnum.

My task was to find a root cause why this parameter suddenly disappears from memory. At my surprise I found memory parameter 'LGN' is cleared by each run of BAPI_HU_UNPACK (My SAP is EHP6).

Here is the way how to track any SPA-GPA parameters


  1. Run debugger (suppose we have already set memory parameter by above statement), choose on the right side "New tool"

  2. From the tree menu choose, "Special Tools" and then "System Areas (Internal Only)"

  3. You will get the below screen

  4. Within the Cntnts tab fill Area field, type "SMEM" (for SPA-GPA parameters) and press Enter

  5. Now you can easily observe the values during the debug. Just scroll by arrows to wanted parameter and continue with debugging. Now it is easy to catch the responsible BAPI.


Monday, February 15, 2016

OSS NOTE 1825346 - Archiving of batches

This is just a small hint for those who will fight with implementation of SAP NOTE 1825346.

SAP NOTE text says in Delivery part:  Apply this correction and follow the manual steps.

But is it a bit tricky. Manual steps are supposed to be done after Applying the change. But when you run Note Assistant the transaction SNOTE , when you choose proper note 1825346, you are in the end ask to activate the changed source codes. But here the are connections/relations to newly created data elements, data dictionary structure, badi method interface done by manueal steps.

You can not start with manual steps only, because you would not see selection text keys as BMST_RTI, BM_RTI, BM_RTINO. There would not be possibility to fill the selection texts.

What worked for us was to create all data elements, structure (all data dictionary objects) in advance together with badi interface method definitions (CHECK_RETENTION_TIME_BM, CHECK_RETENTION_TIME_STOCK).

Later on we ran SNOTE transaction for note 1825346 and confirmed all needed steps, including source code activation. And it worked OK.

Then missing selection texts (BMST_RTI, BM_RTI, BM_RTINO) are visible in (SE38/SE80) and ready to be filled with texts.

That's all.




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.
    DATA: t_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.

    DATA: lt_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
    DATA: lv_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