Mostrando entradas con la etiqueta alv OO. Mostrar todas las entradas
Mostrando entradas con la etiqueta alv OO. Mostrar todas las entradas

martes, 21 de agosto de 2018

Patrones de diseño con ABAP - MVC

El patrón MVC o Modelo-Vista-Controlador es un patrón de diseño de software que se centra en separar la lógica de negocio y los datos de la aplicación del código para su representación visual. Se fundamenta en separar el código en tres capas diferentes, acotadas por su responsabilidad. 
  • MODELO ("M"): Es la capa donde se trabaja con los datos. Esta formada por las clases que contienen la lógica de la aplicación. Estas clases no pueden contener ningún código relacionado con el interfaz de usuario. En ABAP, estas clases correspondería a una o varias clases globales definidas en la transacción SE24.
  • VISTA ("V"): Es la capa donde se va ha producir la visualización de los datos al usuario. Esta formada por las clases clases que contiene todo el código relaciona con el interfaz de usuario. En abap utilizariamos las clases ya existentes para generar ALV, Webdynpros, etc... reciben como parámetro de entrada los datos generados por la capa MODELO y generan la salida.
  • CONTROLADOR ("C"): Esta capa sirve de enlace entre las capas de Modelo y Vista. No puede contener ningún código que manipule datos, ni muestre datos, solamente debe servir como enlace entre los modelos y las vistas. En Abap correspondería a un programa ejecutable implementado en la transacción SE38.

Esta separación en capas otorga al software un grado muy alto de flexibilidad, permitiendo cambiar la lógica de negocio independientemente del interfaz de la aplicación y viceversa.


Patrón Modelo Vista Controlador MVC

Imaginemos una aplicación Abap donde el usuario requiera al programa realizar una acción determinada ( por ejemplo ver  los datos de un proveedor, cliente, pedido de venta, etc... ).

  1. El usuario ejecuta la transacción solicitando al controlador el listado.
  2. El controlador llamara al método del modelo para realizar la acción solicitada por el usuario. 
  3. El modelo retornara los datos solicitados por el usuario al controlador
  4. El controlador llamara al método de la vista encargado de mostrar los datos por pantalla. 
  5. La vista mostrara los datos solicitados al usuario.

En ningún momento de este proceso, el controlador busca o procesa los datos, ni realiza ninguna operación para mostrar los datos al usuario, su trabajo se centra en ser el enlace entre la capa modelo y la capa vista.

Para explicar mejor como utilizar el patrón MVC con ABAP , imaginemos un cliente que nos pide un programa ABAP con los siguientes requerimientos:
  1. Leer los datos de los pedidos de ventas requeridos por el usuario
  2. Elimine aquellos que sean de clientes bloqueados
  3. Muestre el resultado por pantalla utilizando un ALV
  4. El usuario podrá seleccionar registros del ALV
  5. Un pulsador para crear la entrega de salida de los pedidos seleccionados
En ingles a estos programas se les llama aplicaciones CRUD (Create , Read, Update, Delete).

Si implementamos el programa en la transacción SE38 con una estructura clásica ABAP , a grandes rasgos,  nos quedara un programa parecido al siguiente código:

REPORT zreport_clasico_abap.

* Definiciones de datos y estructuras globales
* Parametros de la pantalla de selección

STAR-OF-SELECTION.
  PERFORM leer_pedidos_ventas.
  PERFORM eliminar_bloqueados.
  PERFORM display_alv.

END-OF-SELECTION.

FORM leer_pedidos_ventas.
* Código para leer datos de las tablas VBAK/VBAP
* Almacenar los datos en una tabla interna
ENDFORM.

FORM eliminar_bloqueados.
* Código para eliminar pedidos con clientes bloqueados
* Leer datos de alguna tabla KN**
* Eliminar registros de la tabla interna
ENDFORM.

FORM display_alv.
* Mostar la tabla interna por pantalla en un ALV (REUSE_ALV_GRID_DISPLAY)
ENDFORM.

FORM user_command USING pi_ucomm rs_selfield.
  IF ( pi_ucomm EQ c_ucomm_save ).
    PERFORM crear_entregas.
  ENDIF.
ENDFORM.

FORM crear_entregas.
*  Código para  crear entregas de los pedidos seleccionados
ENDFORM

* Más y más subrutinas....


El principal problema del enfoque clásico es que, el código para obtener y manipular los datos esta mezclado con el código del interfaz de usuario o GUI. Con suerte, el programador encapsulara en subrutinas las operaciones de acceso de datos y para lanzar el ALV, pero en la mayoría de los casos nos encontraremos mezclado, dentro de la misma subrutina, tanto el código para acceder  a los datos  y el código para mostrar los datos. Para que os deis cuenta de los problemas que puede acarrear esta estructura, imaginaos que surge un nuevo requerimiento que diga que incluyamos la misma funcionalidad del programa en una función RFC (Remote Funcion Call). Despues de crear nuestro programa nos piden crear una función RFC que reciba una serie de números de pedidos de venta, elimine los que tiene clientes bloqueados y genere las entregas de salida.

En estos casos, lo que podemos hacer con una estructura de programación clásica de ABAP, es bastante limitado:
  1. Copiar el código del programa dentro de una bapi. No es muy buena idea... porque si cambia la lógica para seleccionar clientes bloqueados o se añaden nuevos campos, tendremos que adaptar ambos programas, es decir para una modificación habrá que adaptar múltiples programas en el sistema. Lo mejor para mantener un código siempre es que sea simple y evitar duplicidades.
  2. Incluir el código común en subrutinas y guardarlo en un fichero que pueda ser utilizado desde cualquier programa con la instrucción INCLUDE. Es una solución mejor que la anterior, pero la interfaz, la información que conpartiran las subrutinas al ejecutarse,  necesitara ser definida en las transacciones SE37/SE38 múltiples veces. Volvemos al caso anterior, si por ejemplo añadimos nuevos campos, necesitaremos modificar en múltiples puntos.
  3. Crear un grupo de funciones y convertir las subrutinas en funciones que puedan ser llamadas desde cualquier programa. La solución más elegante de las tres, pero si usas funciones jamas podrás utilizar las ventajas que te ofrece la programación orientada a objetos como la herencia, polimorfismo y la encapsulación.
Enfoquemos el problema utilizando programación orientada a objetos y el patrón MVC. El objetivo es  ver como nos puede ayudar a realizar aplicaciones, con un grado de flexibilidad ante los cambios o nuevos requerimientos mucho mayor que la clásica programación ABAP.

La primera ventaja la encontramos en la fase de diseño. Al utilizar programación orientada a objetos podemos utilizar el Lenguaje de Modelado Unificado o UML para diseñar un diagrama de nuestro programa siguiendo el patrón MVC. Para este ejemplo, el diagrama UML es bastante sencillo.

diagrama UML de la aplicación


Ambos controladores ( programa y la funcion RFC ) usaran la misma clase de modelo pero podrán después reconducir los datos y recibir las peticiones de sus modelos de vistas. La clase ZCL_MODELO_DATOS encapsula toda la lógica de la aplicación en sus métodos y no contiene ningún código referente al GUI de la aplicación. Por ultimo, la capa VISTA sera la función REUSE_ALV_GRID_DISPLAY y para la función RFC sera la llamada a la función RFC desde el programa externo. Como podéis observar cada capa tiene acotada su responsabilidad y no se encarga de nada que no le corresponda.

La clase ZCL_MODELO_DATOS la creamos como una clase global en la transacción SE24 para que podamos reutilizarla en todos los controladores que diseñemos.

 El código de la clase global seria más o menos el siguiente:
class ZCL_MODELO_DATOS definition public final create public .

  public section.

    types: BEGIN OF T_PARAM,
      s_vbeln TYPE RANGE OF vbak-vbeln,
      s_vbtyp TYPE RANGE OF vbak-vbtyp,
      s_kunnr TYPE RANGE OF vbak-kunnr,
     END OF t_param .
   
    types: BEGIN OF T_PEDIDOS,
      VBELN type vbak-vbeln,
      posnr type vbap-posnr,
      kunnr type vbak-kunnr,
      name1 type kna1-name1,
      matnr type vbap-matnr,
      maktx type makt-maktx,
     END OF t_pedidos .
   
    types:
      tt_pedidos type STANDARD TABLE OF t_pedidos with non-unique key TABLE_LINE.

    methods LEER_PEDIDOS_VENTAS
     importing
       !IS_PARAM type T_PARAM.
  
    methods ELIMINAR_BLOQUEADOS.
    methods GET_LISTA_PEDIDOS.
     returning
      value(RT_LISTA) type TT_PEDIDOS .
    methods CREAR_PEDIDOS.
  protected section.

  private section.
    data ZTT_PEDIDOS type TT_PEDIDOS .

ENDCLASS.

CLASS ZCL_MODELO_DATOS IMPLEMENTATION.

  method LEER_PEDIDOS_VENTAS.
    "Código para leer la VBAK/VBAP segun parametros y rellenar la tabla ZTT_PEDIDOS
  endmethod.


  method ELIMINAR_BLOQUEADOS.
    "Código para recorrer la tabla interna ZTT_PEDIDOS 
    "Eliminar clientes bloqueados consultando tablas KN**
  endmethod.

  method GET_LISTA_PEDIDOS.
    rt_lista[] = ztt_pedidos
  endmethod.

  method CREAR_PEDIDOS.
    "Algo de código para un LOOP a la tabla ZTT_PEDIDOS y crear las entregas 
  endmethod.

ENDCLASS.

No hace falta que toda la lógica de la aplicación este en una misma clase. La ventaja de usar clases globales es la reutiliziación del código. Para un proyecto podría crearse toda una librería de clases que encapsulasen la lógica de las aplicaciones y así evitar la duplicidad del código. Para crear toda esta estructura de clases y no morir en el intento, podeis utilizar el patron de diseño COMPOSITE para construir objetos complejos a partir de otros más simples y similares entre sí, gracias a la composición recursiva y a una estructura en forma de árbol.

Ahora que tenemos la capa modelo, veamos como quedaría el programa en la transacción SE38:

REPORT ZREPORT_SE38.

DATA go_modelo_datos TYPE REF TO zcl_modelo_datos.
DATA ti_pedidos TYPE ztt_pedidos.

* Algunas definiciones de datos 
* Parametros de la pantalla de seleccion

START-OF-SELECTION.
  go_modelo_datos = NEW #( ).
  go_modelo_datos->leer_pedidos_ventas( ).
  go_modelo_datos->eliminar_bloqueados( ).
  ti_pedido = go_modelo_datos-get_lista_pedidos( ).
  perform display_alv.
END-OF-SELECTION.

FORM display_alv.
* Código para llamar a REUSE_ALV_GRID_DISPLAY
ENDFORM.

FORM user_command USING pi_ucomm rs_selfield.
  IF ( pi_ucomm EQ c_ucomm_save ).
    go_modelo_datos->crear_entregas( ).
  ENDIF.
ENDFORM.

Utilizando el patrón MVC,  lo que hace el programa ZREPORT_SE38 es únicamente funcionar como "mensajero" entre la clase ZCL_MODELO_DATOS y la función REUSE_ALV_GRID_DISPLAY.

En caso de necesitar modificar el ALV y cambiar nombres de columnas, orden de las columnas, añadir pulsadores, etc... podrías crear una nueva clase que llame a REUSE_ALV_GRID_DISPLAY e implementar todos los métodos que necesitáis para modificar y ajustar el ALV. El programa que funcione como controlador llamaría a esta nueva clase en vez de a REUSE_ALV_GRID_DISPLAY.

También, en vez de usar la bapi REUSE_ALV_GRID_DISPLAY, podríamos utilizar la clase CL_SALV_TABLE. Crear una nueva clase que tenga un constructor o un método que configure el ALV y genere el ALV usando la clase CL_SALV_TABLE. De esta manera seguimos manteniendo el principio del patrón MVC de separar la responsabilidad de cada capa.

En resumen, El objetivo del patrón MVC es separar los datos y su procesamiento de su representación visual. Las principales ventajas son que los programas se vuelven mucho mas flexibles y escalables ante nuevos requerimientos o cambios. También nos permite tener un mismo modelo para la obtención de datos que podemos reutilizar en múltiples vistas para la representación de los datos.

Por contra, al utilizar programación orienta a objetos, tendremos que crear nuestras propias clases, aumentando la cantidad de objetos que hay que mantener ( pasamos de 1 programa a como mínimo 1 clase global y un programa )  y la complejidad del sistema.

Fuentes:
Design Patterns in ABAP Objects 2017 Kerem Koseoglu
The MVC design pattern in ABAP for practical use
ABAP Objects Design Patterns – Model View Controller
SDN Wiki - Model View Controller design Pattern

miércoles, 9 de marzo de 2016

Clase CL_SALV_TABLE: Toolbar, pulsadores y funciones a medida - parte I

Hasta ahora,  hemos explicado como mostrar la información que extraemos de la base de datos y como modificar los atributos de la columnas del listado ALV. Pero los usuarios, rara vez solamente quieren "ver" la información, lo normal es que quieran interactuar con ella de diferentes formas. Por ejemplo ordenar el listado, imprimir el listado descargar el listado  en una hoja excel, etc...

Para añadir funcionalidades a medida a los listados ALV, la CL_SALV_TABLE incluye pulsadores y funciones estándar que podemos configurar  antes de llamar al método display( ) .

try.
      cl_salv_table=>factory(
        importing
          r_salv_table = gr_table
        changing
          t_table      = ti_mara ).

      gr_table->get_functions( )->set_all( if_salv_c_bool_sap=>true ).     "TODOS los pulsadores estandard
*     gr_table->get_functions( )->set_default( if_salv_c_bool_sap=>true ). "Solamente algunos puls estandard

      gr_table->display( ).

    catch cx_salv_msg into cx_salv.
*     Gestionamos las excepciones que puedan suceder
      gr_msg = cx_salv->get_text( ).
      message gr_msg type 'E'.
    catch  cx_salv_not_found into cx_not_found.
      gr_msg = cx_not_found->get_text( ).
      message gr_msg type 'E'
  endtry.

Toolbar estándar creada con el metodo set_all( if_salv_c_bool_sap=>true )

Toolbar estándar creada con el metodo set_default( if_salv_c_bool_sap=>true )

Tambien es posible añadir pulsadores personalizados que llamen a  funciones diseñadas para propósitos específicos.

Para crear vuestro propio status de usuario, podeios copiar el status SALV_STANDARD del programa SALV_DEMO_METADATA desde la trnasacción SE38

  1. Transacción  SE38 -> SALV_DEMO_METADATA  -> Status GUI -> SALV_STANDARD
  2. Situar el cursor sobre Status GUI -> SALV_STANDARD
  3. Pulsar el botón derecho del ratón -> Copiar
  4. Introducimos el nombre de nuestro programa y el nuevo nombre del status gui
  5. En la copia que hemos realizado, añadir los pulsadores a medida que necesitamos.
  6. Vincular el STATUS GUI al ALV utilizando el método set_screen_status( ).
  7. Activarlo todo
try.
      cl_salv_table=>factory(
        importing
          r_salv_table = gr_table
        changing
          t_table      = ti_mara ).

      gr_table->set_screen_status( pfstatus = 'ZZSTATUS_001'  "Nuestro STATUS GUI
                                   report = sy-repid
                                   set_functions = gr_table->c_functions_all ).

      gr_table->display( ).

    catch cx_salv_msg into cx_salv.
*     Gestionamos las excepciones que puedan suceder
      gr_msg = cx_salv->get_text( ).
      message gr_msg type 'E'.
    catch  cx_salv_not_found into cx_not_found.
      gr_msg = cx_not_found->get_text( ).
      message gr_msg type 'E'
  endtry.

Si activamos y ejecutamos, aparecerán los pulsadores de nuestro status gui. Pero si los pulsamos, no sucederá nada. Para asignar una función a un pulsador, primero  necesitamos implementar una clase de eventos y enlazarla a nuestra clase CL_SALV_TABLE.

1.- Crear la siguiente definición para la clase de eventos CLASS_HANDLE_EVENTS. En este caso, solamente implementamos el evento ON_USER_COMMAND, pero se pueden implementar mas eventos que explicare en otra entrada.

*---------------------------------------------------------------------*
*       CLASS class_handle_events DEFINITION
*---------------------------------------------------------------------*
*  define a local class for handling events of cl_salv_table
*---------------------------------------------------------------------*
class class_handle_events definition.
  public section.
    methods:
      on_user_command
                    for event added_function of cl_salv_events
        importing e_salv_function. "e_salv_function es como el OK_CODE de las dynpros

endclass.                    "lcl_handle_events DEFINITION


2.- Crear la  implementacion de la clase y el método ON_USER_COMMAND.
*---------------------------------------------------------------------*
*       CLASS lcl_handle_events IMPLEMENTATION
*---------------------------------------------------------------------*
* implement the events for handling the events of cl_salv_table
*---------------------------------------------------------------------*
class class_handle_events implementation.

  method on_user_command.

    case e_salv_function. "Contiene el cod. de funcion del pulsador seleccionado
      WHEN 'GO_VBELN'.  "mensaje por pantalla
        MESSAGE "Esto es una prueba, funcion GO_VBELN" display like 'I'.
    ENDCASE.
  endmethod.

endclass.                    "lcl_handle_events IMPLEMENTATION


3.- El ultimo paso es enlazar la clase de eventos con nuestra instancia de la clase CL_SALV_TABLES. Esto significa que cuando se produzca un evento ( doble click, pulsar un boton de la barra de herramientas, etc...) sera "escuchado" por la clase de eventos y ejecutara el metodo asignado al evento.

Como podéis observar el parámetro de entrada e_alv_funcion contiene el código de función del pulsador que ha seleccionado el usuario en la barra de herramientas. Identificando ese código en el método ON_USER_COMMAND podemos identificar, procesar y actuar en función de la acción realizada por del usuario ( por ejemplo grabar los datos, ejecutar una bapi, salir del programa, etc...).

Todo evento que queráis en vuestro ALV tenéis que enlazarlo con la clase CL_SALV_TABLES:

DATA r_handler_salv_table type REF TO class_handle_events. 
try.
      cl_salv_table=>factory(
        importing
          r_salv_table = gr_table
        changing
          t_table      = ti_mara ).

      gr_table->set_screen_status( pfstatus = 'ZZSTATUS_001'  "Nuestro STATUS GUI
                                    report = sy-repid
                                    set_functions = gr_table->c_functions_all ).

*     Creamos la instancia de la clase de eventos y registramos el evento on_user_command
      CREATE OBJECT r_handler_salv_table.
      set handler r_handler_salv_table->on_user_command for gr_table->get_event( ).

      gr_table->display( ).

    catch cx_salv_msg into cx_salv.
*     Gestionamos las excepciones que puedan suceder
      gr_msg = cx_salv->get_text( ).
      message gr_msg type 'E'.
    catch  cx_salv_not_found into cx_not_found.
      gr_msg = cx_not_found->get_text( ).
      message gr_msg type 'E'
  endtry.


Aquí os dejo un ejemplo completo,  Es un listado de materiales, con pulsadores a medida.
No os olvidéis de crear el status gui y añadirle los pulsadores a medida
En la siguiente entrada os mostrare como añadir pulsadores y menús a la barra de herramientas utilizando eventos.

STATUS GUI de nuestro ALV
CL_SALV_TABLE con pulsadores a medida


*&---------------------------------------------------------------------*
*& Report:  ZZCL_SALV_TABLE_FULL_SCREEN
*& Autor :  David Rueda Barrón
*&---------------------------------------------------------------------*
*& Creacion de ALV a pantalla completa con la clase CL_SALV_TABLE
*& Se añaden 3 pulsadores a medida con un STATUS GUI
*& La clase de eventos CLASS_HANDLE_EVENTS recoje los eventos
*&---------------------------------------------------------------------*

report zzcl_salv_table_full_screen.

*---------------------------------------------------------------------*
*       CLASS class_handle_events DEFINITION
*---------------------------------------------------------------------*
*  define a local class for handling events of cl_salv_table
*---------------------------------------------------------------------*
class class_handle_events definition.
  public section.
    methods:
      on_user_command
                    for event added_function of cl_salv_events
        importing e_salv_function. "e_salv_function es como el OK_CODE de las dynpros

endclass.                    "lcl_handle_events DEFINITION
*---------------------------------------------------------------------*
*       CLASS lcl_handle_events IMPLEMENTATION
*---------------------------------------------------------------------*
* implement the events for handling the events of cl_salv_table
*---------------------------------------------------------------------*
class class_handle_events implementation.

  method on_user_command.

    case e_salv_function. "Contiene el cod. de funcion del pulsador seleccionado
      WHEN 'GO_VBELN'.  "mensaje por pantalla
        MESSAGE 'Esto es una prueba, funcion GO_VBELN' TYPE 'I'.
      WHEN 'GO_VF03'.
        MESSAGE 'Esto es una prueba, funcion GO_VF03' TYPE 'I'.
      WHEN 'GO_MM02'.
        MESSAGE 'Esto es una prueba, funcion MM02' TYPE 'I'.
    ENDCASE.

  endmethod.

endclass.                    "lcl_handle_events IMPLEMENTATION

*---------------------------------------------------------------------*
*       TIPOS, ESTRUCTURAS y VARIABLES GLOBALES
*---------------------------------------------------------------------*
types: begin of type_matnr,
         matnr type mara-matnr,
         maktx type makt-maktx,
         mtart type mara-mtart,
         matkl type mara-matkl,
         meins type mara-meins,
       end of type_matnr.

* Tabla interna con los datos del ALV
data ti_mara type standard table of type_matnr.


data gr_table type ref to cl_salv_table.
data r_handler_salv_table type REF TO class_handle_events.

*Variables globales para gestionar las excepciones
data gr_msg  type string.
data cx_salv  type ref to cx_salv_msg.
data cx_not_found TYPE ref to cx_salv_not_found.
*&---------------------------------------------------------------------*
*& START-OF-SELECTION
*&---------------------------------------------------------------------*
start-of-selection.

  select m~matnr t~maktx m~mtart m~matkl m~meins
    into corresponding fields of table ti_mara
    from mara as m
    inner join makt as t
       on m~matnr eq t~matnr
      and t~spras eq sy-langu.

  try.
      cl_salv_table=>factory(
        importing
          r_salv_table = gr_table
        changing
          t_table      = ti_mara ).
    catch cx_salv_msg into cx_salv.
*     Gestionamos las excepciones que puedan suceder
      gr_msg = cx_salv->get_text( ).
      message gr_msg type 'E'.
  endtry.

  try.
*   Registramos el status gui para el ALV
    gr_table->set_screen_status( pfstatus = 'ZZSTATUS_001'  "Nuestro STATUS GUI
                                 report = sy-repid
                                 set_functions = gr_table->C_FUNCTIONS_ALL ).

*   Creamos la instancia de la clase de eventos y registramos el evento on_user_command
    CREATE OBJECT r_handler_salv_table.
    SET HANDLER r_handler_salv_table->on_user_command for gr_table->get_event( ).

    catch  cx_salv_msg into cx_salv.
      gr_msg = cx_salv->get_text( ).
      message gr_msg type 'E'.
    catch  cx_salv_not_found into cx_not_found.
       gr_msg = cx_not_found->get_text( ).
      message gr_msg type 'E'.
  endtry.

  gr_table->display( ).

Entradas anteriores:

Entradas siguientes:
Clase CL_SALV_TABLE: Toolbar, pulsadores y funciones a medida - parte II

domingo, 27 de septiembre de 2015

Clase CL_SALV_TABLE: Modificar atributos de las columnas

En el anterior post mostramos como generar un ALV utilizando solo la clase CL_SALV_TABLE. A continuación vamos a explicar como se modifican los atributos de las columnas del ALV cuando lo generamos utilizando la clase CL_SALV_TABLE.

A diferencia de los ALV generados con la bapi REUSE_ALV_GRID_DISPLAY o con la clase CL_GUI_ALV_GRID  la clase  CL_SALV_TABLE no utiliza un catalogo de campos o fieldcat para determinar las características de las columnas del listado ALV.  Cada columna del ALV debe ser tratada como un objeto individual con métodos que nos permitirán modificar sus atributos y  características como su descripción, longitud, ocultar la columna, etc...

Necesitamos instanciar dos clases:
  • CL_SALV_COLUMNS_TABLE  : Gestionar las columnas que componen el ALV
  • CL_SALV_COLUMN    : Gestionar los atributos de una columna del ALV
La primera clase CL_SALV_COLUMNS_TABLE nos permitirá gestionar atributos y características de las columnas del ALV. Para mas información podéis acceder a los métodos y atributos de la clase CL_SALV_COLUMNS_TABLE desde la transacción SE24 -> Pasar a -> Documentación -> Clase.

Transacción SE24 -> Clase CL_SALV_COLUMNS_TABLE

Para modificar las características y atributos propios de cada columna del ALV  instanciaremos un objeto CL_SALV_COLUMN_TABLE utilizando el metodo GET_COLUMN de la clase CL_SALV_COLUMNS_TABLE. Este método recibe como parámetro de entrada el nombre de una columna del ALV y retorna un objeto de la clase CL_SALV_COLUMN_TABLE que nos permite con sus métodos cambiar las características y atributos de la columna.

Transacción SE24 -> Clase CL_SALV_COLUMN_TABLE

Desde la transacción SE24  -> Pasar a -> Documentación -> Clase  podéis ver todos los métodos de la clase CL_SALV_COLUMN y que atributos nos permiten modificar.  Los métodos que mas se suelen utiliza son:


  • SET_LONG_TEXT:  Descripción larga de la columna
  • SET_MEDIUM_TEXT:Descripción media de la columna
  • SET_SHORT_TEXT: Descripción corta de la columna
  • SET_VISIBLE:   Oculta o muestra la columna ( 'X' muestra , '  '  oculta la columna )
  • SET_OUTPUT_LENGTH:  Especifica el ancho de la columna 
  • SET_OPTIMIZED:  Optimiza el ancho de la columna automáticamente
  • SET_CELL_TYPE: formato de la celda ( ej: campo, checkbox, etc.. ) 

A continuación, ampliamos el código del anterior post para modificar las características de algunas columnas.


*&---------------------------------------------------------------------*
*& Report:  ZZCL_SALV_TABLE_FULL_SCREEN
*& Fecha :  29.07.2015
*& Autor :  David Rueda Barrón
*&---------------------------------------------------------------------*
*& Creacion de ALV a pantalla completa con la clase CL_SALV_TABLE
*& Modificacion de los atributos de la columnas con las clases:
*&   - CL_SALV_COLUMNS_TABLE
*&   - CL_SALV_COLUMN_TABLE
*&---------------------------------------------------------------------*

report zzcl_salv_table_full_screen.

types: begin of type_matnr,
         matnr type mara-matnr,
         maktx type makt-maktx,
         mtart type mara-mtart,
         matkl type mara-matkl,
         meins type mara-meins,
         box   type c,
       end of type_matnr.

data: ti_mara type standard table of type_matnr.

data gr_table     type ref to cl_salv_table.         "Instancia de la clase
data gr_columns   type ref to cl_salv_columns_table. "Para gestionar las columnas
data gr_column    type ref to cl_salv_column_table.  "Para gestionar atrb. de una columna
data cx_salv      type ref to cx_salv_msg.
data cx_not_found TYPE ref to cx_salv_not_found.

data gr_msg  type string.
*&---------------------------------------------------------------------*
*& START-OF-SELECTION
*&---------------------------------------------------------------------*
start-of-selection.

  select m~matnr t~maktx m~mtart m~matkl m~meins
    into corresponding fields of table ti_mara
    from mara as m
    inner join makt as t
       on m~matnr eq t~matnr
      and t~spras eq sy-langu.

  try.
      cl_salv_table=>factory(
        importing
          r_salv_table = gr_table
        changing
          t_table      = ti_mara ).
    catch cx_salv_msg into cx_salv.
*     Gestionamos las excepciones que puedan suceder
      gr_msg = cx_salv->get_text( ).
      message gr_msg type 'E'.
  endtry.

  try.
      gr_columns ?= gr_table->get_columns( ).
*     gr_columns->set_optimize( 'X' ).   "Optimizar automa. abcho de TODAS las columnas

*     Cambiamos la descripción de la columna MATNR - MATERIAL
      gr_column  ?= gr_columns->get_column( 'MATNR' ).
      gr_column->set_short_text( 'Cod.Mat.' ).
      gr_column->set_medium_text( 'Cod. Material' ).
      gr_column->set_long_text( 'Código Material SAP' ).

*     Ocultamos la columna MTART - Tipo de material
      gr_column  ?= gr_columns->get_column( 'MTART' ).
      gr_column->set_visible( value  = if_salv_c_bool_sap=>false ).

*     Cambiamos la longitud de la columnas:
*     MAKTX descripción del material
*     MATKL Grupo de articulos
      gr_column  ?= gr_columns->get_column( 'MAKTX' ).
      gr_column->set_output_length( 30 ).

      gr_column  ?= gr_columns->get_column( 'MATKL' ).
      gr_column->set_output_length( 20 ).

*     Cambiamos la descripcion y formato de la columna box
      gr_column  ?= gr_columns->get_column( 'BOX' ).
      gr_column->set_short_text( 'box' ).
      gr_column->set_medium_text( 'Checkbox' ).
      gr_column->set_long_text( 'Checkbox' ).
      gr_column->set_cell_type( if_salv_c_cell_type=>checkbox ).

    catch  cx_salv_msg into cx_salv.
      gr_msg = cx_salv->get_text( ).
      message gr_msg type 'E'.
    catch  cx_salv_not_found into cx_not_found.
       gr_msg = cx_not_found->get_text( ).
      message gr_msg type 'E'.
  endtry.

  gr_table->display( ).

CL_SALV_TABLE  con los atributos de las comunas modificados

Entradas anteriores:
Clase CL_SALV_TABLE: Introducción