Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Tuesday, May 12, 2009

ABAP Web Dynpro: Let’s make a simple report

Intro

If you are trying to start development in Web Dynpro a word of advice, stay clear of SAP documentation or courses or books. They are just too complex for the uninitiated. A visit to them is not a bad idea if you know what you are looking for. But to start off it is better to look at some samples or basic tutorials. Since I have gone through this road I thought of writing a small intro with a simple example.

As you might have heard or know already SAP has made great strides in developing its platform for the web. First came the ITS server which was a transformation engine of sorts that converted the screens of SAP into HTML. Then the other technology introduced was BSP which was based on the model of JSP and ASP which involved writing a mix of HTML and ABAP . Now with ABAP (not talking about the JAVA one, that’s another story) Web Dynpro we have a full blown ABAP environment that create a pukka web application.

First let me clear one thing that might be confusing. What we in layman terms call Web Dynpro application is in fact in SAP lingo a Web Dynpro Component. For SAP, Web Dynpro application is similar to an ABAP transaction that refers to a report and a screen. The following image referred from help.sap.com tells more about it.

The Web Dynpro application is merely a link to a view of a Web Dynpro.

The Web Dynpro component contains any number of windows and views. It will have at least one window and one view for an Web Dynpro application to work. There is also the concept of controllers talked about which basically are the collection of attributes, events and methods that will influence the global component or view or window. Each of these have their own controller.

Practical

Enough of theory, let’s see how it translates to practical application.We will build a ABAP like report where we will display the address of customers.

image Web Dynpro Component

image The output

Component reuse

So as can be seen above, we have a selection screen and the output. Let’s see how to build the selection screen first, this is going to be used all the time in reporting. I haven’t built the selection screen myself but used one of SAP’s component. There is one great advantage of the component model of the Web Dynpro and that is reusability. SAP has a Web Dynpro Component WDR_SELECT_OPTIONS which provides the functionality of selection screen.

When you create a Web Dynpro it will create a Window and the global controller. To include an existing Web Dynpro component go to component definition screen and add WDR_SELECT_OPTIONS. Now that the Web Dynpro has been added we can add its components in the our own different components, in our case view component.

image

Create a view, give it a name e.g. V_RC_CUSTOMERS. Go to its properties tab and click on the create controller button. Select WDR_SELECT_OPTIONS/INTERFACECONTROLLER. This now can be initialised and used in this view. For the layout we have the following:

image

VC_SEARCH element is of type ViewContainerUIElement. The ViewContainerUIElement UI element is an area within a view that contains another view. We will embed the view WND_SELECTION_SCREEN of Web Dynpro WDR_SELECT_OPTIONS here. This can be done dynamically or statically. We are doing statically here. Go to the Window of our Component and drag and drop the view V_RC_CUSTOMERS into the Window W_RC_REP_CUSTOMERS. You will have something like this:

image

Initialise Component

VC_SEARCH is the place holder of the embedded view. Right click on VC_SEARCH and select embed view. In the window that pops up do an F4 on view to be embedded. Select WND_SELECTION_SCREEN. Having done this, when the Window calls the View V_RC_CUSTOMERS then the embedded View will also be called. There is just one more step before that happens. The component needs to be initialised first. We go back to the View and go to the method WDDOINIT. This is initialisation method of the View V_RC_CUSTOMERS. Create a local method INIT_SELECT_OPTIONS and add a call to this in WDDOINIT. Go the method init_select_options( ). SAP has some wizards that do very basic jobs. We will use that here to initiated the component that has been added. Click on the wizard. image

Select Instantiate Used Components

image

This will write a bunch of code that does the initiation. Now using the wizard we want to call the method of select_option controller that does the initialising of the selection screen and also returns a assisting class of the type IF_WD_SELECT_OPTIONS.

image

This again will write a bunch of code. Here is the final code listing:

method INIT_SELECT_OPTIONS .
* Call wizard to activate component usage of Select Option
  DATA: l_ref_cmp_usage TYPE REF TO if_wd_component_usage.
  l_ref_cmp_usage =   wd_this->wd_cpuse_select_options( ).
  IF l_ref_cmp_usage->has_active_component( ) IS INITIAL.
    l_ref_cmp_usage->create_component( ).
  ENDIF.
* Call wizard to make a call to init_selection_screen
  wd_this->o_wdr_so_component =   wd_this->wd_cpifc_select_options( ).
  wd_this->o_so_helper_class = wd_this->o_wdr_so_component->init_selection_screen( ).
  CALL METHOD wd_this->o_so_helper_class->set_global_options
    EXPORTING
      i_display_btn_cancel  = abap_false
      i_display_btn_check   = abap_false
      i_display_btn_reset   = abap_true
      i_display_btn_execute = abap_false.
endmethod.
We have a attributes defined in the view that will refer to the helper class of select_option view and the controller itself.

image


Add fields in selection

Now all the initialisation has been done, time to add some fields in the selection_option view. Again this will be done in the method WDDOINIT. For this we have created a local method called ADD_SELECTION_FIELDS. Using the helper class of select_option component we can create ranges for a given data type and then attach that range table in the view. Here is the code that will do that:
METHOD add_selection_fields .
* Add ranges to selection screen
  DATA:
    lt_range_table TYPE REF TO data.
* create a range table for customer
  lt_range_table = wd_this->o_so_helper_class->create_range_table( i_typename = 'KUNNR' ).
  wd_this->o_so_helper_class->add_selection_field( i_id         = 'KUNNR'
                                                   i_obligatory = abap_true
                                                   it_result    = lt_range_table ).
  CALL METHOD wd_this->o_so_helper_class->add_horizontal_divider
    EXPORTING
      i_id           = 'LINE'
*    I_WITHIN_BLOCK = MC_ID_MAIN_BLOCK
      .
* create a range table for Postal code
  lt_range_table = wd_this->o_so_helper_class->create_range_table( i_typename = 'PSTLZ' ).
  wd_this->o_so_helper_class->add_selection_field( i_id        = 'PSTLZ'
                                                   it_result   = lt_range_table ).
* create a range table for Region
  lt_range_table = wd_this->o_so_helper_class->create_range_table( i_typename = 'REGIO' ).
  wd_this->o_so_helper_class->add_selection_field( i_id        = 'REGIO'
                                                   it_result   = lt_range_table ).
* create a range table for email
  lt_range_table = wd_this->o_so_helper_class->create_range_table( i_typename = 'AD_SMTPADR' ).
  wd_this->o_so_helper_class->add_selection_field( i_id        = 'SMTP_ADDR'
                                                   it_result   = lt_range_table ).
ENDMETHOD.

Finally the WDDOINIT will look like this:
METHOD wddoinit .
  init_select_options( ).
  add_selection_fields( ).
ENDMETHOD.

Search Event

This is a web report that means the user has to press a submit button to send the selection options to get some result back. The submit is done by adding a button on the view layout that triggers an event in the form of a method.

image

As defined above we have SEARCH_CENTRES as the action handler and it will correspond to a method called ONACTIONSEARCH_CENTRES. In the event we will do 2 things. 1. Select the customer and show it on the screen. We have 2 methods for this:
METHOD onactionsearch_centres .
  select_centres( ).
  add_to_view( ).
ENDMETHOD.

Get ranges and select

We have to get the user entered selection options from the screen before we run the SQL on our tables. Again using the helper class of the select_option component we will call a method that returns the range tables filled with user entered data. Using these range table we can simple do select on our SAP tables. The code for SELECT_CENTRES is as:
METHOD select_centres .
  DATA:
  l_t_range TYPE REF TO data.
  FIELD-SYMBOLS:
    <l_fs_r_kunnr> TYPE ANY TABLE,
    <l_fs_r_pstlz> TYPE ANY TABLE,
    <l_fs_r_regio> TYPE ANY TABLE,
    <l_fs_r_smtp_addr> TYPE ANY TABLE,
    <l_fs_r_kvgr2> TYPE ANY TABLE,
    <l_fs_r_kvgr1> TYPE ANY TABLE,
    <l_fs_centre> TYPE zst_wd_centre_add.
  CALL METHOD wd_this->o_so_helper_class->get_range_table_of_sel_field
    EXPORTING
      i_id           = 'KUNNR'
    RECEIVING
      rt_range_table = l_t_range.
  ASSIGN l_t_range->* TO <l_fs_r_kunnr>.
  CALL METHOD wd_this->o_so_helper_class->get_range_table_of_sel_field
    EXPORTING
      i_id           = 'PSTLZ'
    RECEIVING
      rt_range_table = l_t_range.
  ASSIGN l_t_range->* TO <l_fs_r_pstlz>.
  CALL METHOD wd_this->o_so_helper_class->get_range_table_of_sel_field
    EXPORTING
      i_id           = 'REGIO'
    RECEIVING
      rt_range_table = l_t_range.
  ASSIGN l_t_range->* TO <l_fs_r_regio>.
  CALL METHOD wd_this->o_so_helper_class->get_range_table_of_sel_field
    EXPORTING
      i_id           = 'SMTP_ADDR'
    RECEIVING
      rt_range_table = l_t_range.
  ASSIGN l_t_range->* TO <l_fs_r_smtp_addr>.
  SELECT kna1~kunnr adrnr INTO TABLE wd_this->t_centres
    FROM kna1
    JOIN knvv ON knvv~kunnr = kna1~kunnr
    AND          knvv~vkorg = '5000'
    AND          knvv~vtweg = '05'
    AND          knvv~spart = '50'
    JOIN adr6 ON adr6~addrnumber = kna1~adrnr
    AND          adr6~persnumber = space
    JOIN knvp ON knvp~kunnr = kna1~kunnr
    AND          knvp~kunn2 <> kna1~kunnr
    AND          knvp~parvw = 'RE' " Bill to party
    WHERE kna1~kunnr IN <l_fs_r_kunnr>
    AND   pstlz IN <l_fs_r_pstlz>
    AND   regio IN <l_fs_r_regio>
    AND   smtp_addr IN <l_fs_r_smtp_addr>.
  LOOP AT wd_this->t_centres ASSIGNING <l_fs_centre>.
    CALL FUNCTION 'ADDRESS_INTO_PRINTFORM'
      EXPORTING
*       ADRSWA_IN                            =
*       ADDRESS_1                            =
*       ADDRESS_2                            =
*       ADDRESS_3                            =
       address_type                         = '1'
       address_number                       = <l_fs_centre>-adrnr
*       ADDRESS_HANDLE                       = ' '
*       PERSON_NUMBER                        = ' '
*       PERSON_HANDLE                        = ' '
*       SENDER_COUNTRY                       = ' '
*       RECEIVER_LANGUAGE                    = ' '
*       NUMBER_OF_LINES                      = 10
*       STREET_HAS_PRIORITY                  = ' '
*       LINE_PRIORITY                        = ' '
*       COUNTRY_NAME_IN_RECEIVER_LANGU       = ' '
*       LANGUAGE_FOR_COUNTRY_NAME            = ' '
*       NO_UPPER_CASE_FOR_CITY               = ' '
*       IV_NATION                            = ' '
*       IV_NATION_SPACE                      = ' '
*       IV_PERSON_ABOVE_ORGANIZATION         = ' '
*       IS_BUPA_TIME_DEPENDENCY              = ' '
     IMPORTING
*       ADRSWA_OUT                           =
       address_printform                    = <l_fs_centre>-address
*       ADDRESS_SHORT_FORM                   =
*       ADDRESS_SHORT_FORM_S                 =
*       ADDRESS_DATA_CARRIER                 =
*       ADDRESS_DATA_CARRIER_0               =
*       NUMBER_OF_USED_LINES                 =
*       NAME_IS_EMPTY                        =
*       ADDRESS_NOT_FOUND                    =
*       ADDRESS_PRINTFORM_TABLE              =
*       ADDRESS_SHORT_FORM_WO_NAME           =
*       EV_NATION                            =
              .
  ENDLOOP.
ENDMETHOD.
We will store the selected data in the attribute of the view.

Show

Now that we have the data in an internal table, we have to display it. Note that I am not using Context here which is another way of storing data within the components of a Web Dynpro.

To display the data we are going to do a bit of dynamic programming as the number of lines is variable. But to see what we want to achieve I first made UI elements in the layout so that I knew which ones to use and where to place them. Having known which elements to use, we just program it dynamically and attaching them to the view. The design is worked out as follows:

image

Create a group and within the group create transparent container for each customer. Each customer container will have one container containing the customer number and another container containing the address lines.

Any UI element that needs to be accessed or created requires a handle on the view first. We use the method WDDOMODIFYVIEW to get the reference for the view object. Code is:
METHOD wddomodifyview .
  IF first_time = abap_true.
    wd_this->o_view = view.
  ENDIF.
ENDMETHOD.
And finally code to actual creation of UI elements:
METHOD add_to_view .
  DATA:
    l_o_container TYPE REF TO cl_wd_uielement_container,
    l_o_group     TYPE REF TO cl_wd_group,
    l_o_caption   TYPE REF TO cl_wd_caption,
    l_o_t_cont_top   TYPE REF TO cl_wd_transparent_container,
    l_o_t_cont   TYPE REF TO cl_wd_transparent_container,
    l_o_ft_view   TYPE REF TO cl_wd_formatted_text_view,
    l_str_kunnr TYPE string,
    l_str_line TYPE string,
    l_cell_backgroup TYPE wdui_cell_bg_design VALUE '00', " Transparent
    l_rc TYPE i.
  FIELD-SYMBOLS:
    <l_fs_centres> TYPE zst_wd_centre_add,
    <l_fs_line> TYPE lines.
*  CHECK wd_this->t_centres IS NOT INITIAL.
* Get access to the route
  l_o_container ?= wd_this->o_view->get_element( 'ROOTUIELEMENTCONTAINER' ).
* Remove any previous existance
  l_o_container->remove_child( id = 'GRP_CENTRES' ).
* Create a new group
  l_o_group = cl_wd_group=>new_group( id = 'GRP_CENTRES'
                                      design = '03' ). "  secondarybox
  l_o_caption = cl_wd_caption=>new_caption( text = 'Centre Addresses' ).
  l_o_group->set_header( the_header = l_o_caption ).
  cl_wd_flow_data=>new_flow_data( element = l_o_group ).
  cl_wd_grid_layout=>new_grid_layout( cell_padding = 5
                                      cell_spacing = 5
                                      col_count    = 1
                                      container    = l_o_group ).
  LOOP AT wd_this->t_centres ASSIGNING <l_fs_centres>.
* Create a new transparent container to contain centre and address
    l_o_t_cont_top = cl_wd_transparent_container=>new_transparent_container( ).
* For each new centre toggle background colour
    IF l_cell_backgroup = '00'.
      l_cell_backgroup = '01'.                              " Fill1
    ELSE.
      l_cell_backgroup = '00'. " Transparent
    ENDIF.
    cl_wd_grid_data=>new_grid_data( cell_background_design = l_cell_backgroup
                                    element                = l_o_t_cont_top ).
    cl_wd_grid_layout=>new_grid_layout( cell_padding = 5
                                        cell_spacing = 5
                                        col_count    = 2
                                        container    = l_o_t_cont_top ).
* add Center code to a new T container
    l_o_t_cont = cl_wd_transparent_container=>new_transparent_container( ).
    cl_wd_grid_data=>new_grid_data( element = l_o_t_cont
                                    v_align = '01' ). " Top
    cl_wd_flow_layout=>new_flow_layout( container = l_o_t_cont ).
    l_str_kunnr = <l_fs_centres>-kunnr.
    l_o_ft_view = cl_wd_formatted_text_view=>new_formatted_text_view( text = l_str_kunnr ).
    cl_wd_flow_data=>new_flow_data( element = l_o_ft_view ).
    l_o_t_cont->add_child( the_child = l_o_ft_view ).
    l_o_t_cont_top->add_child( the_child = l_o_t_cont ).
* Address lines
    l_o_t_cont = cl_wd_transparent_container=>new_transparent_container( ).
    cl_wd_grid_data=>new_grid_data( element = l_o_t_cont
                                    v_align = '00' ). " Baseline
    cl_wd_grid_layout=>new_grid_layout( container = l_o_t_cont ).
    CLEAR l_rc.
    WHILE l_rc IS INITIAL.
      ASSIGN COMPONENT sy-index OF STRUCTURE <l_fs_centres>-address TO <l_fs_line>.
      l_rc = sy-subrc.
      IF l_rc IS NOT INITIAL.
        EXIT.
      ENDIF.
      CHECK <l_fs_line> IS NOT INITIAL.
      l_str_line = <l_fs_line>.
      l_o_ft_view = cl_wd_formatted_text_view=>new_formatted_text_view( text = l_str_line ).
      cl_wd_grid_data=>new_grid_data( element = l_o_ft_view ).
      l_o_t_cont->add_child( the_child = l_o_ft_view ).
    ENDWHILE.
    l_o_t_cont_top->add_child( the_child = l_o_t_cont ).
    l_o_group->add_child( the_child = l_o_t_cont_top ).
  ENDLOOP.
* If nothing is found display message
  IF sy-subrc IS NOT INITIAL.
    l_o_ft_view = cl_wd_formatted_text_view=>new_formatted_text_view( text = 'No results' ).
    cl_wd_grid_data=>new_grid_data( element = l_o_ft_view ).
    l_o_group->add_child( the_child = l_o_ft_view ).
  ENDIF.
  l_o_container->add_child( the_child = l_o_group ).
ENDMETHOD.
Once all this is done just create an application that links to the view and component and test it out!

Wednesday, April 22, 2009

Check delivery status

To check the delivery status of an order the following code can be used:

*Get all the schedule details by doing
SELECT * INTO TABLE xvbap FROM vbap
  WHERE vbeln = '7500000028'
  AND posnr = '000010'.
SELECT * INTO TABLE xvbup FROM vbup
  WHERE vbeln = '7500000028'
  AND posnr = '000010'.
SELECT * INTO TABLE xvbfa FROM vbfa
  WHERE vbelv = '7500000028'
  AND posnv = '000010'
  AND vbtyp_n = 'J'.
SELECT * INTO TABLE da_vbep FROM vbep
  WHERE vbeln = '7500000028'
  AND posnr = '000010'.
CALL FUNCTION 'RV_SCHEDULE_CHECK_DELIVERIES'
  EXPORTING
    fbeleg = '7500000028'
    fposnr = '000010'
* FVERRECHNUNG = ' '
* FS073_ALT = ' '
* IF_NO_SORT = ' '
  TABLES
    fvbfa = xvbfa
    fvbup = xvbup
    fxvbep = da_vbep
* FVBLB =
    fvbap = xvbap
  EXCEPTIONS
    fehler_bei_lesen_fvbup = 1
    fehler_bei_lesen_fxvbep = 2
  OTHERS = 3.

Tuesday, April 14, 2009

Read the domain value ranges

Sometimes we need to display the description based on the key value of a field. And the description is not in a text table but defined as value range in the domain definition. For example the domain XUDATFM lists all the types of dates possible

image

Using the class CL_ABAP_ELEMDESCR, one can retrieve these values in an internal table of type DDFIXVALUES. Here is an example:

DATA o_rtti_attr TYPE REF TO cl_abap_elemdescr.
DATA t_fixed_values TYPE ddfixvalues.
TRY.
    rtti_attr ?= cl_abap_typedescr=>describe_by_name( 'XUDATFM' ).
  CATCH cx_root.                                            "#EC *
ENDTRY.
*       get the fixed values
CALL METHOD rtti_attr->get_ddic_fixed_values
  RECEIVING
    p_fixed_values = fixed_values
  EXCEPTIONS
    no_ddic_type   = 1
    OTHERS         = 2.

Thursday, April 09, 2009

Dynamic where conditions in select

I had a problem in work where I would be selecting from tables that are decided on run time and also the where conditions on these tables are to be decided on run time. I found a lot of examples where one could create a dynamic internal table using the RTTS for INTO clause of SELECT. This was only half the solution to my problem. i still have to dynamically create as many where conditions as possible. The syntax of a dynamic select is:

SELECT (columns) INTO CORRESPONDING FIELDS OF TABLE (Internal Table Name)
  FROM (Table Name)
  WHERE (Where String containing conditions)
We have four components in the above SQL query that can be given at run time:
  1. Columns. We don’t worry about this as the clause ‘INTO CORRESPONDING FIELDS’ will only pick the fields that are common in the internal table and the database table.
  2. Internal Table: This is achieved by following the example here.
  3. Table name: The calling service knows what table to select from.
  4. Where string containing conditions: This is the problem that is to be solved!
In SELECT conditions there is a concept of ranges. You can create an internal table of type range that will contain the value to be searched and the operators for searching. I will use this for the solution here.

What I intend to do is let the calling program send me any number of ranges and its respective column name. So there will be an internal table (RANGES_TABLE) of the following structure:
BEGIN OF ranges_tables,
  tablename TYPE tabname16,
  fieldname TYPE fieldname,
  range TYPE REF TO data,
END OF ranges_table.
Taking the example of table T001:

image

Let’s populate RANGES_TABLE with two criteria, ORT01 and LAND1. We want to get table entries where ORT01 = ‘Walldorf’ and LAND1 = ‘DE’.
* 1
* Create range for ORT01
s_ort01-sign = 'I'.
s_ort01-option = 'EQ'.
s_ort01-low = 'Walldorf'.
APPEND s_ort01 TO r_ort01.
* Add entry in the Ranges table
s_ranges_table-tablename = 'T001'.
s_ranges_table-fieldname = 'ORT01'.
CREATE DATA s_ranges_table-range LIKE r_ort01.
ASSIGN s_ranges_table-range->* TO <range>.
<range> = r_ort01.
APPEND s_ranges_table TO t_range_table.
* 2
* Create range for LAND1
s_land1-sign = 'I'.
s_land1-option = 'EQ'.
s_land1-low = 'DE'.
APPEND s_land1 TO r_land1.
* Add entry in the Ranges table
s_ranges_table-tablename = 'T001'.
s_ranges_table-fieldname = 'LAND1'.
CREATE DATA s_ranges_table-range LIKE r_land1.
ASSIGN s_ranges_table-range->* TO <range>.
<range> = r_land1.
APPEND s_ranges_table TO t_range_table.
RANGES_TABLE now has a list of columns that we need to select from with the conditions respectively. But the user can send one column or two columns or (n) columns. This is will be solved by dynamically creating a structure that will have fields referring to different ranges. The structure will look like this:
BEGIN OF select_struc
  field1 TYPE range1
  .
  .
  fieldn TYPE rangen
END OF select_struc
Having creating a structure we create dynamic where conditions. So now the select query will be as follows:
SELECT * INTO TABLE <l_rows>
  FROM (s_ranges_table-tablename)
  WHERE field1 IN select_struc-field1
  .
  .
  AND   fieldn IN select_struc-fieldn
Here is the full list of the method that does all this:

image

METHOD select_from_ranges.
  DATA:
  l_index TYPE i,
  l_t_dyn_field TYPE abap_component_tab,
  l_s_dyn_field LIKE LINE OF l_t_dyn_field,
  l_str_range_name TYPE string,
  l_str_sel_option TYPE string,
  l_o_structdescr TYPE REF TO cl_abap_structdescr,
  l_d_range_str TYPE REF TO data,
  l_s_table_range LIKE LINE OF i_t_range.
  FIELD-SYMBOLS:
    <l_rows> TYPE ANY TABLE,
    <l_dyn_range_str> TYPE ANY,
    <l_range_tbl> TYPE table,
    <l_range_tbl2> TYPE table.
* For all the where conditions sent
* create fields in the select strucuture
  LOOP AT i_t_range INTO l_s_table_range.
    _index = sy-tabix.
    ASSIGN l_s_table_range-range->* TO <l_range_tbl>.
* Create dynamic field
    CLEAR:
      l_s_dyn_field.
l_s_dyn_field-name = l_s_table_range-fieldname.
    l_s_dyn_field-type ?= cl_abap_datadescr=>describe_by_data( <l_range_tbl> ).
    APPEND l_s_dyn_field TO l_t_dyn_field.
    CONCATENATE '<l_dyn_range_str>-' l_s_table_range-fieldname INTO l_str_range_name.
* More than one where requires in AND
    IF l_index > 1.
      CONCATENATE l_str_sel_option 'AND'
       INTO l_str_sel_option SEPARATED BY space.
    ENDIF.
    CONCATENATE l_str_sel_option l_s_table_range-fieldname 'in' l_str_range_name
      INTO l_str_sel_option SEPARATED BY space.
  ENDLOOP.
* Create dynamic structure with fields pointing to different range
  l_o_structdescr = cl_abap_structdescr=>create( p_components = l_t_dyn_field ).
  CREATE DATA l_d_range_str TYPE HANDLE l_o_structdescr.
  ASSIGN l_d_range_str->* TO <l_dyn_range_str>.
* For all the fields in the structure point them to the range tables
  WHILE sy-subrc IS INITIAL.
    ASSIGN COMPONENT sy-index OF STRUCTURE <l_dyn_range_str> TO <l_range_tbl2>.
    IF sy-subrc IS NOT INITIAL.
      EXIT.
    ENDIF.
    READ TABLE i_t_range INTO l_s_table_range INDEX sy-index.
    ASSIGN l_s_table_range-range->* TO <l_range_tbl>.
    <l_range_tbl2> = <l_range_tbl>.
  ENDWHILE.
  ASSIGN c_rows->* TO <l_rows>.
* Select from the table
  SELECT * INTO TABLE <l_rows>
    FROM (l_s_table_range-tablename)
    WHERE (l_str_sel_option).
ENDMETHOD.