sábado, 10 de mayo de 2014

ALV Orientado a Objetos (OO)

En este post un ejemplo de creación de un ALV con objetos en SAP.

Para crear un ALV OO tenemos que crearnos una nueva DYNPRO y dentro de esta insertar un “Custom control”. Este control será el que contenga al ALV.


Definimos los objetos para el ALV y para el contenedor
* ALV Grid
DATA gref_alvgrid    TYPE REF TO cl_gui_alv_grid.
* Contenedor ALV

DATA gref_ccontainer TYPE REF TO cl_gui_custom_container.  
Cuando queramos lanzar el ALV tenemos que hacer lo siguiente:
-  Creamos el objeto contenedor. Tenemos que pasarle el nombre que hemos dado al “Custom control” de la Dynpro que hemos creado.

 CREATE OBJECT gref_ccontainer
      
EXPORTING

        container_name                      = gc_custom_control_name
      
EXCEPTIONS
        cntl_error                                = 
1
        cntl_system_error                   = 
2
        create_error                             = 
3
        lifetime_error                          = 
4
        lifetime_dynpro_dynpro_link = 
5
        
OTHERS              = 6.
-  Creamos el objeto del ALV. Tenemos que pasarle el objeto contenedor que acabamos de crear.

 CREATE OBJECT gref_alvgrid
      
EXPORTING

        i_parent          = gref_ccontainer
      
EXCEPTIONS
        error_cntl_create = 
1
        error_cntl_init   = 
2
        error_cntl_link   = 
3
        error_dp_create   = 
4
        
OTHERS            = 5.

Además de esto también debemos definir:
-  Catálogo de campos

FORM preparar_catalogo_campos CHANGING po_fieldcat TYPE lvc_t_fcat.

  
DATA ls_fcat TYPE
 lvc_s_fcat .

  
CLEAR
: po_fieldcat.
  
REFRESH
: po_fieldcat.

* Indicador

  
CLEAR ls_fcat.
  ls_fcat-col_pos   = 
1
.
  ls_fcat-coltext   = 
'Ind.'
.
  ls_fcat-fieldname = 
'IND'
.
  ls_fcat-outputlen = 
6
.
  ls_fcat-no_out    = 
' '
.
  ls_fcat-
key = 'X'
.
  
APPEND ls_fcat TO
 po_fieldcat.

* Status
  
CLEAR ls_fcat.
  ls_fcat-col_pos   = 
2
.
  ls_fcat-coltext   = 
'Status'
.
  ls_fcat-fieldname = 
'MSGNR'
.
  ls_fcat-outputlen = 
6
.
  ls_fcat-no_out    = 
' '
.
  ls_fcat-
key = 'X'
.
  
APPEND ls_fcat TO po_fieldcat.

-  Características del layout

FORM preparar_salida  CHANGING po_layout TYPE lvc_s_layo.

  po_layout-grid_title = 
text-002.
*  po_layout-smalltitle = gc_marca.
  po_layout-stylefname = 
'BOTON'.
  po_layout-no_rowmark = 
'X'.
*Los siguientes dos campos son necesarios si queremos definir estilos sobre las celdas
*  po_layout-ctab_fname = 'CELLCOLORS'.
*  po_layout-info_fname = 'ROWCOLOR'.
*El siguiente campo lo informaremos si queremos que las filas del ALV sean seleccionables
*  po_layout-sel_mode = ''.
 
-  Ordenación

FORM prepare_sort_table CHANGING pt_sort TYPE lvc_t_sort .

  
DATA ls_sort TYPE
 lvc_s_sort .

  ls_sort-spos = 
'1'
 .
  ls_sort-fieldname = 
'ZZDOCUMENO'
 .
  ls_sort-
up = 'X' . "A to Z

  ls_sort-down = space .
  
APPEND ls_sort TO pt_sort .

  ls_sort-spos = 
'2'
 .
  ls_sort-fieldname = 
'ZZVERSION'
 .
  ls_sort-
up
 = space .
  ls_sort-down = 
'X' . "Z to A

  
APPEND ls_sort TO pt_sort .

Si queremos quitar botones estándar de la barra del ALV  hay que indicárselo al ALV excluyendo esos botones:
* Botones excluidos del ALV
    
APPEND cl_gui_alv_grid=>mc_fc_refresh  TO lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_check  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_info  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_loc_append_row  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_loc_copy  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_loc_copy_row  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_loc_cut  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_loc_delete_row  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_loc_insert_row  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_loc_move_row  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_loc_paste  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_loc_paste_new_row  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_loc_undo  TO
 lt_excl_func1.
    
APPEND cl_gui_alv_grid=>mc_fc_graph  TO lt_excl_func1.
Por otro lado si lo que queremos es añadir botones (a los que podamos dar funcionalidad) a la barra del ALV lo tenemos que hacer mediante el evento “handle_toolbar”. Ademas debemos indicar al ALV que la barra va a ser interactiva. Para esto llamamos al método “CALL METHOD gref_alvgrid->set_toolbar_interactive.
Para añadir los botones hacemos lo siguiente:
FORM handle_toolbar  CHANGING p_e_object TYPE REF TO cl_alv_event_toolbar_set
                              p_e_interactive 
TYPE
 char01.

  
DATA gs_toolbar TYPE
 stb_button.

  
CLEAR
 gs_toolbar.
  
MOVE 'FICH' TO gs_toolbar-function
.
  
MOVE icon_write_file TO gs_toolbar-icon
.
  
MOVE 'Generar fichero' TO
 gs_toolbar-quickinfo.
  
MOVE 'Generar fichero' TO gs_toolbar-text
.
  
MOVE ' ' TO
 gs_toolbar-disabled.
  
APPEND gs_toolbar TO
 p_e_object->mt_toolbar.
  
CLEAR gs_toolbar.
Además de todo esto podemos añadir muchos eventos al ALV, tantos como los que estén definidos en la clase CL_GUI_ALV_GRID.
En este ejemplo vamos a ver 5 (Uno de ellos es el handle_toolbar explicado en el apartado anterior):
-  handle_user_command

FORM handle_user_command  USING p_e_ucomm TYPE syucomm.

  
CASE
 p_e_ucomm .

    
WHEN 'FICH'
.
      
PERFORM
 generar_fichero.
  
ENDCASE
.
-  handle_double_click

FORM handle_double_click  USING    p_e_row TYPE lvc_s_row
                                   p_e_column 
TYPE
 lvc_s_col
                                   p_es_row_no 
TYPE
 lvc_s_roid.

  
READ TABLE t_lecturas INDEX
 p_es_row_no-row_id.

  
IF sy-subrc = 0
.
    
CASE
 p_e_column-fieldname.
      
WHEN 'MATNR'
.
        
SET PARAMETER ID 'MAT' FIELD
 t_lecturas-matnr.
        
CALL TRANSACTION 'MM03' AND SKIP FIRST SCREEN
.
    
ENDCASE
.
  
ENDIF.

-  handle_data_changed

FORM handle_data_changed USING p_er_data_changed TYPE REF TO cl_alv_changed_data_protocol.

  
DATAline TYPE
 lvc_s_modi,
        indice 
TYPE
 sy-tabix,
        selected_row 
TYPE
 lvc_t_roid,
        l_sel_row 
TYPE
 lvc_s_roid.

  
CLEARline
,
         indice.

  
READ TABLE p_er_data_changed->mt_mod_cells INTO line INDEX 1
.
  
IF sy-subrc = 0
.
    indice = 
line
-row_id.
    
IF indice IS NOT INITIAL
.

      
REFRESH
 selected_row.
      l_sel_row-row_id = indice.

      
APPEND l_sel_row TO
 selected_row.

*     Seleccionamos la linea que ha sido modificada

      
CALL METHOD gref_alvgrid->set_selected_rows
        
EXPORTING

          it_row_no = selected_row.

    
ENDIF.
  
ENDIF.

 
-  handle_click

FORM handle_click  USING p_es_col_id TYPE lvc_s_col
                         p_es_row_no 
TYPE
 lvc_s_roid.

  
DATA: es_row_no  TYPE
 lvc_s_roid,
        es_row_info  
TYPE
 lvc_s_row,
        es_col_info  
TYPE
 lvc_s_col.

  
CLEAR
: es_row_no,
         es_row_info,
         es_col_info.

  
IF p_es_col_id = 'IND'
.
    
PERFORM modificar_status USING
 p_es_row_no-row_id.
  
ENDIF.

Además para que el evento “handle_data_changed” se dispare hay que llamar al método “register_edit_event”.

 CALL METHOD gref_alvgrid->register_edit_event
      
EXPORTING

        i_event_id = cl_gui_alv_grid=>mc_evt_modified.
Finalmente para lanzar el ALV se debe hacer lo siguiente:

    CALL METHOD gref_alvgrid->set_table_for_first_display
      
EXPORTING

        is_layout                     = gs_layout
        it_toolbar_excluding          = lt_excl_func1
      
CHANGING
        it_outtab                     = t_lecturas[]
        it_fieldcatalog               = gi_fieldcat
      
EXCEPTIONS
        invalid_parameter_combination = 
1
        program_error                 = 
2
        too_many_lines                = 
3
        
OTHERS                        = 4.
Si hemos modificado la tabla interna que muestra el ALV y queremos que estos cambios se reflejen al ALV llamamos al siguiente método:
* Refresca el ALV
    
CALL METHOD gref_alvgrid->refresh_table_display
      
EXCEPTIONS

        finished = 
1
        
OTHERS   = 2.
Si al lanzar algún evento no queremos que los scroll se nos inicialicen hacemos lo siguiente antes y después del código que estemos ejecutando (Recuperamos la situación del scroll antes de ejecutar nuestro código, y al finalizar nuestro código le indicamos cual queremos que sea la situación del scroll):
  CALL METHOD gref_alvgrid->get_scroll_info_via_id
    
IMPORTING

      es_row_no   = es_row_no
      es_row_info = es_row_info
      es_col_info = es_col_info.
Si queremos saber que líneas de la tabla interna se están visualizando en pantalla y cuales no (por que se ha aplicado algún filtro) llamamos al siguiente método:
  CALL METHOD gref_alvgrid->get_filtered_entries
    
IMPORTING

      et_filtered_entries = et_filtered_entries.
Si queremos dar a alguna columna alguna apariencia diferente (por ejemplo de botón) hacemos lo siguiente:
-  En la tabla interna que contiene los datos que se muestran en el ALV definimos un nuevo campo de tipo LVC_T_STYL. Este campo contendrá las características de cada fila.

-  Despues al rellenar la tabla interna tendremos que completar este nuevo campo. En este caso, en el que queremos darle la apariencia de botón hacemos lo siguiente:

  CLEAR ls_style.
  
ls_style-fieldname = 'IND'
.
    ls_style-style = cl_gui_alv_grid=>mc_style_button.
    
APPEND ls_style TO
 t_lecturas-boton.
Con esto le estamos indicando que la columna “IND” va a tener la apariencia de un botón.
Finalmente en la definición del layout tenemos que indicarle que campo de nuestra tabla interna es la que define las características de cada fila.

 po_layout-stylefname = 'BOTON'.

3 comentarios:

  1. Si a tu código le vas a seguir metiendo sub-rutinas(form) entonces ya dejó de ser orientado a objetos porque las subrutinas han sido creadas para la programación estructurada, deberías crear y usar tus propios métodos(ya sean privados, públicos, protegidos o estáticos) y utilizarlos en vez de subrutinas. Buen post.

    ResponderEliminar
    Respuestas
    1. Gracias por tu comentario!!Tienes razón en que el programa no es 100% orientado a objetos. Muchas veces en SAP este tipo de clases se utilizan dentro de programas estrucuturados, de ahí que veas los FORM. Yo casi siempre los he utilizado de esta forma, de ahí que en los ejemplos aparezcan los FORM. Tomo nota de tus comentarios. Gracias!!

      Eliminar
    2. Lo que dicen ambos es cierto,en mi caso cuando utilizo clases locales siempre las mezclo con FORM, pero cuando creo clases globales no le meto ningun FORM, bye.

      Eliminar