lunes, 27 de agosto de 2018

READ TABLE .... BINARY SEARCH

BINARY SEARCH es una opción de la instrucción READ TABLE que realiza la búsqueda de un registro en concreto en una tabla interna utilizando un algoritmo de búsqueda binario. Los algoritmos de búsqueda binarios son mucho  más rápidos y eficientes que la búsqueda secuenciales. Pero, si utilizáis la opción BINARY SEARCH y no ordenáis correctamente la tabla,  al ejecutar el programa es  posible que no encuentre el registro en la tabla interna, aunque utilizando el debugger comprobereis que si que existe el registro que buscamos en la tabla interna.

En esta entrada del blog. Explicaremos la diferencia entre la búsqueda secuencial y la búsqueda binaria con la instrucción READ TABLE  y porque es necesario asegurarnos de que la tabla interna esta ordenada antes de realizar una búsqueda binaria.

READ TABLE: BÚSQUEDA SECUENCIAL

La instrucción READ TABLE busca un registro en la tabla interna especificada que cumpla una condición dada. Por defecto, a menos que la tabla interna sea de tipo SORTED o HASHED, la búsqueda del registro se realiza secuencialmente. Una búsqueda secuencial consiste en leer cada registro de la tabla de forma lineal, desde el primer registro al ultimo o  hasta encontrar un registro que cumpla la condición de la busqueda.

Búsqueda secuencial en una tabla interna

Da igual que ordenemos la tabla antes, la búsqueda sera siempre por defecto secuencial o lineal. Lo bueno de la búsqueda secuencial es que es muy simple de usar y siempre funciona. Por contra, como podéis ver en la imagen siguiente, es el método de búsqueda  que más tiempo consume.

Tiempos de acceso a una tabla interna con 100.000 registros


READ TABLE: BÚSQUEDA BINARIA

Una forma de acelerar la búsqueda es añadir la opción BINARY SEARCH a la instrucción READ TABLE. Esta opción realiza una búsqueda en la tabla interna utilizando un algoritmo de búsqueda binaria. Una búsqueda binaria comenzará examinando el registro central de la tabla interna. Si el registro es el que buscamos, hemos terminado. Si no es el registro correcto, podemos utilizar la naturaleza ordenada de la tabla para eliminar la mitad de los registros restantes. Si el registro que buscamos es mayor que el registro central, sabemos que no hace falta buscar en los registros anteriores al registro central. El registro que buscamos, si es que está en la tabla interna, debe estar en la mitad superior al registro central, ya que este es menor que el registro buscado. Podemos entonces repetir el proceso con la mitad superior. Buscamos el registro central de la parte superior y lo comparamos con el valor que estamos buscando. Una vez más, o lo encontramos o dividimos la lista por la mitad, eliminando por tanto otra gran parte de nuestro espacio de búsqueda posible.

Búsqueda binaria del registro con valor 20

Es importante ordenar la tabla antes por los campos que utilizamos para la búsqueda. Si no ordenamos la tabla antes de ejecutar la búsqueda binaria es posible que aunque el registro que buscamos exista en la tabla interna, nunca lo encuentre.

Por ejemplo, el siguiente código,  ordena la tabla antes de ejecutar la búsqueda binaria. 

Búsqueda binaria ordenando antes la tabla interna


El proceso de búsqueda seria:
  1. Busca el registro medio de la tabla y compara el valor
  2. El valor del registro 9 es mayor que el valor buscado
  3. Busca el registro medio entre los registros 0 y 9.
  4. El valor del registro 4 es mayor que el valor buscado
  5. Busca el registro medio entre los registros 0 y 4
  6. El valor del registro 2 es igual al valor buscado
  7. EL puntero <FS_KUNNR> apuntara al registro 2 de la tabla TI_KUNNR y retorna sy-subrc = 0.

En cambio, si no ordenamos la tabla antes, no encontrara nunca el registro.

Búsqueda binaria SIN ordenar antes la tabla interna



El proceso de búsqueda seria:
  1. Busca el registro medio de la tabla y compara el valor
  2. El valor del registro 9 es mayor que el valor buscado
  3. Busca el registro medio entre los registros 0 y 9.
  4. El valor del registro 4 es mayor que el valor buscado....
  5. Repite la búsqueda sin encontrarlo nunca porque ya no estan entre los registros seleccionados
  6. retorna sy-subrc = 4.

CONCLUSIÓN

 Recordar siempre ordenar la tabla interna antes de usar  READ TABLE...BINARY SEARCH.


¿Y que pasa si  la tabla interna es SORTED?

Si estamos utilizando una tabla interna de tipo sorted. No hace falta indicar la opción BINARY SEARCH. Si utilizamos parte o todos los campos que forman la clave de la tabla interna, la búsqueda sera binaria por defecto. En cambio, si utilizamos campos de la tabla interna que no pertenecen a la clave principal, la búsqueda sera  siempre secuencial.


¿ Y las tablas HASHED?

Nunca se utilizan búsquedas binarias con tablas interna de tipo HASHED. Con las tablas hashed  se accede a cada registro de la tabla con una clave hash. No es necesario utilizar búsquedas binarias porque cualquier búsqueda utilizando tablas hashed, es mucho más rápido que utilizando búsquedas binarias.

PROGRAMA DE EJEMPLO

En el repositorio de GITHUB del blog os dejo un programa para ilustrar el caso:

Programa ZZREAD_TABLE_BINARY.

  • El primer READ TABLE es secuencial.
  • EL segundo READ TABLE es binario sin ordenar la tabla.
  • El tercer READ TABLE es binario y ordena la tabla antes.

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

lunes, 11 de septiembre de 2017

ABAP CDS: Introducción

“ Las aplicaciones van y vienen, pero los datos viven para siempre “
Cameron O’Rourke –ORACLE

El modelo entidad/relación es y a sido una de las mejores forma de de representar la estructura de la base de datos, las tablas implicadas en cada consulta SQL y sus relaciones. Un buen programador, antes de empezar a escribir código, dedica un tiempo en modelar el programa y a analizar a que datos va ha acceder y como hacerlo de la manera más optima.

Diagrama entidad/relación de varias tablas de SAP

Yo personalmente, utilizo los diagramas de entidad/relación para planificar y optimizar mis consultas SQL. Una vez tengo terminado los diagramas, implemento el diagrama en el código de dos formas  diferentes:

  • Utilizando la transacción SE11 para crear las tablas, vistas, estructuras, elementos de datos, etc… 
  • Directamente combinando sentencias SQL simples o mas complejas con los operadores INNER JOIN o LEFT OUTER JOIN.


Si utilizamos la transacción SE11, los objetos que creamos en el diccionario podemos reutilizarlos en diferentes aplicaciones. Si utilizamos consultas SQL directamente en el código, pues solamente se podrán usar locamente en el programa que las implementemos. Todo dependerá de si queremos reutilizar las consulta SQL o no.

Modelo de datos y sus posibles implementaciones

En los siguientes artículos nos centraremos en como podemos implementar nuestros modelos de datos en SAP con la ayuda de las CDS para después consumirlos desde  nuestro código ABAP o Fiori.

¿QUE ES ‘CORE DATA SERVICES’?


Es una infraestructura creada por SAP para definir y consumir modelos de datos persistentes en SAP HANA. Utiliza un lenguaje de definición de datos (DDL), un lenguaje de consulta (QL) y un lenguaje de control de datos (DCL) . CDS es independiente de la BD utilizada, aunque actualmente solamente lo soporta SAP HANA.

especificación Core Data Services

El objetivo de las vistas CDS es facilitar la implementación de los modelos de datos desarrollados en la fase de diseño. Las vistas CDS nos permiten implementar los modelos de entidad-relación de una forma mucho más eficiente que las tradicionales vistas de la transacción SE11.

Modelo de datos, implementación y consumo con CDS

Originalmente, solamente era posible crear vistas CDS para SAP HANA y requería una conexión directa al servidor de HANA. Pero a partir de SAP NW ABAP 7.4 SP05, podemos crear vistas CDS desde el servidor de aplicación ABAP utilizando el IDE  Eclipse + plugin ADT.


'CODE TO DATA'  vs  'DATA TO CODE'

Las vista CDS son parte del nuevo paradigma de programación 'Code Push-Down' o 'Code To Data' que nos ha traído HANA . Este nuevo paradigma es completamente opuesto al tradicional paradigma 'Data to Code'. Tradicionalmente, en el paradigma 'Data to Code', como los accesos a la base de datos son lentos, se cargan todos los datos en las tablas internas, estructuras, variables, etc.... que se almacenan en la memoria del servidor de aplicación , mucho más rápida,  para después realizamos las operaciones o cálculos requeridos antes de mostrar los datos por pantalla.

Data to core  vs Code to data

En el nuevo paradigma 'Code Push-Down', podemos realizar operaciones y cálculos directamente en la capa de la base de datos, quedando la capa del servidor de aplicación libre para el tratamiento de los datos procesados. Esto es posible gracias a que la base de datos SAP HANA tiene un soporte para lenguajes procedurales directamente incluido en el kernel de la base de datos. 

Por ejemplo, gracias al paradigma 'Code Push-Down' con la nueva implementación de OPEN SQL para ABAP podemos incluir operaciones aritméticas dentro de la instrucción SELECT que se ejecutaran al nivel de la base de datos y liberando los recursos del servidor de aplicación. En posteriores veremos cómo aprovecharnos de estas nuevas funcionalidades.

Operaciones aritméticas directamente en la sentencia SQL que se ejecutan a nivel de base de dato


HANA CDS y ABAP CDS


Existen dos implementaciones diferentes de la misma especificación CDS: 

HANA CDS
  • Se crean directamente en la base de datos de SAP HANA 
  • Pueden consumirse externamente con servicios ODATA 
  • Para consumirlas desde una aplicación ABAP: 
  • Crear una vista externa en el DICC ABAP 
  • Enlazar la vista externa con la vista HANA CDS 
  • Consumir la vista externa utilizando sentencias OPEN SQL 
  • DOS órdenes de trasporte diferentes: 
  • La vista HANA CDS que se trasporta como una unidad de trasporte de HANA 
  • La vista externa que se trasporta con una orden de SAP (transacción STMS) 
HANA CDS, dos ordenes de trasporte para ABAP

ABAP CDS
  • Disponible desde SAP NW ABAP 7.4 SP05 
  • Se pueden definir y crear desde el servidor de aplicaciones ABAP con ECLIPSE + ADT 
  • Necesitamos de un servidor de aplicaciones ABAP para crearla 
  • Pueden consumirse externamente con servicios ODATA 
  • Son independientes de la base de datos 
  • Pueden utilizar tablas, vistas, elementos, etc…. definidos en el diccionario ABAP 
  • No requieren crear vistas externas 
  • Una única orden de trasporte estándar de SAP ( transacción STMS
ABAP CDS, una única orden de transporte

En esta serie de entradas sobre CDS nos centramos exclusivamente en vistas ABAP CDS.

ABAP CDS vs VISTAS SE11

¿Que ventajas tiene las vistas ABAP CDS sobre las vistas tradicionales del dicccinario que creamos desde la transacción SE11? Para empezar,  Las vistas creadas desde la transacción SE11 tienen limitaciones como que solamente podemos crear asociaciones de tipo INNER JOIN y que únicamente las vistas de mantenimiento son asociaciones de tipo LEFT OUTER JOIN.

En la siguiente tabla se muestran algunas de las características disponibles en las vistas ABAP CDS y que no se encuentran disponibles en las vistas de bases de datos tradicionales de la transacción SE11.

Algunas diferencias entre ABAP CDS y vistas de la transacción SE11
En el siguiente enlace podéis leer un listado completo de las características de ABAP CDS.


PASOS PARA CREAR Y CONSUMIR UNA VISTA ABAP CDS


Para comprobar si vuestro servidor de aplicación soporta las vistas ABAP CDS,  conectaros con el cliente SAPGUI y comprobar el nivel del módulo SAP_BASIS:
  • En el menú superior Sistemas -> STATUS -> Info Componentes
  • Comprobar que el nivel del módulo SAP_BASIS es 7.40 SP05 o superior.
Nivel módulo SAP_BASIS

Para crear vistas ABAP CDS necesitais tener instalado el IDE Eclipse y el plugin ABAP Development Tools y como hemos dicho conexión a un servidor de aplicación con SAP NW ABAP 7.4 SP05 o superior.

Pasos para crear la vista ABAP CDS:

1. Seleccionar el proyecto ABAP donde queremos crear la vista ABAP CDS
2. Seleccionar el paquete donde queremos crear la vista CDS.
    Botón derecho del ratón -> New -> Other ABAP Repository Object

3. Seleccionar el objeto"Data Definition" dentro de la carpeta "Core Data Services". 


Nota: Si estáis trabajando con un componente SAP_BASIS inferior a la versión 7.5 tenéis que seleccionar el objeto "Data Definition" o "DLL Source" dentro de la carpeta  "Diccionary".

4. Rellenar el nombre de la vista ABAP CDS y la descripción


5. Seleccionar o crear la orden de trasporte.

6. Por defecto tenemos varias plantillas disponibles para crear las vistas ABAP CDS.
    Seleccionamos la plantilla "Define view" y pulsamos finalizar.


7. Se abrirá una nueva ventana con el siguiente código.

vista ABAP CDS

8. Para implementar la vista ABAP CDS:
  1. Cambiar el texto "sql_view_name" por el nombre que tendrá la vista en el diccionario ABAP. IMPORTANTE: El nombre de la vista en el diccionario ABAP no puede ser el mismo que el nombre que hemos dado a la vista ABAP CDS en el paso 4.
  2. Reemplazar "data_source_name" por el nombre de la tabla de la base de datos 
  3. Para referenciar los campos se utiliza el punto (‘.’) en vez de ('~')
  4. Las columnas que recuperamos en la consulta se definen dentro de las llaves {}.
  5. Después de las llaves {} podemos incluir la cláusula WHERE.
  6. Los comentarios empiezan por // para una línea o /*   */ para un bloque

9. Grabar y activar.


En el diccionario ABAP las vistas ABAP CDS se denominan Vistas SQL DDL. Una vez activada la vista en Eclipse ya podemos ver nuestra vista desde la transacción SE11 y ejecutarla y acceder a su definición en SQL desde el menú Detalles -> ABAP Create, pero no se puede modificar la vista ABAP CDS desde el diccionario ABAP, solamente desde ECLIPSE.

Vista ABAP CDS desde la transacción SE11

Desde nuestras aplicaciones ABAP, las vistas ABAP CDS pueden consumirse con sentencias OPEN SQL como cualquier vista o tabla del diccionario ABAP. También pueden utilizarse como tipos para la definición de tablas internas, estructuras y variables de los programas ABAP. Debemos utilizar el nombre que hemos especificado en el atributo sqlViewName y no el nombre de la vista ABAP CDS.

Consumición de la vista ABAP CDS 

COMO ENCONTRAR LAS VISTAS ABAP CDS CREADAS EN EL SISTEMA SAP

Consultando tabla TADIR, con los siguientes parámetros de selección, podemos encontrar todas las vistas CDS existentes en SAP, su nombre en el diccionario ABAP y el paquete al que pertenecen.

Acceder a la tabla TADIR con los siguientes parámetros:
  • PGMID = ‘R3TR’ 
  • OBJECT =’DDLS’ 
Accediendo la tabla DDLDEPENDENCY podemos obtener los nombres de la vista en el diccionario ABAP y en SAP HANA. Acceder a la tabla DDLDEPENDENCY con el parámetro:

  • OBJECTTYPE = 'VIEW'



Entradas de la tabla TADIR correspondientes a vistas CDS

Nombre de la vista ABAP CDS en HANA y en el diccionario ABAP


Fuente:

jueves, 6 de octubre de 2016

Misión Cumplida!!!

Los nervios casi me destrozan, pero misión completada!! Termine mi Máster universitario en investigación en ingeniería de software y sistemas informáticos!! 


lunes, 3 de octubre de 2016

Defensa del trabajo de fin de Máster

Mañana me toca viajar a Madrid a defender mi TFM
"Generación automática de código ABAP para sistemas SAP R3"

Un trabajo con el que me he introducido en las arquitecturas dirigidas a modelos (MDA) y la creación de modelos que generen automáticamente el código utilizando Eclipse Modeling Tools y Acceleo.

...que nervios...



jueves, 2 de junio de 2016

Aun seguimos aqui

Perdón por la inactividad  del blog.

No he desaparecido. Y sí, voy a continuar escribiendo pero este año esta siendo complicado.
Es mi ultimo año de Master por la UNED y entre el máster y el trabajo me queda poco tiempo para encargarme del blog.

Lo dicho, perdonar la inactividad.
En breves volveremos.

Un saludo a todos.

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