Creando una vista CDS

En este post rápido, les voy a mostrar como crear una vista CDS fácilmente.

Ingresamos a Eclipse, vamos al package que usemos o creamos un Data Definition como objeto local.

En la ventana que se abre, buscamos Core Data Services y seleccionamos Data Definition.

Ingresamos un nombre / Descripción y hacemos click en next.

En la siguiente pantalla, se nos dará la opción de elegir una orden de transporte, en el caso que estemos trabajando en un package, si estamos en un objeto local nos aparecerá la pantalla griseada.

Seleccionamos la orden de transporte y hacemos click en next.

Si estamos en un objeto local hacemos click en next.

En la siguiente pantalla se nos dará a seleccionar un template para la vista CDS, seleccionamos Define View y hacemos click en Finish.

Al hacer click en Finish volveremos a Eclipse y veremos algo como lo siguiente:

En esta pantalla, debemos ingresar un nombre para nuestra vista ABAP (sqlViewName) y reemplazar el data_source_name por las tablas de donde sacamos los datos. Por ejemplo:

@AbapCatalog.sqlViewName: 'ZCDS_VUELOS_V'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'CDS Para aerolineas y vuelos'
define view ZCDS_VUELOS as select from scarr 
                        join sflight 
                            on scarr.carrid = sflight.carrid
                        join sbook
                            on sflight.carrid = sbook.carrid and
                               sflight.connid = sbook.connid and
                               sflight.fldate = sbook.fldate
{
    key scarr.carrid,
    key sflight.connid,
    key sflight.fldate,
    key sbook.bookid,
    scarr.carrname,
    scarr.url,
    sflight.price,
    sflight.currency,
    sflight.planetype,
    sflight.seatsmax,
    sflight.seatsocc,
    sbook.customid,
    sbook.custtype,
    sbook.class,
    sbook.loccuram,
    sbook.loccurkey,
    sbook.order_date,
    sbook.passname
}

En esta vista CDS podemos ver que estamos haciendo un JOIN de 3 tablas.

Una ves que tenemos nuestra vista CDS lista, activamos (CTRL+F3).

Con nuestra vista CDS ya activa podemos ir a la SE16 con el nombre, en este caso ZCDS_VUELOS_V y probarla.

Vemos que nos trae datos:

También la podemos probar desde eclipse, haciendo click derecho sobre nuestra vista CDS y yendo a:

Tambien podemos hacer uso de la vista CDS desde un reporte de ABAP común y corriente.

Como podemos ver en la captura, estamos parados después de que se realizo el SELECT y la tabla interna LT_DATOS contiene datos.

Clases ABAP (Globales)

En SAP hay dos tipos de clases, las que podemos desarrollar en un reporte Z, como vimos en el post anterior, o las clases globales que son las que definimos en la transacción SE24.

La principal diferencia entre una clase global y una local es que las locales son especificas a un reporte, mientras las clases globales pueden ser usadas desde cualquier lado.

Para definir una clase, vamos a la transacción SE24, para seguir con el ejemplo anterior vamos a volver a crear la clase vehículo.

Hacemos click en “Create”

En la siguiente ventana ingresamos una descripción y hacemos click en “SAVE”

Creamos el objeto como un objeto local.

Nos aparecerá la siguiente ventana donde debemos especificar el método a crear, visibilidad, tipo, etc.

El siguiente paso es definir los parámetros de los métodos, para eso seleccionamos un método y hacemos click en el botón “Parameters”.

Vamos a la solapa “Attributes” y definimos nuestra variable protegida.

Con nuestra clase con los métodos y variable definidas le damos click a activar.

Nos aparecerá una ventana como la siguiente en donde seleccionamos todos los objetos y le damos OK.

Nuestra clase debería haber quedado activa.

Finalmente hay que hacer el código que en cada método para que la clase haga algo.

Metodo obtener tipo:

WRITE 'AUTO'.

Método obtener marca:

WRITE 'FORD'.

Método set_notas:

lv_notas = notas.

Método ver_notas:

notas = lv_notas.

En este punto ya tenemos nuestra clase lista, solo nos resta volver a activarla para asegurarnos que esta todo OK.

Ahora nuestra clase estará disponible para ser usada desde cualquier reporte que se quiera crear.

Abajo dejo un ejemplo de uso.

*&---------------------------------------------------------------------*
*& Report Z_CLASE_GLOBAL
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT Z_CLASE_GLOBAL.

DATA: lc_cg TYPE REF TO ZCL_VEHICULO.

CREATE OBJECT lc_cg.

lc_cg->set_notas( 'Una nota' ).
lc_cg->obtener_tipo( ).
NEW-LINE.
lc_cg->obtener_marca( ).
NEW-LINE.

DATA: lv_notas TYPE string.
lc_cg->ver_notas( IMPORTING notas = lv_notas ).
WRITE lv_notas.

Encapsulamiento, Herencia y Polimorfismo en ABAP

Estaba refrescando un poco mi memoria de programación orientada a objetos (OOP) en ABAP, y decidí hacer el siguiente ejemplo, usando los conceptos de Encapsulamiento, Herencia y Polimorfismo.

Haciendo un ejemplo de polimorfismo podemos de paso abarcar también Herencia y Encapsulamiento, por ejemplo, digamos que queremos tener una clase con los atributos de vehículos, por ejemplo autos y motos. Para hacer esto podríamos definir una clase “Vehículos” que abarque a varios tipos de valga la redundancia vehículos.

Primero definimos una clase abstracta llamada vehículo, una clase abstracta tiene la particularidad que solo necesitamos definir, la DEFINITION y no tiene IMPLEMENTATION. Esto lo usaremos de mano de la definición de métodos para lograr polimorfismo.

CLASS lcl_vehiculo DEFINITION ABSTRACT.

  PUBLIC SECTION.
    METHODS: obtener_tipo ABSTRACT,
             obtener_marca ABSTRACT,
             set_notas ABSTRACT
              IMPORTING notas TYPE string,
             ver_notas ABSTRACT
              EXPORTING notas TYPE string.

  PROTECTED SECTION.
    DATA: lv_notas TYPE string.

ENDCLASS.

En el código de arriba podemos ver que definimos la clase junto con sus métodos (Públicos) y en la sección Protegida la variable notas (Aca tenemos un ejemplo de encapsulamiento, donde la variable al estar dentro de la sección PROTECTED solo puede ser accedida desde si misma y clases heredadas. Podríamos pensar de una clase abstracta como un template o una “receta” para luego definir otras clases.

El paso siguiente seria por ejemplo definir una clase para los objetos de tipo auto usando la que ya tenemos.

CLASS lcl_auto DEFINITION
               INHERITING FROM lcl_vehiculo.

  PUBLIC SECTION.
    METHODS: obtener_tipo REDEFINITION,
             obtener_marca REDEFINITION,
             set_notas REDEFINITION,
             ver_notas REDEFINITION,
             cantidad_puertas.

ENDCLASS.

En el código de arriba definimos la clase lcl_auto y podemos ver la sentencia INHERITING FROM esto indica que estamos HEREDANDO la clase lcl_vehiculo, con todos sus métodos y variables.

Tambien podemos ver que esta clase (lcl_auto) tiene un método mas (cantidad_puertas), cada clase sea heredada o no puede tener sus propios métodos.

En los métodos podemos ver que tienen la palabra clave REDEFINITION, esto quiere decir que el método lo vamos a redefinir para que haga lo que nosotros deseemos. Ahora creamos la implementacion de la clase.

CLASS lcl_auto IMPLEMENTATION.

  METHOD obtener_tipo.
    WRITE 'AUTO'.
  ENDMETHOD.

  METHOD obtener_marca.
    WRITE 'FORD'.
  ENDMETHOD.

  METHOD set_notas.
    lv_notas = notas.
  ENDMETHOD.

  METHOD ver_notas.
    WRITE lv_notas.
  ENDMETHOD.

  METHOD cantidad_puertas.
    WRITE '4'.
  ENDMETHOD.

ENDCLASS.

En la implementacion de cada método podemos poner lo que nosotros queremos que el mismo haga. Podemos ver particularmente en el método set_notas que estamos seteando el valor de la variable lv_notas, esto es posible porque la misma esta declarada como PROTECTED, con lo cual una clase heredada puede modificar su valor. Si en lugar de PROTECTED fuese PRIVATE esto no seria posible.

Ahora hacemos lo mismo que antes pero para un objeto moto.

Primero declaramos la definición y luego la implementacion.

CLASS lcl_moto DEFINITION
               INHERITING FROM lcl_vehiculo.

  PUBLIC SECTION.
    METHODS: obtener_tipo REDEFINITION,
             obtener_marca REDEFINITION,
             set_notas REDEFINITION,
             ver_notas REDEFINITION.

ENDCLASS.
CLASS lcl_moto IMPLEMENTATION.

  METHOD obtener_tipo.
    WRITE 'MOTO'.
  ENDMETHOD.

  METHOD obtener_marca.
    WRITE 'HONDA'.
  ENDMETHOD.

  METHOD set_notas.
    lv_notas = notas.
  ENDMETHOD.

  METHOD ver_notas.
    WRITE lv_notas.
  ENDMETHOD.

ENDCLASS.

Como podemos ver en el ejemplo de arriba esta clase no tiene el método cantidad_puertas.

Finalmente solo resta hacer la declaración de los objetos y su llamado.

Para declarar los objetos hacemos lo siguiente:

"Declaramos los objetos.
DATA: lo_autos TYPE REF TO lcl_auto,
      lo_motos TYPE REF TO lcl_moto.

"Creamos los objetos.
CREATE OBJECT lo_autos.
CREATE OBJECT lo_motos.

Primero creamos dos variables que van a contener los objetos y después creamos los mismos.

El ultimo paso es la llamada a los métodos de los objetos

CALL METHOD lo_autos->set_notas( 'ALGO Autos' ).
CALL METHOD lo_motos->set_notas( 'ALGO 2 Motos').


WRITE 'TIPO:'.
CALL METHOD lo_autos->obtener_tipo( ).
NEW-LINE.
WRITE 'MARCA:'.
CALL METHOD lo_autos->obtener_marca( ).
NEW-LINE.
WRITE 'NOTAS:'.
CALL METHOD lo_autos->ver_notas( ).
NEW-LINE.
WRITE 'PUERTAS:'.
CALL METHOD lo_autos->cantidad_puertas( ).
NEW-LINE.
WRITE '**************'.
NEW-LINE.
WRITE 'TIPO:'.
CALL METHOD lo_motos->obtener_tipo( ).
NEW-LINE.
WRITE 'MARCA:'.
CALL METHOD lo_motos->obtener_marca( ).
NEW-LINE.
WRITE 'NOTAS:'.
CALL METHOD lo_motos->ver_notas( ).

Esto nos muestra como resultado:

El código completo:

*&---------------------------------------------------------------------*
*& Report ZPOLYTEST2
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT ZPOLYTEST2.

"Ejemplo de encapsulamiento, herencia y polimorfismo en ABAP.

"Definimos una clase abstracta, podriamos llamarla una plantilla
"para las clases heredadas. Cuando definimos una clase como abstracta
"no tiene implementacion.
CLASS lcl_vehiculo DEFINITION ABSTRACT.

  PUBLIC SECTION.
    METHODS: obtener_tipo ABSTRACT,
             obtener_marca ABSTRACT,
             set_notas ABSTRACT
              IMPORTING notas TYPE string,
             ver_notas ABSTRACT
              EXPORTING notas TYPE string.

  PROTECTED SECTION.
    DATA: lv_notas TYPE string.

ENDCLASS.

***********************************************************************
"Definimos una clase lcl_auto que hereda lcl_vehiculo y redefine los
"metodos de la clase padre (lcl_vehiculo).
CLASS lcl_auto DEFINITION
               INHERITING FROM lcl_vehiculo.

  PUBLIC SECTION.
    "Los metodos con REDEFINITION son los originales de lcl_vehiculo.
    "Como podemos ver podemos agregar nuestros propios metodos
    "como por ejemplo cantidad_puertas que pertenece solo a esta clase.
    METHODS: obtener_tipo REDEFINITION,
             obtener_marca REDEFINITION,
             set_notas REDEFINITION,
             ver_notas REDEFINITION,
             cantidad_puertas.

ENDCLASS.

"Implementamos la clase lcl_auto con sus metodos.
CLASS lcl_auto IMPLEMENTATION.

  METHOD obtener_tipo.
    WRITE 'AUTO'.
  ENDMETHOD.

  METHOD obtener_marca.
    WRITE 'FORD'.
  ENDMETHOD.

  METHOD set_notas.
    lv_notas = notas.
  ENDMETHOD.

  METHOD ver_notas.
    WRITE lv_notas.
  ENDMETHOD.

  METHOD cantidad_puertas.
    WRITE '4'.
  ENDMETHOD.

ENDCLASS.

***********************************************************************
"Definimos una clase lcl_moto que hereda lcl_vehiculo y redefine los
"metodos de la clase padre (lcl_vehiculo).
CLASS lcl_moto DEFINITION
               INHERITING FROM lcl_vehiculo.

  PUBLIC SECTION.
    METHODS: obtener_tipo REDEFINITION,
             obtener_marca REDEFINITION,
             set_notas REDEFINITION,
             ver_notas REDEFINITION.

ENDCLASS.

"Implementamos la clase lcl_moto con sus metodos.
CLASS lcl_moto IMPLEMENTATION.

  METHOD obtener_tipo.
    WRITE 'MOTO'.
  ENDMETHOD.

  METHOD obtener_marca.
    WRITE 'HONDA'.
  ENDMETHOD.

  METHOD set_notas.
    lv_notas = notas.
  ENDMETHOD.

  METHOD ver_notas.
    WRITE lv_notas.
  ENDMETHOD.

ENDCLASS.

***********************************************************************
START-OF-SELECTION.

"Declaramos los objetos.
DATA: lo_autos TYPE REF TO lcl_auto,
      lo_motos TYPE REF TO lcl_moto.

"Creamos los objetos.
CREATE OBJECT lo_autos.
CREATE OBJECT lo_motos.

"Llamamos a los metodos que setean las notas.
CALL METHOD lo_autos->set_notas( 'ALGO Autos' ).
CALL METHOD lo_motos->set_notas( 'ALGO 2 Motos').

"Llamamos a los metodos que muestran la informacion.
WRITE 'TIPO:'.
CALL METHOD lo_autos->obtener_tipo( ).
NEW-LINE.
WRITE 'MARCA:'.
CALL METHOD lo_autos->obtener_marca( ).
NEW-LINE.
WRITE 'NOTAS:'.
CALL METHOD lo_autos->ver_notas( ).
NEW-LINE.
WRITE 'PUERTAS:'.
CALL METHOD lo_autos->cantidad_puertas( ).
NEW-LINE.
WRITE '**************'.
NEW-LINE.
WRITE 'TIPO:'.
CALL METHOD lo_motos->obtener_tipo( ).
NEW-LINE.
WRITE 'MARCA:'.
CALL METHOD lo_motos->obtener_marca( ).
NEW-LINE.
WRITE 'NOTAS:'.
CALL METHOD lo_motos->ver_notas( ).

Simple ALV en ABAP

Les comparto una forma de hacer un ALV muy rápido que incluye varias opciones útiles.

Lo primero que tenemos que hacer es crear un reporte, en este caso lo llamaremos ZPRUEBA_ALV.

Con el reporte ya creado, hacemos un select simple como para tener datos para mostrar:

Por ejemplo:

SELECT * FROM sflight
  INTO TABLE @DATA(lt_sflight)
  UP TO 1000 ROWS.
IF sy-subrc = 0.

ENDIF.

Con lo cual nos quedaría de la siguiente forma:

Paso siguiente creamos el código que nos interesa para mostrar el ALV:

Primero declaramos los siguientes objetos:

DATA: lo_table     TYPE REF TO cl_salv_table,
      lo_functions TYPE REF TO cl_salv_functions_list,
      lo_columns   TYPE REF TO cl_salv_columns_table.

Estos objetos usan clases (las que están después de TYPE REF TO) para poder usar el ALV.

Dentro del IF que valida si encontró datos el SELECT agregamos el siguiente código:

  cl_salv_table=>factory( IMPORTING r_salv_table = lo_table
                          CHANGING t_table = lt_sflight ).

  lo_columns = lo_table->get_columns( ).
  lo_columns->set_optimize( abap_true ).
  lo_functions = lo_table->get_functions( ).
  lo_functions->set_all( ).

  lo_table->set_screen_status(
              pfstatus = 'ZSTATUS_GUI'
              report = sy-repid
              set_functions = lo_table->c_functions_all ).

  lo_table->display( ).

La clase cl_salv_table es una clase que contiene métodos relacionados a los ALV, con el método factory lo que hacemos es obtener una nueva instancia del ALV, pasando como parámetros lo_table, que es el que declaramos previamente, y los datos a mostrar (lt_sflight) que es donde guardamos el resultado del SELECT.

lo_columns = lo_table->get_columns( ). Con esta sentencia le indicamos que obtenga las columnas.

lo_columns->set_optimize( abap_true ). Con esta sentencia le indicamos que el ALV ajuste automáticamente el tamaño de las columnas.

lo_functions->set_all( ). Usamos este método para activar todas las funciones genéricas del ALV.

Con la sentencia lo_table->set_screen_status, lo que hacemos es especificarle un STATUS (que crearemos en breve).

Finalmente con el método lo_table->display( ). Mostramos el ALV.

Ahora agregamos el STATUS.

Para esto desde la se38 podemos hacer click en el siguiente icono para abrir el árbol de objetos:

En la ventana de objetos hacemos click derecho Create–>GUI STATUS:

Completamos la pantalla que nos aparece y le damos OK:

Nos aparecerá la siguiente pantalla:

Vamos al menu Extras–>Adjust Template:

Nos aparecerá otra ventana en donde seleccionamos “List Viewer” y le damos OK

Haciendo esto se nos completara todo lo que necesitamos, todos lo que nos resta es activar el GUI STATUS.

El código completo quedaría así:

REPORT zprueba_alv.

DATA: lo_table     TYPE REF TO cl_salv_table,
      lo_functions TYPE REF TO cl_salv_functions_list,
      lo_columns   TYPE REF TO cl_salv_columns_table.

SELECT * FROM sflight
  INTO TABLE @DATA(lt_sflight)
  UP TO 1000 ROWS.
IF sy-subrc = 0.

  cl_salv_table=>factory( IMPORTING r_salv_table = lo_table
                          CHANGING t_table = lt_sflight ).

  lo_columns = lo_table->get_columns( ).
  lo_columns->set_optimize( abap_true ).
  lo_functions = lo_table->get_functions( ).
  lo_functions->set_all( ).

  lo_table->set_screen_status(
              pfstatus = 'ZSTATUS_GUI'
              report = sy-repid
              set_functions = lo_table->c_functions_all ).

  lo_table->display( ).

ENDIF.

Finalmente si ejecutamos nuestro reporte vemos lo siguiente:

Si por ejemplo queremos bajar el listado a un archivo .XLSX, hacemos click en el siguiente botón y seleccionamos como aparece en la pantalla.

Bienvenidos a mi blog

Buenas a quien sea que esta leyendo esto!

Este sera mi blog donde hare posting de diferentes tipo de cosas, desde tecnologia, programacion hasta juegos probablemente.

Para los que quieran saber un poco mas sobre mi, soy de Argentina, tengo 38 años y trabajo en IT (con SAP), entre mis intereses estan también programación en C/C++/C#, assembler. Tambien me interesa lo relacionado a Information Security (Mas que nada análisis de malware), Astrofotografía y gaming 😀