Showing posts with label Dynamic programing. Show all posts
Showing posts with label Dynamic programing. Show all posts

Saturday, October 27, 2018

Update any table on remote system? YES! Remote updater via remote enabled function module

If you are looking for a way how to control database table synchronization process between two systems in a custom way, check out below solution aimed for transferring of transparent tables.

Of course we need two parts, some sender part and receiver part. The base is to do a XML transformation into binary Xstring which is suitable for transferring of any data, even whole table. I did lot of tests, it really nicely flexible.


The sender part


I am not providing the full source of  sender part. The corner stone of sender program would be the below XML transformation. Keep in mind to name source on both sides in the same way. Here is the source name "remote_data",it could be anything you want, but it has to be the same on both sides sender and receiver part of XML transformation (see the receiver FM as well).

It is possible to pack into Xstring whole tables, part of the tables based on certain conditions, anything that fits your needs.



Part of the sender code:

" Declaration snippet
    METHODS:
      encode_input
        IMPORTING it_data           TYPE ANY TABLE
        EXPORTING ev_xstring        TYPE xstring,


" Implementation
  METHOD encode_input.

    DATA: lo_writer TYPE REF TO cl_sxml_string_writer.

    lo_writer = cl_sxml_string_writer=>create( if_sxml=>co_xt_json ).

    TRY.
        CALL TRANSFORMATION id
        SOURCE remote_data = it_data
        RESULT XML lo_writer.
      CATCH cx_xslt_format_error.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDTRY.

    ev_xstring = lo_writer->get_output( ).

  ENDMETHOD.                    "encode_input




The receiver part


Let me describe a bit the receiver part. It consists of remote enabled function module on remote system. There has to be established RFC connection between servers, ask your BC team. As input serves few parameters:
     
  • Table name - in a character way
  • Table content as a binary string
  • Update type with below options

Possible update types:

  1. I – insert only, it adds just new records 
  2. N – overwrite older records by newer ones based on standard date colums ERSDA or LAEDA. It skips update of tables where there is no LAEDA, ERSDA column present. This relays on DATE column only, other DATE/DATS columns are not considered.
  3. F – force update overwrite all records

Furthermore there is an exporting parameter receiving a possible error code or success confirmation. By the sent error code you can act accordingly in your sender part.





The source code of receiver:


FUNCTION /yournamespace/customizing_update.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(IV_TABNAME) TYPE  TABNAME
*"     VALUE(IV_UPDATE_TYPE) TYPE  C1
*"     VALUE(IV_XSTRING_TAB) TYPE  XSTRING
*"  EXPORTING
*"     VALUE(EV_STATUS) TYPE  CHAR4
*"----------------------------------------------------------------------

  IF ( iv_tabname IS INITIAL )
    OR
     ( iv_xstring_tab IS INITIAL ).
    ev_status = 'ERR0'.
    RETURN.
  ENDIF.


 "Note: Do here your own AUTHORITY-CHECK stuff!

  TYPES : BEGIN OF ty_where_clause,
            line TYPE char72,
          END OF ty_where_clause.

  DATA:
    lo_typedescr     TYPE REF TO cl_abap_typedescr,
    lo_struct        TYPE REF TO cl_abap_structdescr,
    lo_new_tab       TYPE REF TO cl_abap_tabledescr,
    lt_comp          TYPE cl_abap_structdescr=>component_table,
    lt_desc_fields   TYPE ddfields,
    lt_data_remote   TYPE REF TO data,
    ls_data_remote   TYPE REF TO data,
    ls_data_local    TYPE REF TO data,
    lv_count_ok      TYPE i,
    lv_count_err     TYPE i,
    lt_where_cond    LIKE STANDARD TABLE OF hrcond,
    ls_where_cond    LIKE LINE OF lt_where_cond,
    lt_where_clause  TYPE STANDARD TABLE OF ty_where_clause,
    lt_date_col      TYPE STANDARD TABLE OF cl_abap_structdescr=>component-name,
    lv_date_col      LIKE LINE OF lt_date_col,
    lv_date_col_comp LIKE LINE OF lt_date_col.

  FIELD-SYMBOLS:
    <ft_data_remote> TYPE ANY TABLE,
    <fs_data_remote> TYPE data,
    <fs_data_local>  TYPE data,
    <fv_value>       TYPE any,
    <fv_date_remote> TYPE dats,
    <fv_date_local>  TYPE dats,
    <fs_desc_fields> LIKE LINE OF lt_desc_fields,
    <fv_col>         LIKE LINE OF lt_comp,
    <fv_col_date>    LIKE LINE OF lt_date_col.


  " Fill known date columns to be checked in received data (it is used in update type "N" - newer records)
  lv_date_col = 'LAEDA'. " Date of last modification (in loop checked as the first one)
  APPEND lv_date_col TO lt_date_col.
  lv_date_col = 'ERSDA'. " Date of creation
  APPEND lv_date_col TO lt_date_col.


  " Create target itab type
  cl_abap_typedescr=>describe_by_name( " Note: cx_root does not catch type_not_found here
    EXPORTING  p_name         = iv_tabname
    RECEIVING  p_descr_ref    = lo_typedescr
    EXCEPTIONS type_not_found = 1
  ).
  IF sy-subrc NE 0.
    ev_status = 'ERR1'.
    "MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. --> causes dump within RFC call
    WRITE 'Error, unknown target structure/table type in data dictionary: '.
    WRITE iv_tabname.
    RETURN.
  ENDIF.

  IF lo_typedescr IS NOT INITIAL.

    " Get structure components
    lo_struct ?= lo_typedescr.
    lt_comp = lo_struct->get_components( ).

    " Is date column of last modification/creation present?
    IF ( iv_update_type EQ 'N' ).
      LOOP AT lt_date_col ASSIGNING <fv_col_date>. " Loop over known date columns (suitable for comparison), items order does matter
        LOOP AT lt_comp ASSIGNING <fv_col>. " Loop over table components
          IF <fv_col_date> EQ <fv_col>-name.
            lv_date_col_comp = <fv_col_date>.
            EXIT.
          ENDIF.
        ENDLOOP.

        IF lv_date_col_comp IS NOT INITIAL.
          EXIT.
        ENDIF.
      ENDLOOP.
    ENDIF.

    " Create new target itab
    lo_new_tab = cl_abap_tabledescr=>create(
      p_line_type  = lo_struct
      p_table_kind = cl_abap_tabledescr=>tablekind_std
      p_unique     = abap_false
    ).

    IF lo_new_tab IS NOT INITIAL.
      CREATE DATA lt_data_remote TYPE HANDLE lo_new_tab.
    ELSE.
      ev_status = 'ERR2'.
      RETURN.
    ENDIF.
  ELSE.
    ev_status = 'ERR3'.
    RETURN.
  ENDIF.

  " Decode data into itab
  ASSIGN lt_data_remote->* TO <ft_data_remote>.

  TRY.
      CALL TRANSFORMATION id
      SOURCE XML iv_xstring_tab
      RESULT remote_data = <ft_data_remote>.
    CATCH cx_xslt_format_error.
      ev_status = 'ERR4'.
      "MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. --> Note: causes dump on both sides, calling program and this target FM
      WRITE 'Error occured during itab (xstring) decoding for table: '.
      WRITE iv_tabname.
      RETURN.
  ENDTRY.

  " Loop over itab for DB update
  IF <ft_data_remote> IS ASSIGNED.

    CREATE DATA ls_data_remote LIKE LINE OF <ft_data_remote>.
    ASSIGN ls_data_remote->* TO <fs_data_remote>.

    CREATE DATA ls_data_local LIKE LINE OF <ft_data_remote>.
    ASSIGN ls_data_local->* TO <fs_data_local>.

    " Get key fields of the table (keyflag value)
    lt_desc_fields = lo_struct->get_ddic_field_list( ).

    " Update table according to given type
    LOOP AT <ft_data_remote> ASSIGNING <fs_data_remote>.

      IF ( iv_update_type EQ 'I' )  " Increment only
         OR
         ( iv_update_type EQ 'N' ). " Update only older

        "*******************************************************************************
        "  Do partial update
        "
        "  I - Increment only = add missing records only
        "    or
        "  N - Update only older recocords by newer ones based on LAEDA, ERSDA columns
        "*******************************************************************************

        " Prepare SQL WHERE condition
        LOOP AT lt_desc_fields ASSIGNING <fs_desc_fields> WHERE keyflag = abap_true. " get primary key fields

          " Get value of key component
          ASSIGN COMPONENT  <fs_desc_fields>-fieldname OF STRUCTURE <fs_data_remote> TO <fv_value>.

          " Prepare condition table
          ls_where_cond-field = <fs_desc_fields>-fieldname.
          ls_where_cond-opera = 'EQ'.
          ls_where_cond-low   = <fv_value>.
          APPEND ls_where_cond TO lt_where_cond.

          UNASSIGN <fv_value>.
          CLEAR ls_where_cond.

        ENDLOOP.

        IF lt_where_cond IS NOT INITIAL.

          " Convert condition table into SQL WHERE clause
          CALL FUNCTION 'RH_DYNAMIC_WHERE_BUILD'
            EXPORTING
              dbtable         = iv_tabname
            TABLES
              condtab         = lt_where_cond
              where_clause    = lt_where_clause
            EXCEPTIONS
              empty_condtab   = 1
              no_db_field     = 2
              unknown_db      = 3
              wrong_condition = 4
              OTHERS          = 5.
          IF sy-subrc <> 0.
            ev_status = 'ERR7'.
            WRITE 'Some error occured during building SQL WHERE condition for table: '.
            WRITE iv_tabname.
          ENDIF.

          SELECT SINGLE *
            FROM  (iv_tabname)
            INTO  <fs_data_local>
            WHERE (lt_where_clause).

          IF sy-subrc EQ 0.

            " Update only older recocords by newer ones based on LAEDA, ERSDA columns
            IF ( iv_update_type EQ 'N' ).
              IF <fs_data_local> IS ASSIGNED. " Then compare date, if it is present
                IF lv_date_col_comp IS NOT INITIAL.
                  " Compare remote and local dates

                  " Get values of date component
                  ASSIGN COMPONENT lv_date_col_comp OF STRUCTURE <fs_data_local>  TO <fv_date_local>.
                  ASSIGN COMPONENT lv_date_col_comp OF STRUCTURE <fs_data_remote> TO <fv_date_remote>.

                  IF <fv_date_local> < <fv_date_remote>.

                    "*******************************************************************************
                    " Overwrite record with newer one
                    "*******************************************************************************
                    UPDATE (iv_tabname) FROM <fs_data_remote>.
                    IF sy-subrc EQ 0.
                      lv_count_ok = lv_count_ok + 1.
                      COMMIT WORK.
                    ELSE.
                      lv_count_err = lv_count_err + 1.
                      ROLLBACK WORK.
                    ENDIF.

                  ENDIF.

                  UNASSIGN <fv_date_local>.
                  UNASSIGN <fv_date_remote>.

                ENDIF.
              ENDIF.
            ENDIF.

          ELSE.

            "*******************************************************************************
            " Record does not exist = add it (It fits to both types of update I + N)
            "*******************************************************************************
            INSERT (iv_tabname) FROM <fs_data_remote>.
            IF sy-subrc EQ 0.
              lv_count_ok = lv_count_ok + 1.
              COMMIT WORK.
            ELSE.
              lv_count_err = lv_count_err + 1.
              ROLLBACK WORK.
            ENDIF.

          ENDIF.

          CLEAR lt_where_cond.
          CLEAR lt_where_clause.

        ENDIF.

      ELSEIF iv_update_type EQ 'F'.

        "*******************************************************************************
        "  Do force update = overwrite all already existing records
        "  Inserts + Updates
        "*******************************************************************************
        MODIFY (iv_tabname) FROM <fs_data_remote>.
        IF sy-subrc EQ 0.
          lv_count_ok = lv_count_ok + 1.
          COMMIT WORK.
        ELSE.
          lv_count_err = lv_count_err + 1.
          ROLLBACK WORK.
        ENDIF.

      ENDIF.

    ENDLOOP.

    IF lv_count_err EQ 0.
      ev_status = 'OK'.
    ELSE.
      ev_status = 'ERR6'.
      WRITE 'Some error occured during update of table: '.
      WRITE iv_tabname.
      WRITE ', Number of failed records: '.
      WRITE lv_count_err.
      WRITE ', Number of well passed records: '.
      WRITE lv_count_ok.
      RETURN.
    ENDIF.

  ELSE.
    ev_status = 'ERR5'.
    WRITE 'No data for table: '.
    WRITE iv_tabname.
    RETURN.
  ENDIF.

ENDFUNCTION.



The sources



  • https://stackoverflow.com/questions/30597747/cant-catch-certain-exceptions
  • http://zevolving.com/2008/09/dynamic-internal-table-creation/
  • https://blogs.sap.com/2013/04/16/writing-dynamic-where-clause-in-abap-select-query/



  • Saturday, June 9, 2018

    Update all dynpro/screen fields in one shot

    Lazyness is one of the engines for iprovements. So this is the root cause of this short article.

    Once you are trying to update a classic Dynpro you should call FM DYNP_VALUES_UPDATE. Ok, you can collect ITAB for each screen field providing name and value as on bellow example.

          ls_dynpfield-fieldname  = lv_screen_field_name.
          ls_dynpfield-fieldvalue = lv_screen_field_val.
          APPEND ls_dynpfield TO lt_dynpfield.

    It is still annoying to do so for each screen field...

    Imagine, you can set the screen values as you need and run just once some certain method doing the whole screen update job. Here it is, there is just one simple call. You can reuse it as many times you want. The only condition is to have screen connected to a certain structure variable. That's all.


    Screen update method call

        " Update screen
         lo_ctrl_screen->update_screen_fields(
          iv_prog       = sy-cprog                                                      " Program name
          iv_screen     = '0200'                       " Dynpro number
          iv_struc_name = 'GS_SCREEN_STRUCTURE_NAME' " Name of structure holding the screen values
          is_struc_data = gs_real_structure                            " Structure variable itself
        ).




    The method source code: 


      METHOD update_screen_fields.

        DATA:
          lt_dynpfield         TYPE STANDARD TABLE OF dynpread,
          ls_dynpfield         LIKE LINE OF lt_dynpfield,
          lo_struc_descr       TYPE REF TO cl_abap_structdescr.

        lo_struc_descr ?= cl_abap_structdescr=>describe_by_data( is_struc_data ).
        CHECK sy-subrc = 0.

        FIELD-SYMBOLS:
          <fs_comp> LIKE LINE OF lo_struc_descr->components,
          <fs_val>  TYPE any.

        LOOP AT lo_struc_descr->components ASSIGNING <fs_comp>.
          ASSIGN COMPONENT <fs_comp>-name OF STRUCTURE is_struc_data TO <fs_val>.

          IF <fs_val> IS NOT INITIAL.
            CLEAR ls_dynpfield.
            CONCATENATE iv_struc_name <fs_comp>-name INTO ls_dynpfield-fieldname.
            ls_dynpfield-fieldvalue = <fs_val>.
            APPEND ls_dynpfield TO lt_dynpfield.
          ENDIF.

        ENDLOOP.

        " Update screen
        IF lt_dynpfield IS NOT INITIAL.
          CALL FUNCTION 'DYNP_VALUES_UPDATE'
            EXPORTING
              dyname     = iv_prog    "Program name
              dynumb     = iv_screen  "Screen number
            TABLES
              dynpfields = lt_dynpfield
            EXCEPTIONS
              OTHERS     = 0.
        ENDIF.

      ENDMETHOD.                    "update_screen_fields


    Sunday, June 4, 2017

    Convert big PARID/PARVA table into big flat structure dynamically

    Imagine, you can have a huge table with various keys PARID/PARVA similar to USR05 but defined for plant specific parameters (see below table structure).  There is an application working with BIG STRUCTURE receiving data from this database table. But how to do the conversion from table PAR(ID)/PAR(VAlue) into a structure? If you would do that manually, it would be huge list of IF ITAB-PARID = 'SOME_STRUCTURE_FIELD1' ELSE IF ITAB-PARID = 'SOME_STRUCTURE_FIELD2'  etc. - long list of checks made within a loop over ITAB. You would need to write down all the structure components one by one. Yes, it is nicely easily readable , but its ugly programming at all.

    Let's look at the dynamic approach, it saves lots of manual coding here. The below code snippet is, I hope, well commented to grasp the idea.


    Source database table structure:





    Target filled flat structure:






    *&---------------------------------------------------------------------*

    REPORT z_dynamic_prg.

    * Some huge flat target stucture receiving PARID/PARVA data from ITAB
    TYPES:
      BEGIN OF tls_plant_specifics,
        delete_aufnr     TYPE abap_bool,
        use_screen_202   TYPE abap_bool,
        global_selection TYPE abap_bool,
        run_co04         TYPE abap_bool,
        not_use_opr_num  TYPE abap_bool,
        export_9_31      TYPE string,
        export_9_4       TYPE string,
        export_9_5       TYPE string,
        export_special   TYPE string,
        screen_907_clear TYPE string,
        not_use_fstau    TYPE abap_bool,
        blank_staging    TYPE abap_bool,
        filter_version   TYPE c LENGTH 1,
        export_plan_in   TYPE abap_bool,
        staging_blank    TYPE abap_bool,
        remove_routines  TYPE abap_bool,
        panning_rmw_e3   TYPE abap_bool,
        schedule_old     TYPE abap_bool,
        free_time        TYPE c LENGTH 1,
        set_subrc        TYPE abap_bool,
        zroh_1           TYPE string,
        zroh_2           TYPE abap_bool,
        fevor_set_bu     TYPE abap_bool,
        component_active TYPE abap_bool,
        sel_reservations TYPE string,
        mode_3_lgort     TYPE lgort_d,
        change_profil    TYPE abap_bool,
        set_user_attrib  TYPE abap_bool,
        import_check_lr  TYPE abap_bool,
        read_marc_as_1   TYPE werks_d,
        read_marc_as_2   TYPE werks_d,
        read_marc_as_3   TYPE werks_d,
        default_lgort    TYPE lgort_d,
        select_days      TYPE abap_bool,
        use_zmet_zroh    TYPE abap_bool,
        set_fevor_icon   TYPE string,
        read_marc_as_4   TYPE werks_d,
        set_fstad_fstau  TYPE abap_bool,
        set_okcode       TYPE string,
        set_affree       TYPE abap_bool,
        set_time         TYPE abap_bool,
        userexit_lgnum   TYPE lgnum,
        get_mto_mts_by   TYPE string,
      END OF tls_plant_specifics,

      BEGIN OF tls_plant_specifics_parva_parid,
        parid       TYPE memoryid,
        parva       TYPE xuvalue,
      END OF tls_plant_specifics_parva_parid.


    DATA: lt_plant_specifics_parva_parid TYPE STANDARD TABLE OF tls_plant_specifics_parva_parid,
          ls_plant_specifics_parva_parid LIKE LINE OF lt_plant_specifics_parva_parid,
          lo_structdescr     TYPE REF TO cl_abap_structdescr,
          lt_components      TYPE cl_abap_structdescr=>component_table,
          ls_component       LIKE LINE OF lt_components,
          lv_element_name    TYPE string,
          lv_element_content TYPE string.

    DATA: lv_werks       TYPE werks_d VALUE '9999', " some plant - hardcoded for test
          lv_plant_table TYPE tabname VALUE 'SOME_YOUR_PARAM_TABLE_NAME', 
          ls_plant_specifics TYPE tls_plant_specifics. " target structure

    FIELD-SYMBOLS <fs_element> TYPE simple.

    " Get all plant parameters into itab
    SELECT parid parva FROM (lv_plant_table)
    INTO TABLE lt_plant_specifics_parva_parid
    WHERE werks = lv_werks.


    IF ( sy-subrc EQ 0 ) AND ( lt_plant_specifics_parva_parid IS NOT INITIAL ).

      " Get the values out of table (parid/parva) into class public structure
      lo_structdescr ?= cl_abap_structdescr=>describe_by_data( ls_plant_specifics ).
      lt_components   = lo_structdescr->get_components( ).

      " Loop at parid x parva itab (comming from database)
      LOOP AT lt_plant_specifics_parva_parid INTO ls_plant_specifics_parva_parid.
     
        " Loop over components of target structure
        LOOP AT lt_components INTO ls_component.

          " Read component name out of table
          lv_element_name = ls_plant_specifics_parva_parid-parid.
       
          " If PARAM name is equal to STRUCTURE component name => assign the value to the structure for this field
          IF ls_component-name EQ lv_element_name. 

            CONCATENATE 'ls_plant_specifics-' lv_element_name INTO lv_element_name.
            ASSIGN (lv_element_name) TO <fs_element>.

            IF ( <fs_element> IS ASSIGNED ) AND ( ls_plant_specifics_parva_parid-parva IS NOT INITIAL ).
           
              " You can assign data based on its type...   
              IF ls_component-type->absolute_name CS 'ABAP_BOOL'.
                " just one char
                <fs_element> = ls_plant_specifics_parva_parid-parva+0(1).
              ELSE.
                " strings, etc.
                lv_element_content = ls_plant_specifics_parva_parid-parva.
                CONDENSE lv_element_content NO-GAPS.
                <fs_element> = lv_element_content.
              ENDIF.

            ENDIF.
          ENDIF.

        ENDLOOP.
      ENDLOOP.

      FREE lo_structdescr.

    ENDIF.

    Sunday, May 21, 2017

    Create Open SQL query dynamically out of custom table

    Open SQL offers really flexible way of use. In below example you can see dynamically created query, there is not clear from the beginning what we are looking for, from which table and under what WHERE condition. The example shows how to get data for a certain user based on another custom table storing target table name and expected field name for username. That's all. The rest is a dynamic miracle :)

    Fully flexible query:


        SELECT SINGLE (<fs_user_table>-fieldname) FROM (<fs_user_table>-tabname)
        INTO gv_text
        WHERE (gt_sql_where).


    Note: Even target INTO variable can be created dynamically - see another post.


    Source table for Query

    Structure of custom table Z_USER_TABS:




    Possible table content (user related tables):



    ABAP coding:


    REPORT z_osql_dynamics.

    PARAMETER p_user TYPE xubname.

    " Get/List user param table list
    TYPES: BEGIN OF tys_user_tab,
            tabname   TYPE tabname,
            fieldname TYPE fieldname,
           END OF tys_user_tab.

    TYPES tyv_user_tab TYPE c LENGTH 72.

    DATA: gt_user_tables TYPE STANDARD TABLE OF tys_user_tab,
          gt_sql_where   TYPE STANDARD TABLE OF tyv_user_tab,
          gv_sql_where   LIKE LINE OF gt_sql_where,
          gv_text        TYPE string.


    START-OF-SELECTION.

      " Get list of all user param tables to be checked
      SELECT tabname fieldname
      FROM z_user_tabs
      INTO TABLE gt_user_tables.

      FIELD-SYMBOLS <fs_user_table> LIKE LINE OF gt_user_tables.
      LOOP AT gt_user_tables ASSIGNING <fs_user_table>.

        CLEAR gv_sql_where.
        CLEAR gt_sql_where.

        CONCATENATE <fs_user_table>-fieldname ' = ' '`' p_user '`' INTO gv_sql_where RESPECTING BLANKS.
        APPEND gv_sql_where TO gt_sql_where.

        SELECT SINGLE (<fs_user_table>-fieldname) FROM (<fs_user_table>-tabname)
        INTO gv_text
        WHERE (gt_sql_where).
        IF sy-subrc = 0.
          WRITE: / <fs_user_table>-tabname, <fs_user_table>-fieldname, ' = ', gv_text.
        ENDIF.
      ENDLOOP.

    END-OF-SELECTION.


    Sunday, May 7, 2017

    Display table content in a popup window via SALV

    If you have a list of various db tables with different structure (selected by specific field name) you may want add a preview of each ones content via popup window. But field name could be a variable of unknown type. Below coding shows a way how to reach this functionality, it uses dynamic data creation, dynamic Open SQL and SALV class.

    Note: Sorry for the ugly subroutine surrounding ;-) I placed this functionality into an old program, where is the subroutine triggered by event  AT LINE-SELECTION working  together with variables provided by HIDE command. The popup window nicely works with selection screen.



    TYPES tyv_sql_line TYPE c LENGTH 72.

    *&---------------------------------------------------------------------*
    *&      Form  show_table_content
    *&---------------------------------------------------------------------*
    *       Show table content in a popup window by specific field
    *----------------------------------------------------------------------*
    FORM show_table_content USING p_tablename p_fieldname p_fieldvalue.

      DATA: lt_sql_where   TYPE STANDARD TABLE OF tyv_sql_line,
                  lv_sql_where   LIKE LINE OF lt_sql_where,
                  rv_dref        TYPE REF TO data,
                  rcl_alv        TYPE REF TO cl_salv_table,
                  lx_msg         TYPE REF TO cx_salv_msg.

      FIELD-SYMBOLS: <itab> TYPE ANY TABLE.

      CLEAR lv_sql_where.
      CLEAR lt_sql_where.

      CONCATENATE p_fieldname ' = ' '`' p_fieldvalue '`' INTO lv_sql_where RESPECTING BLANKS.
      APPEND lv_sql_where TO lt_sql_where.

      CREATE DATA rv_dref TYPE TABLE OF (p_tablename).
      ASSIGN rv_dref->* TO <itab>.

      SELECT * FROM (p_tablename)
      INTO TABLE <itab>
      WHERE (lt_sql_where).

      IF <itab> IS ASSIGNED.

        TRY.
            cl_salv_table=>factory(
            IMPORTING
              r_salv_table = rcl_alv
            CHANGING
              t_table      = <itab> ).
          CATCH cx_salv_msg INTO lx_msg.
        ENDTRY.

        rcl_alv->set_screen_popup(
          start_column = 25
          end_column   = 150
          start_line   = 15
          end_line     = 22
        ).

        rcl_alv->display( ).

      ENDIF.

    ENDFORM.                    "show_table_content



    The popup looks like e.g.



    Valuable source for ALV popup