Friday, March 06, 2009

Two ALV list in one with download option

The requirement was to create two fixed width files to be sent to a third party. The selection of data is pretty straight forward. I wanted to display the data sent in an ALV and wanted to do all this in one report.

For each output format and file structure I created one dictionary structure. For the fields I didn’t bother with data elements as the file output was to be of different lengths then the existing domains lengths. So I just went for the predefined ones.

Structure 1 = File 1

Structure 2 = File 2

The logic I was going to follow was:

  1. Select Data
  2. Download data
  3. Create Split Container
  4. Display ALV in each container

For doing all this I created a local class as I want to as much coding as possible in OO. The definition of class looks as like this:

CLASS lcl_report DEFINITION.
  PUBLIC SECTION.
    METHODS:
      extract,
      build_file_names,
      download,
      display.
    CLASS-METHODS:
      get_directory RETURNING value(r_dir) TYPE localfile.
  PRIVATE SECTION.
    CONSTANTS:
      c_container(15) VALUE 'C_CONTROL'.
    METHODS:
      create_split_control CHANGING ch_split_container TYPE REF TO cl_gui_easy_splitter_container,
      display_alv IMPORTING im_o_container TYPE REF TO cl_gui_container
                            im_heading     TYPE string
                  CHANGING  ch_t_out_tab   TYPE table,
      set_column_text IMPORTING im_alv       TYPE REF TO cl_salv_table
                                im_t_out_tab TYPE table.
    DATA:
      t_candidate TYPE TABLE OF zst_cand_out,
      t_centre TYPE TABLE OF zst_centre_out,
      centre_file TYPE string,
      cand_file TYPE string.
ENDCLASS.                    "lcl_report DEFINITION


In the selection option I provided a parameter to enter the directory to which the files will be downloaded. The F4 to display the directories on the local PC was implemented by the following code.



F4 event for the parameter



AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_dir.
  p_dir = lcl_report=>get_directory( ).

 



Static method that does the directory display dialog



  METHOD get_directory.
    DATA:
        l_t_file_table TYPE TABLE OF file_table,
        l_s_file_table TYPE file_table,
        l_subrc TYPE i,
        l_directory TYPE string.
*    CALL METHOD cl_gui_frontend_services=>file_open_dialog
*      CHANGING
*        file_table              = l_t_file_table
*        rc                      = l_subrc
*      EXCEPTIONS
*        file_open_dialog_failed = 1
*        cntl_error              = 2
*        error_no_gui            = 3
*        not_supported_by_gui    = 4
*        OTHERS                  = 5.
*    IF sy-subrc <> 0.
*      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
*                 WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
*    ENDIF.
    CALL METHOD cl_gui_frontend_services=>directory_browse
      CHANGING
        selected_folder      = l_directory
      EXCEPTIONS
        cntl_error           = 1
        error_no_gui         = 2
        not_supported_by_gui = 3
        OTHERS               = 4.
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                 WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
*    READ TABLE l_t_file_table INTO l_s_file_table INDEX 1.
*    r_dir = l_s_file_table-filename.
    r_dir = l_directory.
  ENDMETHOD.                    "get_directory


So the report once executed will do the extract, download and display



START-OF-SELECTION.
  CREATE OBJECT o_report.
  o_report->build_file_names( ).
  o_report->extract( ).
END-OF-SELECTION.
  o_report->download( ).
  CALL SCREEN 0100.


The ALV is display in the screen 0100. Call to the method display( ) will display the output in PBO



MODULE status_0100 OUTPUT.
  SET PF-STATUS 'STATUS'.
  SET TITLEBAR 'TITLE'.
  o_report->display( ).
  CALL METHOD cl_gui_cfw=>flush.
ENDMODULE.                 " status_0100  OUTPUT


Within the display method we create the split screen and display the two alv’s



  METHOD display.
    DATA:
      l_o_split_container TYPE REF TO cl_gui_easy_splitter_container.
    create_split_control( CHANGING ch_split_container = l_o_split_container ).
* Display candidates
    display_alv( EXPORTING im_o_container = l_o_split_container->top_left_container
                           im_heading     = 'Candidates'
                 CHANGING  ch_t_out_tab   = t_candidate ).
* Display centre
    display_alv( EXPORTING im_o_container = l_o_split_container->bottom_right_container
                           im_heading     = 'Centres'
                 CHANGING  ch_t_out_tab   = t_centre ).
  ENDMETHOD.                    "display


The split ALV posed one problem for me which was giving the title to each of the containers. I posted this question on SDN and got response from Naimesh Patel. The class CL_SALV_DISPLAY_SETTINGS takes care of that. The code for displaying each ALV is modularised in the method display_alv( ). Since the structure I had created did not refer to any data elements the output was coming without any column heading. For this I created another method set_cloumn_text( ) that would read the DDIC structure and get the values from there and the set the column text. I got to use the ABAP runtime classes that give data type description in that method. This works both the structures I had



  METHOD display_alv.
    DATA:
      l_o_table   TYPE REF TO cl_salv_table.
* Get the ALV object refering to the output table
    TRY.
        cl_salv_table=>factory(
          EXPORTING
            r_container = im_o_container
          IMPORTING
            r_salv_table = l_o_table
          CHANGING
            t_table      = ch_t_out_tab ).
      CATCH cx_salv_msg.                                "#EC NO_HANDLER
    ENDTRY.
** Add basic default functionality in the ALV report
** Functions
*    DATA:
*      l_o_functions TYPE REF TO cl_salv_functions_list.
*
*    l_o_functions = l_o_table->get_functions( ).
*    l_o_functions->set_all( abap_true ).
* Column text
    set_column_text( im_alv = l_o_table im_t_out_tab = ch_t_out_tab ).
* Heading
    DATA:
      l_o_display TYPE REF TO cl_salv_display_settings,
      l_title TYPE lvc_title.
    l_title = im_heading.
    l_o_display = l_o_table->get_display_settings( ).
*   Title to ALV
    l_o_display->set_list_header( l_title ).
* Display the list
    l_o_table->display( ).
  ENDMETHOD.                    "display_alv


  METHOD set_column_text.
* Columns
    DATA:
      l_o_columns TYPE REF TO cl_salv_columns_table,
      l_o_column TYPE REF TO cl_salv_column.
    l_o_columns = im_alv->get_columns( ).
    l_o_columns->set_optimize( abap_true ).
    DATA:
      l_o_tabledescr TYPE REF TO cl_abap_tabledescr,
      l_o_structdescr TYPE REF TO cl_abap_structdescr,
      l_t_fields TYPE ddfields.
    FIELD-SYMBOLS:
      <l_field> TYPE LINE OF ddfields.
    l_o_tabledescr ?= cl_abap_typedescr=>describe_by_data( im_t_out_tab ).
    l_o_structdescr ?= l_o_tabledescr->get_table_line_type( ).
    CALL METHOD l_o_structdescr->get_ddic_field_list
*  EXPORTING
*    P_LANGU                  = SY-LANGU
*    P_INCLUDING_SUBSTRUCTRES = ABAP_FALSE
      RECEIVING
        p_field_list             = l_t_fields
    EXCEPTIONS
      not_found                = 1
      no_ddic_type             = 2
      OTHERS                   = 3
            .
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                 WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
    LOOP AT l_t_fields ASSIGNING <l_field>.
      TRY.
          CALL METHOD l_o_columns->get_column
            EXPORTING
              columnname = <l_field>-fieldname
            RECEIVING
              value      = l_o_column.
        CATCH cx_salv_not_found .
      ENDTRY.
      CALL METHOD l_o_column->set_short_text
        EXPORTING
          value = <l_field>-fieldtext(10).
      CALL METHOD l_o_column->set_medium_text
        EXPORTING
          value = <l_field>-fieldtext(20).
    ENDLOOP.
  ENDMETHOD.                    "set_column_text


The split is created using the class CL_GUI_EASY_SPLITTER_CONTAINER this is done in:



  METHOD create_split_control.
    DATA:
      l_o_container TYPE REF TO cl_gui_custom_container.
*      o_split_container TYPE REF TO cl_gui_easy_splitter_container.
    CREATE OBJECT l_o_container
      EXPORTING
*    PARENT                      =
        container_name              = c_container
*    STYLE                       =
*    LIFETIME                    = lifetime_default
*    REPID                       =
*    DYNNR                       =
*    NO_AUTODEF_PROGID_DYNNR     =
      EXCEPTIONS
        cntl_error                  = 1
        cntl_system_error           = 2
        create_error                = 3
        lifetime_error              = 4
        lifetime_dynpro_dynpro_link = 5
        OTHERS                      = 6
        .
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                 WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
    CREATE OBJECT ch_split_container
      EXPORTING
*    LINK_DYNNR        =
*    LINK_REPID        =
*    METRIC            = cntl_metric_dynpro
    parent            = l_o_container
*    ORIENTATION       = 0
*    SASH_POSITION     = 50
*    WITH_BORDER       = 1
*        name              = c_container
      EXCEPTIONS
        cntl_error        = 1
        cntl_system_error = 2
        OTHERS            = 3
        .
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                 WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
  ENDMETHOD.                    "create_split_control




The output will look something like this



image

No comments: