El Arte de Programar SAP NetWeaver

Para mi esposa Milly y mi hija Tammy Luz. Sobre el Autor… Alvaro Tejada Galindo se inició en el mundo de la programaci

Views 72 Downloads 7 File size 7MB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

Para mi esposa Milly y mi hija Tammy Luz.

Sobre el Autor… Alvaro Tejada Galindo se inició en el mundo de la programación a los 20 años. Sus primeros lenguajes fueron C++ y Visual Basic 6.0 lo cuales aprendió de manera autodidacta gracias a libros, foros, tutoriales y códigos fuente. A lo largo de los años, fue agregando nuevos lenguajes a su colección, tales como Java, QBasic, Pascal, Delphi, HTML y JavaScript. Fue cuando tenía 24 años, que conoció el mundo del SAP, puesto que entró a hacer sus prácticas en TSNet Global, en donde trabajó por espacio de 2 años, con grandes proyectos en empresas como Telefónica del Perú, Carsa, Minsur, Alicorp y Funsur. Luego de dejar TSNet Global (Consultora Peruana), se dio un descanso para continuar estudiando y dedicarse al aprendizaje de PHP. Luego de esto, trabajó durante 2 años en la Consultora ActualiSap (Consultora Chilena), en donde además de dos exitosos proyectos de implementación en Suez Enegy Perú y el Aeropuerto Jorge Chávez, dictó un curso interno de IDoc’s en Santiago de Chile. Tuvo un breve paso por Servisoft (Consultora Peruana) en el proyecto de E. Wong. Con Stefanini IT Solutions trabajó para Alicorp, Exsa, Votorantim Metais y el Banco de Crédito del Perú.

2

Su experiencia en SAP y ABAP es de 5 años a la fecha, en los cuales siempre ha buscado obtener lo mejor del sistema. Adicionalmente, es uno de los Bloggers o Columnistas del SAP Developer Network o SDN (http://sdn.sap.com) en donde escribe artículos sobre ABAP, HR, Integración de PHP con SAP e Integración de Ruby con SAP. Además, es Moderador en los foros del SDN y Mentor de SAP para América Latina y el resto del mundo. Pueden leer sus artículos en la siguiente dirección: http://tinyurl.com/jnlfd http://atejada.blogspot.com

3

Indice Conociendo el entorno SAP NetWeaver Introducción Ingresando al sistema Conociendo las transacciones más importantes El Menú de SAP NetWeaver Diccionario de Datos Introducción Elementos del Diccionario de Datos Creando una tabla Creando un dominio Creando un elemento de datos Creando una vista de actualización Creando una ayuda de búsqueda Creando una estructura Creando una Vista Programación en ABAP Introducción Estructura de un programa ABAP Declaración de variables y tablas internas Selección de datos Lectura de datos en tablas internas Operadores de comparación Operaciones en tablas internas Copiar tablas internas Ordenar tablas internas Estructuras de Control Trabajando con Cadenas de Texto Variables de Sistema Modularización de programas Depuración de programas Programas de ejemplo SapScript Introducción Creando un formulario Crear una página principal

7 7 8 12 21 23 23 23 26 37 38 50 60 65 66 70 70 71 78 83 86 89 98 104 108 110 111 119 121 129 138 159 159 159 161 4

Crear ventana en página Crear párrafo por defecto Creando un programa de impresión Diseñando el formulario Ejecutando el formulario Debugger en SAPScript SmartForms Introducción Creando un estilo Creando un formulario Creando un programa de impresión Ejecutando el formulario Crear una tabla Screen Painter y Menu Painter Introducción Screen Painter Controles del Screen Painter Ejemplo de Screen Painter Menu Painter Agregando componentes Programación avanzada en Dynpros MatchCodes dinámicos Eliminar registros en un Table Control Escritura/Lectura en un Table Control Trabajando con subScreens Utilizando listas desplegables Leyendo datos de un Dynpro Módulos de Función y BAPIS Introducción Módulos de Función Creando nuestra primera función Llamando funciones desde un programa Introducción BAPIS ALV (ABAP List Viewer) Introducción Creando un ALV Agregando una cabecera al reporte Eventos ALV Pintemos con colores Barra de menú en ALV ABAP Orientado a Objetos

163 166 168 175 182 185 187 187 187 191 196 200 202 210 210 210 213 215 228 235 240 240 246 249 259 272 278 280 280 280 288 290 292 292 292 301 308 313 320 328 5

Introducción ¿Qué es la Orientación a Objetos? Conceptos básicos de POO Como programar en ABAP Objects Componentes Orientados a Objetos Crear un ALV Grid OO Agregar validaciones y eventos Crear un ALV Tree OO Agregar validaciones y eventos Crear un ALV Object Model Agregar validaciones y eventos Cargar Imágenes en Dynpros Leer PDF’s Comprimir (zip) archivos Crear un Control de Texto WebDynpro Introducción Creando nuestro primer WebDynpro BSP Introducción Creando nuestro primer BSP ABAP y XML Scripting in a Box SAPLink Integración PHP-NetWeaver Introducción Instalando el SAPRFC Comunicándonos con NetWeaver Integración Ruby-NetWeaver Introducción Instalando el SAP:Rfc Comunicándonos con NetWeaver Donde conseguir el SAP NetWeaver Sneak Preview Bibliografía y agradecimientos Enlaces Web

328 328 329 335 351 351 365 379 399 409 416 421 430 438 451 458 458 458 489 489 489 501 518 521 525 525 526 527 539 539 540 540 546 547 549

6

Conociendo el entorno SAP NetWeaver Introducción NetWeaver es la evolución del SAP R/3 que es un ERP (Enterprise Resource Planning – Planificador de Recursos Empresariales). ¿Porque llamamos a NetWeaver una evolución del R/3? Pues porque NetWeaver incorpora todos los aspectos de la programación orientada a objetos, así como una fuerte integración web. Al decir que se incorporan todos los aspectos de la programación orientada a objetos, nos referimos a que el ABAP (Advanced Business Application Programming) ha evolucionado también, proveyendo herramientas de desarrollo que aumentan la productividad. A lo largo de este libro, vamos a trabajar con SAP NetWeaver 7.0 Trial Version al que llamaremos NSP (NetWeaver Sneak Preview), que no es más que una versión reducida del NetWeaver que nos permite tomar ventaja de todos los componentes de desarrollo del sistema. Para poder tener un mejor aprendizaje de los conceptos que se explican en el libro, vamos a crear dos tablas de base de datos muy sencillas y las mismas serán utilizadas para todos los ejemplos.

7

Ingresando al Sistema Para poder ingresar a NSP, deberemos contar con un usuario y password, proporcionados por el administrador del sistema. En nuestro caso, tenemos 2 usuarios que comparten un mismo password. •

SAP* Æ Super Usuario. Con este usuario podemos crear nuevos usuarios en NSP.



BCUSER Æ Usuario Desarrollador. Con este usuario podemos programar en NSP.



DDIC Æ Usuario de Diccionario de Datos. Con este usuario, podemos acceder a los datos almacenados dentro del NSP.

En esta pantalla podemos ver el SAP Logon, que es donde se almacenan las entradas a los diferentes servidores de NSP. En su caso, ustedes solamente van a tener uno, así que los marcan (NSP Local) y presionamos Acceder al Sistema.

8

9

En esta ventana es donde ingresaremos nuestro usuario y password. En la caja de texto de Language (Idioma), solamente podremos ingresar EN Æ Inglés o DE Æ Alemán. El inglés es el idioma por defecto. Luego de haber ingresado al sistema, veremos la pantalla principal del NSP.

Esta es la pantalla de inicio, en donde podremos acceder a las transacciones que nos ofrece NSP. Las transacciones son códigos generalmente de 4 dígitos que sirven como accesos directos a los programas que se ejecutan internamente en el NSP. Para acceder, tenemos dos opciones, buscarlas en el menú del lado izquierdo.

10

O escribir directamente la transacción a la cual queremos dirigirnos.

11

Conociendo las transacciones más importantes 1.- SE38 (Editor ABAP) Este es el entorno de programación del NSP.

Aquí podemos crear nuestro programas o includes (Programas no ejecutables que se incluyen dentro de programas ejecutables).

12

2.- SE11 (Diccionario ABAP) En esta transacción podremos crear, visualizar o modificar Tablas, Vistas, Dominios, Estructuras y Ayudas para Búsqueda.

13

3.- SE16 (Browser de Datos) Es donde visualizamos los datos incluidos en Tablas o Vistas.

14

4.- SE71 (Form Painter) Nos permite crear formularios de Impresión.

15

Podemos definir páginas, ventanas, tipos de párrafo, márgenes, tabuladores, insertar imágenes. Por lo general se utilizan para generar Cartas de Pago a Bancos, Facturas, Cheques, Certificados. 5.- SmartForms (Form Painter Avanzado) Nos permite crear formularios de Impresión. El SmartForms es la nueva versión del SapScript. Se puede utilizar cualquiera de los dos, aunque depende de cada desarrollador.

16

6.- SE51 (Screen Painter) Nos permite diseñar pantallas para crear programas interactivos.

17

7.- SE41 (Menu Painter)

18

8.- SE37 (Function Builder) Nos permite crear funciones para utilizar en nuestros programas, así como modificar o visualizar funciones ya creadas.

19

20

El menú de SAP NetWeaver En todas las transacciones, contamos con una barra de menú, que nos permite interactuar con las aplicaciones de NetWeaver. Veamos cuales son:

Æ Equivale a hacer clic en la tecla Enter. Æ Aquí es donde se escribe la transacción a la cual queremos acceder. Si escribimos /n antes de la transacción, accederemos a ella en la misma ventana. Si escribimos /o antes de la transacción, accederemos a ella en una nueva ventana. Ej: /nSE38 ó /oSE16.

Æ Grabar. Æ Retroceder una pantalla. Æ Salir del programa o transacción. Æ Cancelar el programa o transacción. Æ Imprimir. Æ Buscar. Æ Buscar más.

21

Æ Se habilitan en Tablas y sirven para avanzar o retroceder registros. Æ Abre una nueva ventana o sesión del NetWeaver. Æ Crea un acceso directo en el escritorio. Æ Ayuda. Æ Configuraciones GUI.

22

Diccionario de Datos Introducción El Diccionario de Datos en NetWeaver, es el repositorio en el cual se almacenan todas las tablas, elementos de datos, dominios, estructuras, ayudas de búsqueda. Vamos a dar un breve repaso de todos los conceptos que intervienen en el diccionario de datos, así como la manera de crear cada uno de sus diferentes componentes. Cabe destacar, que en NetWeaver, todo es manejado por tablas, es decir, todos los programas, funciones, includes y elementos del diccionario son almacenados en tablas. Por lo tanto, NetWeaver cuenta con 63,348 tablas standard...Y eso que hablamos de la versión Sneak Preview, la versión real del NetWeaver debe tener por lo menos el doble o triple de tablas.

Elementos del Diccionario de Datos 1.- Tablas Las tablas se dividen en 3 tipos básicos: •

Tablas Transparentes (Transparent Tables): Posee una relación de uno a uno con una tabla de la Base de Datos. Es decir, cada tabla transparente definida en el Diccionario de Datos, existe físicamente en la base de datos. Es el tipo más común de tabla y es el más utilizado por los desarrolladores ABAP. 23



Tablas Reunidas (Pooled Tables): Posee una relación de muchos a uno con una tabla de la Base de Datos. Es decir, por una tabla que existe físicamente en la base de datos, existen muchas tablas en el Diccionario de Datos. Muchas tablas Pool, se encuentran almacenadas físicamente en la Base de Datos en tablas llamadas Pool Tables. Este tipo de tablas, son definidas por SAP.



Tablas Racimo (Cluster Tables): Una tabla racimo, es similar a una Pool Table. Poseen una relación de muchos a uno con una tabla de la Base de Datos. Muchas tablas racimo son almacenadas físicamente en la Base de Datos en tablas llamadas Table Cluster. Este tipo de tablas son definidas por SAP y su uso se limita a tablas que son accedidas constantemente, como las tablas del sistema.

2.- Componentes de una tabla Las tablas están compuestas por campos, y cada campo debe de estar asignado a un Elemento de Datos o a un Tipo Predefinido. Los Elementos de Datos, contienen los nombres del campo, así como también almacenan los valores de la ayuda en línea. Una definición de Elementos de Datos, requiere a su vez de un Dominio. Los Dominios, contienen las características técnicas de un campo, tales como su longitud y su tipo de dato.

24

Tipos de Datos para Dominios Tanto los Elementos de Datos como los Dominios, son reutilizables. Lo que significa que pueden estar definidos en más de una tabla, sin que esto genere algún tipo de conflicto.

Creación de Objetos del Diccionario Para acceder al Diccionario de Datos, deberemos ingresar a la transacción SE11.

25

En esta transacción podremos visualizar, modificar, eliminar o crear los siguientes elementos: •

Tablas Transparentes



Vistas



Estructuras



Dominios



Elementos de Datos



Ayudas para búsqueda

1.- Creando una tabla Para propósitos del libro, vamos a crear 2 tablas, llamadas ZLENGUAJES_PROG y ZENTORNOS_PROG. Con estas

26

tablas, vamos a trabajar todos los ejemplos del libro, así que es muy importante que las creen para poder seguir los ejemplos con mayor facilidad. Como se habrán dado cuenta, ambas tablas comienzan con el prefijo “Z”, puesto que es la única restricción que nos da NetWeaver al momento de crear cualquier elemento o componente. Para crear nuestra primera tabla, hacemos lo siguiente: •

Escribimos el nombre de la tabla que queremos crear.



Presionamos el botón Create.

En este momento, se nos presenta una ventana, en la cual, deberemos ingresar una descripción para la tabla, además de una clase de entrega y definir si la tabla puede ser mantenida desde la transacción SE16 o por algún programa externo.

27

Clase de Entrega Casi en un 99% de las veces, se utiliza la clase de entrega A, así que es la que vamos a utilizar nosotros. En todo caso, la única que podríamos utilizar además de esta, son los tipo C y L. También debemos escoger el tipo de Mantenimiento que se le va a dar a la tabla. En nuestro caso, escogeremos la opción Display/Maintenance Allowed para poder generar una Vista de Actualización más adelante. Cuando grabemos, nos encontraremos con una ventana muy común en NetWeaver. Esta ventana, nos pide asociar nuestra tabla a un Package (Paquete), que nos es más que una tabla donde se organizan los desarrollos por tipos. Tenemos dos opciones, o

28

utilizamos el paquete $TMP que es local y por lo tanto no transportable (Es decir, no puede salir del ambiente DEV de desarrollo), o podemos crear nuestro propio Package en donde almacenaremos todos los componentes que creemos en el libro. Obviamente, vamos a crear nuestro propio Package, así que debemos hacer lo siguiente: •

Abrimos una nueva ventana o sesión con /oSE80 (Object Navigator). Y escogemos la opción Package de la lista.



Con un clic derecho, abrimos un menú emergente y escogemos Create Æ Package.

29



Llenamos los campos y presionamos el botón Save.



NetWeaver nos solicita que ingresemos una Orden de Transporte

para

almacenar

nuestro

nuevo

Package,

presionamos el botón Create Request.

30



Ingresamos una descripción y grabamos.



Una vez creada y asignada la Orden de Transporte, presionamos el botón Continue o presionamos Enter.

31

Atributos del Package ZARTE_PROGRAMAR •

Regresamos a la sesión donde teníamos la tabla y hacemos un clic en el botón del parámetro Package.

32



Podemos ingresar el nombre del Package, o solicitar que se muestren todos los disponibles, presionando el botón Start Search o presionando la tecla Enter.



En nuestro caso, lo mejor es escribir Z* para que nos muestre solamente los paquetes creados por nosotros o por el sistema (Definidos para los usuarios).



Escogemos nuestro Package con un doble clic para que quede asignado a nuestra tabla.

33



Presionamos el botón Save. Y nos va a aparecer la ventana solicitando una Orden de Transporte. Como creamos una orden al momento de crear el Package, entonces la misma orden aparecerá por defecto. Presionamos el botón Continue o la tecla Enter.

Ahora, podemos continuar con la creación de nuestra tabla. Debemos ir a la pestaña Fields (Campos), para poder agregar los campos necesarios para nuestra tabla.

34

El primer campo que vamos a utilizar es el MANDT, que identifica al ambiente en el cual estamos trabajando (Y que es un campo llave). El segundo campo, se llamará Id, y será el encargado de identificar a cada uno de los registros (También es un campo llave).

Como se darán cuenta, en el gráfico no he llenado el campo Data Element (Elemento de Datos), para el campo Id. Esto es porque vamos a utilizar un Predefined Type (Tipo Predefinido). •

Hacemos clic en el botón Predefined Type. Y como podemos ver, la interfaz de la pantalla cambia un poco.

35



Queremos que el tipo de dato sea CHAR y tenga una longitud de 3, además, agregamos una pequeña descripción del campo.



El siguiente campo también necesita un tipo predefinido, así que lo llamamos Nombre y lo definimos como un CHAR de longitud 15.



El siguiente campo se llamará Entorno. Este estará relacionado

con

una

tabla

que

llamaremos

ZENTORNOS_PROG, así que abrimos otro modo para poder

crearla,

antes

de

continuar

con

la

tabla

ZLENGUAJES_PROG. •

Al igual que en la tabla ZLENGUAJES_PROG, los 2 primeros campos serán Mandt y Id.

36



El tercer campo, se llamará Nombre, y no tendrá asociado un Tipo Predefinido, sino que contará con un Elemento de Datos y un Dominio. Para esto, abrimos una nueva ventana en la SE11 y nos posicionamos en Domain (Dominio).

2.- Creando un Dominio

A

este

Dominio,

lo

llamaremos

ZD_ENT_NAME.

Llenamos los campos como se muestra en la imagen.

37

Lo grabamos y lo activamos utilizando el botón Activate (Activar)

o presionando Crtl + F3.

3.- Creando un Elemento de Datos •

Una vez creado el Dominio, pasamos a crear nuestro Data Element (Elemento de Datos). En la misma transacción, nos posicionamos en Data Type (Tipo de Dato).

Lo llamaremos ZE_ENT_NAME. Se dará cuenta, de que al momento de presionar el botón Create, aparece una ventana preguntándonos por el Tipo de Dato que queremos crear. Lo dejamos como Data Element y presionamos Enter. Llenamos los datos como se muestra en la figura, utilizando el Dominio que creamos.

38

Ahora, debemos pasar a la pestaña Field Label (Etiqueta de Campo), que no es más que la descripción del Elemento de Datos. La llenamos como se muestra en la figura. Grabamos y activamos.



Regresamos a la ventana donde estábamos creando la tabla ZENTORNO_PROG.



Como estábamos ingresando Tipos Predefinidos, debemos presionar el botón Data Element

. E

ingresar el nombre de nuestro Elemento de Datos.



Grabamos y ahora debemos llenar los datos de Technical Settings (Caraterísticas Técnicas) Æ Goto Æ Technical Settings o presionar Crtl + Shift + F9. Y luego debemos

39

llenar el Enhacement Category (Categoría de Ampliación) Æ Extras Æ Enhacement Category.

El campo Data Class (Clase de Datos) especifica el área física en la cual se va a crear la tabla. Para nosotros, el valor por defecto siempre será APPL0. El campo Size Category (Categoría de Tamaño), determina la cantidad de espacio que se reservará inicialmente para la tabla, en nuestro caso, nuestra tabla no contendrá mucho datos, así que 0 es la opción a tomar. Grabamos

y

retrocedemos

para

poder

acceder

al

Enhacement Category. 40

Esto sirve para definir si la tabla puede ser modificada con campos adicionales. En nuestro caso, le decimos que no, puesto que son tablas que hemos creado como ejemplo para el libro. Grabamos y activamos. •

Regresamos a nuestra tabla ZLENGUAJES_PROG y presionamos el botón Data Element, para poder ingresar nuestro Elemento de Datos para el campo Entorno.



Nos posicionamos sobre el campo Entorno y presionamos el botón Foreign Keys (Llaves Foráneas)

. Esto nos

mostrará una ventana, que veremos a continuación.

41

En el campo Check Table (Tabla de Verificación), escribimos el nombre de nuestra tabla ZENTORNOS_PROG. Y presionamos Enter.

El sistema nos propone crear una asignación entre las tablas, presionamos Yes (Sí) o presionamos Enter.

42

Recibimos este mensaje, porque la llave completa de la tabla ZENTORNOS_PROG

no

existe

en

la

tabla

ZLENGUAJES_PROG. Dejamos la ventana, como se muestra en la figura.



El ultimo campo, se llama CONEX_SAP y determina si el lenguaje posee algún tipo de conexión con SAP. Para esto,

43

vamos a crear nuevamente un Dominio (ZD_CONEX_SAP) y un Elemento de Datos (ZE_CONEX_SAP).

Como ven, es simplemente un CHAR de 1. Ahora, pasamos a la pestaña Value Range (Rango de Valores). Y llenamos solamente dos valores.

44



Grabamos, activamos y creamos nuestro Elemento de Datos.

Llenamos la pestaña Field Label (Etiqueta de Campo), grabamos y activamos.



De vuelta en la tabla ZLENGUAJES_PROG, agregamos el campo CONEX_SAP con su respectivo elemento de datos.



Llenamos la Características Técnicas, la Categoría de Ampliación, grabamos y activamos. 45

Ahora que tenemos nuestras dos tablas listas, es hora de agregar algunos datos. Nos vamos a la transacción SE16 (Browser de Datos). Colocamos el nombre de nuestra tabla de entornos, y presionamos el botón Create Entries (Crear Entradas)

o

presionamos F5.

Ingresamos algunos cuantos valores. Y grabamos con el botón Save (Guardar)

o presionamos Crtl. + S.

46

Una vez que hemos terminado de insertar registros, retrocedemos presionando el botón Back (Atrás)

o presionando el botón F3.

Para poder ver los registro que hemos creado, podemos presionar el botón Table Contents (Contenido de Tabla)

o presionar Enter.

En esta ventana, podemos hacer un filtro ya sea por Id o por Entorno. En nuestro caso, queremos ver todos los valores, así que dejamos esos campos en blanco. Presionamos el botón Execute (Ejecutar)

o presionamos F8.

47

Tenemos 3 registros grabados en la base de datos. Ahora, es el turno de la tabla ZLENGUAJES_PROG. Seguimos el mismo procedimiento.

Cuando se posicionen en el campo Entorno, se darán cuenta de algo interesante. Aparece un pequeño botón al final del campo, lo cual nos indica que existen valores de los cuales podemos escoger. Para esto debemos hacer clic en ese botón o presionar F4.

48

Esos

son

los

registros

que

ingresamos

en

la

tabla

ZENTORNOS_PROG y que ahora podemos insertar en nuestra tabla ZLENGUAJES_PROG. Lo mismo sucede con el campo CONEX_SAP. Cabe

destacar

que

los

valores

que

están

en

la

tabla

ZENTORNOS_PROG, son los únicos valores válidos, es decir, si ingresamos cualquier otro valor, el sistema nos mostrará un mensaje de error.

49

Ingresamos algunos datos y estamos listos.

Seguramente, les parecerá que ingresar los datos así, es un poco tedioso...No se preocupen, que ahora vamos a crear una vista de actualización. 3.- Creando una Vista de Actualización Para crear nuestra vista de actualización, debemos regresar a la transacción SE11 y modificar la tabla ZLENGUAJES_PROG. En el menú, vamos a Utilities (Utilidades) Æ Table Maintenance Generator (Generador de Mantenimiento de Tabla). Llenamos la ventana como se muestra a continuación.

50

Presionamos el botón Find Scr. Number(s) (Buscar Número(s) de Pantalla)

o presionamos Shift + F7.

En esta ventana, siempre escogemos la primera opción Propose Screen Number(s) (Proponer número(s) de Ventana).

51

Finalmente presionamos el botón Create (Crear)

o presionamos

F6. Grabamos y activamos. Ahora, debemos ir a la transacción SM30.

Y presionar el botón Maintain.

Se nos muestra una pantalla más amigable para el ingreso de datos, pero como se darán cuenta, los dos primeros campos aparecen como

52

“+”. Esto es porque al ser Tipos Predefinidos, no poseen un texto descriptivo. Esto lo podemos solucionar fácilmente regresando a la transacción SE11 y al Generador de Mantenimiento de Tabla.

Debemos hacer clic tanto en el Overview Screen (Ventana de Vista general) como en el Single Screen (Ventana sencilla). Veamos primero en el Overview Screen.

53

Seguramente esta pantalla los asusta un poco, pero no se preocupen, que por el momento no vamos a hacer nada con esto, puesto que es código generado automáticamente por el NetWeaver. Debemos hacer clic en el botón Layout (Disposición)

.

La pantalla del Screen Painter es la que nos interesa, sobre todos las cabeceras que tienen un “+”.

54

Debemos hacer un clic en el botón Display Change (Mostrar Cambiar)

o presionar F1.

Ahora, nos colocamos sobre la primer columna y en la ventana que dice Text (Texto), escribimos lo siguiente

Y en la segunda columna:

Grabamos, activamos y retrocedemos dos veces hasta regresar a la ventana del Generador de Mantenimiento de Tabla. Hacemos doble clic en Single Screen.

55

Y repetimos la operación, modificando los símbolos “+”. Grabamos, activamos y regresamos nuevamente. Una vez hecho esto, nos vamos a la transacción SM30 y veremos que los símbolos “+” han sido reemplazados por los textos correctos.

Ahora, para hacer las cosas más interesantes y poder trabajar mejor los ejemplos del libro, regresamos a la transacción SE11 para crear una nueva y última tabla con las siguientes características.

56

La tabla se llamará ZPROGRAMAS y contendrá algunos programas

que

hemos

hecho

utilizando

los

lenguajes

de

programación que hemos creado.

En otra ventana, creamos un dominio para el código del lenguaje de programación, llamado ZD_ID_LENGUAJE.

Ahora,

creamos

un

Elemento

de

Datos

llamado

ZE_ID_LENGUAJE.

57

Regresamos a la tabla ZPROGRAMAS y tendremos la siguiente estructura:

Para que esto funcione correctamente y podamos hacer una asociación

entre

ZLENGUAJES_PROG,

las

tablas debemos

ZPROGRAMAS modificar

la

y tabla

58

ZLENGUAJES_PROG incluyendo el Elemento de Datos que creamos:

Luego de haber grabado y activado, regresamos a ZPROGRAMAS y nos posicionamos en el campo Id y presionamos el botón Foreign Keys

.

59

Grabamos, actualizamos las Características Técnicas y la Categoría de Amplicación y activamos la tabla. Como solamente hemos asignado el campo Id a nuestra tabla, al momento de querer elegir un lenguaje de programación, solamente vamos a ver el código, lo cual no nos va a ayudar de mucho, así que hora de crear una ayuda de búsqueda. 4.- Creando una Ayuda de Búsqueda Para esto, en una nueva ventana, vamos a la transacción SE11. Y escogemos la opción Seach Help (Ayuda de búsqueda).

Elegimos la primera opción.

60

Como pueden ver, el campo Nombre tiene asignamos un elemento de datos, así que nuevamente, creamos un Dominio y un Elemento de Datos como se muestra a continuación.

61

Grabamos y activamos nuestra ayuda de búsqueda y la probamos con presionando el botón Test (Prueba)

o presionando F8.

62

En esta ventana, podemos filtrar por Id o por Nombre del lenguaje, en este caso, presionamos Enter porque queremos ver todos los registros disponibles.

Nuestra ayuda de búsqueda está terminada, así que regresamos a la tabla ZPROGRAMAS a la pestaña Entry Help/Check (Entrada de Ayuda/Verificación).

Nos posicionamos en el campo Id, y presionamos el botón Search Help

.

63

Asignamos la ayuda de búsqueda que creamos.

Grabamos y activamos. En la transacción SE16 agregamos algunos cuantos registros.

64

Como podemos ver, al hacer F4 en el campo Id, podremos ver tanto el código como el nombre del Lenguaje. Finalmente, nuestra tabla contendrá los siguientes registros.

Con esto terminamos y podemos crear una estructura, que no es otra caso que una tabla que solamente contiene una cabecera, es decir, no puede almacenar registros. Esto nos va a ser útil al momento de desarrollar nuestros programas, puesto que vamos a poder contar con la estructura sin utilizar memoria adicional de la base de datos. 5.- Creando una Estructura En la transacción SE11, en el campo Data type, creamos nuestra estructura llamada ZSLENGUAJES_PROG.

65

, se nos

Cuando presionamos Create (Crear)

muestra una ventana en donde debemos elegir Structure (Estructura).

Utilizamos

los

mismos

componentes

que

en

la

tabla

ZLENGUAJES_PROG, aunque quitamos el campo MANDT. Grabamos y activamos. Nos va a pedir, el Enhacement Category (Categoría de ampliación), lo agregamos para poder activar.

6.- Creando una Vista Dentro de la transacción SE11, creamos nuestra vista en el campo VIEW (Vista). Llamada ZVLENGUAJES_PROG.

66

En la ventana que aparece, elegimos Database view (Vista de Base de Datos). Los demás tipos no los vamos a ver en este libro, puesto que el Database view es el más utilizado.

Primero, debemos de llenar los campos que vamos a utilizar para relacionar las tablas que vamos a utilizar en la vista, en este caso, ZPROGRAMAS y ZLENGUAJES_PROG.

67

En la pestaña View Flds (Campos de la Vista). Definimos los campos que queremos que se muestren en la vista.

Grabamos y activamos. Los mensajes de advertencia, podemos obviarlos. Una vez que la Vista está activa, podemos comprobar los valores presionando el botón Contents (Contenidos)

o presionando Ctrl.

+ Shift + F10. Se darán cuenta de que el sistema no envía a la transacción SE16.

Ejecutamos y vemos los datos generados por la Vista.

68

Con esto, terminamos el capítulo dedicado a Diccionario de Datos. Ahora, ya pueden crear sus propias tablas, elementos de datos, dominios o vistas.

69

Programación en ABAP Introducción ABAP (Advances Business Application Programming), es el lenguaje de programación propietario de SAP AG, con el cual se desarrollan aplicaciones que son integradas al NetWeaver. Cabe de destacar que muchos de los componentes de NetWeaver han sido desarrollados utilizado ABAP, lo cual nos permite hacer modificaciones que otro tipo de sistemas serían imposibles. El ABAP, viene a ser una especie de nieto del COBOL (Common Object Business Oriented Language), que era muy utilizado para el desarrollo de aplicaciones empresariales. En cuanto a la sintaxis de lenguajes, podemos tomarlo como un híbrido entre COBOL, PASCAL y SQL Server. Hasta la versión 45B, el ABAP, era un lenguaje procedural, aunque con el tiempo se el agregaron funcionalidades para convertirlo en un lenguaje orientado a objetos, por lo cual al momento de programar, se pueden mezclar ambas tecnologías sin mayores problemas. El NetWeaver actualmente está en la versión 7.0.0, lo cual significa que nos permite trabajar con ABAP Objects de manera muy completa, aunque como es de suponerse, en versiones posteriores de NetWeaver, se adicionarán algunos componentes extras. En el presente capítulo, vamos a revisar los principales componentes del ABAP, así como la estructura de los programas que se crean con el.

70

Estructura de un programa en ABAP Para crear un programa en ABAP, debemos ingresar a la transacción SE38, y especificar el nombre del programa que queremos crear. Recodemos que debemos utilizar la letra Z antes del nombre del programa. Esto es porque SAP, reserva el nombre Z para los programas que son creados por los clientes y no por los mismos trabajadores de SAP.

Cuando presionamos el botón Create (Crear), el sistema nos mostrará la siguiente ventana, en donde debemos escoger el Type (Tipo de programa) y el Status (Estado) asociado.

71

En Type (Tipo) siempre escogemos Executable program (Programa ejecutable) y en Status (Estado), elegimos SAP Standard Production Program (Programa Standard SAP para Productivo). Al presionar el botón Save cual

queremos

asignar

, nos va a pedir el paquete al el

desarrollo,

elegimos

ZARTE_PROGRAMAR. Y cuando nos pida la orden de transporte, elegimos la creamos puesto que se nos muestra por defecto (Siempre y cuando no hayamos creado otras ordenes). El sistema, nos envía al Editor ABAP, que es donde vamos a poder crear nuestros programas. Debemos tener claro, que existen ciertos comentarios, que debemos colocar en todos nuestros programas, para poder definir algunos bloques importantes.

72

En el espacio de comentario, debemos incluir por ejemplo, quien está creando el programa y cuando.

La primera y única línea que nos muestra el editor es REPORT y el nombre de nuestro programa. Esto indica que se trata de un programa ejecutable. Para empezar, vamos a hacer un programa muy simple, que simplemente solicite al usuario un texto y lo imprima en pantalla, luego de esto, veremos la sintaxis de ABAP y podremos hacer programas más complejos.

73

*&----------------------------------------------------* *& Report

ZDUMMY_PRIMER_PROGRAMA

*

*&----------------------------------------------------* *& Creado por: Alvaro "Blag" Tejada Galindo.

*

*& Fecha creación: 14 de Noviembre del 2007

*

*&----------------------------------------------------* REPORT

ZDUMMY_PRIMER_PROGRAMA.

*=====================================================* * SELECTION-SCREEN

*

*=====================================================* SELECTION-SCREEN BEGIN OF BLOCK PRUEBA WITH FRAME TITLE TEXT-T01. PARAMETERS: TEXTO(30) TYPE C. SELECTION-SCREEN END OF BLOCK PRUEBA. *=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. WRITE: TEXTO.

El SELECTION-SCREEN BEGIN OF BLOCK, nos permite definir un espacio en donde van a ir los parámetros de entrada de nuestro programa. PRUEBA, es el nombre que le estamos asignando al bloque de parámetros. WITH FRAME TITLE, significa que el área de los parámetros de selección va a estar rodeados por un marco y TITLE, significa que va a contar con un título definido por nosotros, en este caso

74

TEXT-T01. TEXT-T01, lo podemos separar en dos partes TEXT, que nos indica que es un texto del sistema y T01, es el nombre de dicho texto. Para poder modificarlo, simplemente deberemos hacer doble clic en el texto. Si no lo hemos creado, nos aparecerá la siguiente ventana:

Simplemente, ingresamos el texto, grabamos y activamos. Dentro del bloque que hemos definido para los parámetros, podemos utilizar 2 tipos de parámetros: • PARAMETERS ÆSon parámetros simples que aceptan solamente un valor. • SELECT-OPTIONS ÆSon parámetros compuestos que aceptan un rango de valores. 75

El parámetro que hemos utilizado en este programa, es un CHAR de 30 caracteres. TEXTO(30) TYPE C. Si activamos y ejecutamos el reporte (Presionando la tecla F8), veremos el parámetro de entrada que definimos.

Ahora, si queremos cambiar el texto que muestra nuestro parámetro, deberemos de ingresar al siguiente menú. GotoÆText ElementsÆSelection Texts.

76

Aquí, deberemos de ingresar el texto que queremos que tenga nuestro parámetro, lo grabamos, lo activamos y listo.

Ahora, volvamos a ejecutar el reporte.

77

El START-OF-SELECTION, nos indica que va a comenzar la ejecución de nuestro programa, es aquí donde colocamos toda la lógica. WRITE: TEXTO, significa que vamos a escribir en la pantalla, el valor que hemos ingresado en el parámetro de entrada TEXTO.

Ese fue nuestro primer y más simple programa en ABAP.

Declaración de Variables y Tablas Internas Para poder declarar variables, utilizamos la sentencia DATA, que lo único que hace es separar un espacio en memoria. DATA: TEXTO TYPE C.

78

Aquí estamos diciendo que vamos a crear una variable llamada TEXTO, y que va a ser de tipo C (Caracter). Además, podemos especificar su tamaño. DATA: TEXTO(30) TYPE C. Entre los tipos de datos que nos ofrece el ABAP, tenemos: C

Character

1

Space

N

Numeric String

1

’00…0’

D

Date

8

‘000000000’

6

‘000000’

1

X’00’

(YYYYMMDD) T

Time (HHMMSS)

X

Byte (Hexadecimal)

I

Integer

4

0

P

Packed Integer

8

0

F

Floating point

8

‘0.0’

number STRING

String

Variable

Empty string

XSTRING

Byte Sequence

Variable

Empty X String

Adicionalmente, podemos utilizar campos de tablas para poder hacer la declaración de variables.

79

DATA: V_CARRID TYPE SPFLI-CARRID. En este caso, estamos declarando una variable que va a ser exactamente igual que el campo CARRID de la tabla SPFLI. Por lo tanto, V_CARRID es un CHAR de 3 caracteres. Ahora veamos las tablas internas, que son uno de los elementos más valiosos del ABAP. Las tablas internas, son tablas temporales que existen solamente en el ámbito del programa que las creó y permiten almacenar información para luego poder manipularla sin tener que acceder múltiples veces a la base de datos. En versiones anteriores, podíamos utilizar la siguiente sintaxis: DATA: BEGIN OF TABLA OCCURS 0, … END OF TABLA. Con la introducción de NetWeaver, esto no es posible, así que de ahora en adelante, vamos a utilizar y aprender solamente las nuevas sintaxis que se nos ofrecen gracias a la creación del ABAP Objects. TYPES: BEGIN OF TY_TABLA, … END OF TY_TABLA. DATA: T_TABLA TYPE STANDARD TABLE OF TY_TABLA. 80

Primero, debemos crear un TYPE, es decir, un tipo de tabla interna y luego, utilizando el DATA, creamos una tabla interna que haga referencia a nuestro tipo de tabla. Para los que ya conocen ABAP, se darán cuenta de que no hemos creado la tabla con una cabecera. Es decir, no utilizamos ni OCCURS 0, ni tampoco WITH HEADER LINE. Esto es porque, en ABAP Objects, está prohibido utilizar cabeceras o workareas. Para los que no conocen ABAP, en versiones anteriores, podiamos crear tablas internas con líneas de cabecera, lo cual facilitaba la lectura de datos, pero que al mismo tiempo ocasionaba problemas de performance. Es por eso, que SAP decició eliminar la cabeceras completamente. Además de crear tablas internas, de la manera que hemos visto, podemos también incluir estructuras completas de Base de Datos. Esto podemos hacerlo de dos maneras, dependiendo de si queremos o no incluir campos adicionales. DATA: T_SPFLI TYPE STANDARD TABLE OF SPFLI. TYPES: BEGIN OF TY_SPFLI. INCLUDE STRUCTURE SPFLI. TYPES: TEST TYPE STRING. TYPES: END OF TY_SPFLI. DATA: T_SPFLI TYPE STANDARD TABLE OF TY_SPFLI.

81

Claro, si queremos crear una tabla interna que tenga datos propios, lo hacemos de la siguiente forma. TYPES: BEGIN OF TY_TEST, NOMBRE(30) TYPE C, EDAD TYPE I, END OF TY_TEST. DATA: TEST TYPE STANDARD TABLE OF TY_TEST. Seguramente se habrán dado cuenta y sobre todo se preguntarán, porque tenemos que utilizar el TYPE STANDARD TABLE, muy simple, porque tenemos disponibles más tipos de tablas.

TYPES: BEGIN OF TY_TEST, ID(3) TYPE C, NOMBRE TYPE STRING, EDAD TYPE I, END OF TY_TEST. DATA: TEST TYPE STANDARD TABLE OF TY_TEST. DATA: TEST_H TYPE HASHED TABLE OF TY_TEST WITH UNIQUE KEY ID. DATA: TEST_S TYPE SORTED TABLE OF TY_TEST WITH UNIQUE KEY ID

Como pueden ver, tenemos 3 tablas internas.

82

TEST Æ Tablas Standard. Puede ser accedida mediante un índice o mediante campos. TEST_H Æ Tabla de tipo hashed. De rápido acceso, pero no puede ser accedida mediante un índice. TEST_S Æ Sorted table. De rápido acceso, siempre está ordenada, no puede ser accedida mediante un índice. En realidad, el uso de tablas Hashed o Sorted, depende del nivel de nivel de datos o de la complejidad del programa, en lo personal, solo he utilizado este tipo de tablas algunas cuantas veces en toda mi carrera.

Selección de Datos Al igual que en SQL, podemos utilizar la clásica sentencia SELECT, para poder seleccionar datos. Aunque en el caso de ABAP, tenemos mayor flexibilidad para poder almacenar los datos, ya sea en Variable o en Tablas internas. En Variables:

DATA: NOMBRE TYPE ZPROGRAMAS-NOM_PROG. SELECT SINGLE NOM_PROG INTO NOMBRE FROM ZPROGRAMAS WHERE ID_PROG EQ '001'.

83

Declaramos una variable llamada NOMBRE del tipo del campo NOM_PROG de la tabla ZPROGRAMAS. Hacemos un SELECT SINGLE para obtener un registro cuyo campo ID_PROG sea igual a 001. En Tablas internas:

TYPES: BEGIN OF TY_PROGRAMAS, NOM_PROG TYPE ZPROGRAMAS-NOM_PROG, END OF TY_PROGRAMAS. DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS. SELECT NOM_PROG INTO TABLE T_PROGRAMAS FROM ZPROGRAMAS.

En esta caso, creamos un TYPE, luego una tabla interna y finalmente leemos todas las instancias del campo NOM_PROG dentro de nuestra tabla interna. Claro, también podemos utilizar INNER JOINS para hacer nuestras consultas.

TYPES: BEGIN OF TY_PROGRAMAS, NOMBRE TYPE ZLENGUAJES_PROG-NOMBRE, ENTORNO TYPE ZLENGUAJES_PROG-ENTORNO, NOM_PROG TYPE ZPROGRAMAS-NOM_PROG, END OF TY_PROGRAMAS.

84

DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS. SELECT NOMBRE ENTORNO NOM_PROG INTO TABLE T_PROGRAMAS FROM ( ZLENGUAJES_PROG INNER JOIN ZPROGRAMAS ON ZLENGUAJES_PROG~ID = ZPROGRAMAS~ID ).

Ahora, supongamos que tenemos un campo más en nuestra tabla interna, pero no queremos seleccionarlo, entonces, el SELECT va a estar incompleto y los registros pueden guardarse donde no les corresponde. Esto lo podemos solucionar utilizando un INTO CORRESPONDING FIELDS, que lo que hace es almacenar los registros en los campos correspondientes, aunque claro, esto afecta el performance de nuestros programas, así que lo mejor es evitarlos.

TYPES: BEGIN OF TY_PROGRAMAS, NOMBRE TYPE ZLENGUAJES_PROG-NOMBRE, ENTORNO TYPE ZLENGUAJES_PROG-ENTORNO, CONEX_SAP TYPE ZLENGUAJES_PROG-CONEX_SAP, NOM_PROG TYPE ZPROGRAMAS-NOM_PROG, END OF TY_PROGRAMAS. DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS.

SELECT NOMBRE ENTORNO NOM_PROG INTO CORRESPONDING FIELDS OF TABLE T_PROGRAMAS FROM ( ZLENGUAJES_PROG INNER JOIN ZPROGRAMAS ON ZLENGUAJES_PROG~ID = ZPROGRAMAS~ID ).

85

Lectura de datos de Tablas Internas Una vez que tenemos datos en nuestras tablas internas, debemos de leerlas para poder hacer algo con ellas. Para eso, contamos con dos opciones. Aunque, antes de eso, debemos conocer un elemento muy importante, sin el cual no podríamos hacer mucho en ABAP. Estamos hablando de los Field-Symbols. Para los que han programado alguna vez en C++, los Fields-Symbols, son muy parecidos a los punteros, es decir, almacenas la dirección en memoria de una variable. Por lo general, los utilizamos para crear cabeceras de tablas internas. FIELD-SYMBOLS: LIKE LINE OF T_TABLA. Con esto, creamos una referencia a la tabla T_TABLA, la cual contiene únicamente una línea de cabecera, con lo cual ganamos mucho performance al hacer lecturas de tablas internas. • LOOP AT TYPES: BEGIN OF TY_PROGRAMAS, NOMBRE TYPE ZLENGUAJES_PROG-NOMBRE, ENTORNO TYPE ZLENGUAJES_PROG-ENTORNO, NOM_PROG TYPE ZPROGRAMAS-NOM_PROG, END OF TY_PROGRAMAS.

86

DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS. FIELD-SYMBOLS: LIKE LINE OF T_PROGRAMAS. SELECT NOMBRE ENTORNO NOM_PROG INTO TABLE T_PROGRAMAS FROM ( ZLENGUAJES_PROG INNER JOIN ZPROGRAMAS ON ZLENGUAJES_PROG~ID = ZPROGRAMAS~ID ). LOOP AT T_PROGRAMAS ASSIGNING . WRITE:/ -NOM_PROG. ENDLOOP.

Al hacer un LOOP AT, lo que hacemos es leer cada uno de los registros almacenados en nuestra tabla interna, y al asignar cada uno de estos registros a nuestro Field-Symbol, lo que estamos haciendo es pasar simplemente la cabecera de ese registro, por lo cual la lectura de la tablas es mucho más veloz. Finalmente, utilizando un WRITE imprimimos el contenido del campo NOM_PROG. El símbolo / nos sirve para dejar un espacio hacia abajo luego de haber impreso el valor (Equivales a hacer un ENTER).

87

• READ TABLE TYPES: BEGIN OF TY_PROGRAMAS, NOMBRE TYPE ZLENGUAJES_PROG-NOMBRE, ENTORNO TYPE ZLENGUAJES_PROG-ENTORNO, NOM_PROG TYPE ZPROGRAMAS-NOM_PROG, END OF TY_PROGRAMAS. DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS. FIELD-SYMBOLS: LIKE LINE OF T_PROGRAMAS. SELECT NOMBRE ENTORNO NOM_PROG INTO TABLE T_PROGRAMAS FROM ( ZLENGUAJES_PROG INNER JOIN ZPROGRAMAS ON ZLENGUAJES_PROG~ID = ZPROGRAMAS~ID ). READ TABLE T_PROGRAMAS INDEX 1 ASSIGNING . WRITE:/ -NOM_PROG. READ TABLE T_PROGRAMAS WITH KEY NOMBRE = 'PHP' ASSIGNING . WRITE:/ -NOM_PROG.

En este caso, al utilizar un READ TABLE, leemos un solo registro de nuestra tabla, como podemos ver, podemos utilizar

88

un Indice o también un Campo para leer el contenido y asignarlo a nuestro Field-Symbol.

Operadores de Comparación Un proceso muy común, es el comparar valores entre variables o tablas internas, para esto, contamos con los siguientes comandos. =, EQ

Igual a

, NE

Distinto a

>, GT

Mayor que

=, GE

Mayor igual

FACT. ENDMETHOD. ENDCLASS.

"GET_RESULT "FACTORIAL IMPLEMENTATION

START-OF-SELECTION. C_FACTORIAL=>MAIN( ). DATA MY_OBJ TYPE REF TO C_FACTORIAL. CREATE OBJECT MY_OBJ. CALL METHOD MY_OBJ->SET_FACT( EXPORTING NUM = NUMBER ). CALL METHOD MY_OBJ->GET_RESULT.

338

Este código se puede ver un poco complicado, extraño e innecesario, pero creanme que con el tiempo uno se acostumbra y la verdad es que es lo mejor programar así. Revisemos un poco el código antes de ejecutarlo.

SELECTION-SCREEN BEGIN OF SCREEN 101 AS WINDOW. PARAMETERS NUMBER TYPE I. SELECTION-SCREEN END OF SCREEN 101.

Declaramos un SELECTION-SCREEN pero utilizando un tipo ventana, esto para poder llamarlo desde nuestra clase.

*-----------------------------------------------------* *

CLASS FACTORIAL DEFINITION

*

*-----------------------------------------------------* CLASS C_FACTORIAL DEFINITION. PUBLIC SECTION. CLASS-METHODS: MAIN. METHODS: SET_FACT IMPORTING NUM TYPE I, GET_RESULT RETURNING VALUE(FACT) TYPE I. PRIVATE SECTION. DATA FACT TYPE I. ENDCLASS.

"FACTORIAL DEFINITION

Creamos nuestra clase C_FACTORIAL. En la sección pública, definimos un CLASS-METHOD, es decir, un método de instacia. Además, declaramos dos métodos, SET_FACT que recibe un

339

parámetro NUM y GET_RESULT que retorna el valor final del programa. En la sección privada, declaramos una variable llamada FACT, que es la que va a tener el resultado del factorial y es la variable que va a imprimir GET_RESULT.

*-----------------------------------------------------* *

CLASS FACTORIAL IMPLEMENTATION

*

*-----------------------------------------------------* CLASS C_FACTORIAL IMPLEMENTATION. METHOD MAIN. CALL SELECTION-SCREEN '101' STARTING AT 10 10. IF SY-SUBRC NE 0. EXIT. ENDIF. ENDMETHOD.

"MAIN

METHOD SET_FACT. FACT = 1. IF NUM GT 0. DO NUM TIMES. FACT = FACT * SY-INDEX. ENDDO. ENDIF. ENDMETHOD.

"SET_FACT

METHOD GET_RESULT. WRITE: 'El Factorial es: ', ME->FACT. ENDMETHOD. ENDCLASS.

"GET_RESULT "FACTORIAL IMPLEMENTATION

Implementamos nuestra clase, agregando el código a los métodos.

340

En el método MAIN, llamamos a nuestra pantalla para poder capturar el valor ingresado por el usuario. En el método SET_FACT realizamos el algoritmo para determinar el factorial. En el método GET_RESULT imprimimos el valor de la variable FACT. Como se darán cuenta, utilizamos ME->FACT para llamar a la variable, esto es porque este método no recibe parámetros, por lo cual necesita obtener el valor de la misma clase, pero como la variable está en la sección privada, entonces necesitamos acceder a ella definiendo el nombre de la clase y la variable, con lo cual quedaría así C_FACTORIAL->FACT. Felizmente, y para no escribir tanto, podemos escribirlo así ME->FACT, donde ME equivale a C_FACTORIAL.

START-OF-SELECTION. C_FACTORIAL=>MAIN( ). DATA MY_OBJ TYPE REF TO C_FACTORIAL. CREATE OBJECT MY_OBJ. CALL METHOD MY_OBJ->SET_FACT( EXPORTING NUM = NUMBER ). CALL METHOD MY_OBJ->GET_RESULT.

Llamamos al método estático MAIN, declaramos un objeto de la clase C_FACTORIAL, y lo creamos. Llamamos al método SET_FACT pasando el valor del parámetro obtenido en la ventana del SELECTION-SCREEN. Llamamos al método GET_RESULT para mostrar el resultado.

341

Por cuestiones de tamaño de almacenamiento de la variable, los números pueden ir desde el 1 hasta el 12, así que veamos que pasa si ingresamos 13.

342

En este caso, se ha producido un error, puesto que el tamaño de la variable que utilizamos no fue lo suficientemente grande como para poder almacenar el valor del factorial. La imagen que vemos, es la de un ShortDump (Es decir, un error grave en la programación). Esta ventana, nos indica porque se ha producido el error, cuando, en donde y hasta como podríamos resolverlo. En esta caso, vamos a utilizar una pequeña artimaña para que nuestro programa siga funcionando a pesar de este error, para ello, vamos a utilizar un comando llamado CATCH SYSTEMEXCEPTION. El cual funciona como un TRY-CATCH en Java o .NET, y lo que hace es simplemente intentar ejecutar una sentencia y si no puede, nos envía un error personalizado, que permite que el programa siga funcionando a pesar del error. Para esto, debemos hacer unas cuantas modificaciones al programa.

CLASS C_FACTORIAL DEFINITION. PUBLIC SECTION. CLASS-METHODS: MAIN. METHODS: SET_FACT IMPORTING NUM TYPE I, GET_RESULT RETURNING VALUE(FACT) TYPE I. PRIVATE SECTION. DATA: FACT TYPE I, FLAG TYPE C. ENDCLASS.

"FACTORIAL DEFINITION

Declaramos una variable llamada FLAG que nos va a servir al momento de mostrar el mensaje de resultado.

343

METHOD SET_FACT. CLEAR FLAG. FACT = 1. IF NUM GT 0. DO NUM TIMES. CATCH SYSTEM-EXCEPTIONS ARITHMETIC_ERRORS = 5. FACT = FACT * SY-INDEX. ENDCATCH. IF SY-SUBRC EQ 5. FACT = 0. FLAG = 'X'. EXIT. ENDIF. ENDDO. ENDIF. ENDMETHOD.

"SET_FACT

Dentro del método SET_FACT, limpiamos nuestra variable FLAG y utilizamos un CATCH SYSTEM-EXCEPTIONS, en este caso el ARITHMETIC_ERRORS, que significa que cualquier error de tipo Overflow (valor más grande que el tamaño de variable), va a lanzar un SY-SUBRC igual a 5 en vez de una pantalla de ShortDump. En el caso de que el SY-SUBRC sea igual a 5, entonces limpiamos la variable FACT y llenamos con “X” la variable FLAG. Obviamente, salimos del método utilizando la sentencia EXIT.

344

METHOD GET_RESULT. IF FLAG EQ SPACE. WRITE: 'El Factorial es: ', ME->FACT. ELSE. WRITE: 'Error, ingrese valores del 1 al 12'. ENDIF. ENDMETHOD.

"GET_RESULT

En el método GET_RESULT, preguntamos si la variable FLAG está vacía o tiene el valor “X”. Si está vacía, imprimimos el valor del factorial, si tiene el valor “X”, imprimimos un mensaje de advertencia.

Ahora, veamos el mismo ejemplo pero utilizando el Generador de Clases (SE24), gracias a lo cual podríamos obtener el Factorial de un número desde cualquier programa.

345

346

Primero, debemos ir a la pestaña Attributes (Atributos) para declarar nuestras variables.

Ambas variables tiene un Level (Nivel) 0 Instance Attribute (Atributo de Instacia), un Visibility (Visibilidad) 0 Private (Privado). Luego, nos dirigimos a la pestaña Methods (Métodos).

Declaramos dos métodos, SET_FACT y GET_RESULT, ambos con Nivel 0 Atributo de Instacia y Visibilidad 2 Public (Pública). Para asignar los parámetros, debemos presionar el botón Parameters (Parámetros)

.

347

SET_FACT tiene el parámetro NUM de tipo IMPORTING.

GET_RESULT tiene el parámetro FACT de tipo RETURNING. Para poder asignarles código a los métodos, debemos posicionarnos sobre cada uno de ellos y presionar el botón Code

.

348

Escribimos los códigos para cada método.

method SET_FACT. CLEAR FLAG. FACT = 1. IF NUM GT 0. DO NUM TIMES. CATCH SYSTEM-EXCEPTIONS ARITHMETIC_ERRORS = 5. FACT = FACT * SY-INDEX. ENDCATCH. IF SY-SUBRC EQ 5. FACT = 0. FLAG = 'X'. EXIT. ENDIF. ENDDO. ENDIF. endmethod.

method GET_RESULT. IF FLAG EQ SPACE. WRITE: 'El Factorial es: ', ME->FACT. ELSE. WRITE: 'Error, ingrese valores del 1 al 12'. ENDIF. endmethod.

Con esto, solo nos queda grabar, activar. La clase está lista para ser utilizada, pero claro, lo mejor es probarlo antes. Para esto, presionamos el botón Test o presionamos F8

.

349

Como sabemos que SET_FACT es el método que recibe el valor ingreado por el usario, lo ejecutamos presionando el botón Execute Method (Ejecutar Método)

.

Ingresamos un valor de prueba y presionamos el botón Execute (Ejecutar) o presionamos F8

.

De vuelta en la pantalla principal, ejecutamos el método GET_RESULT.

350

A pesar de que el valor de FACT aparece como 0, simplemente debemos retroceder para ver el resultado.

Listo, ahora, ya podemos utilizar nuestra nueva clase en un programa.

REPORT ZDUMMY_PRIMER_PROGRAMA. SELECTION-SCREEN BEGIN OF SCREEN 101 AS WINDOW. PARAMETERS NUMBER TYPE I. SELECTION-SCREEN END OF SCREEN 101.

351

START-OF-SELECTION. CALL SELECTION-SCREEN '101' STARTING AT 10 10. IF SY-SUBRC NE 0. EXIT. ENDIF. DATA MY_OBJ TYPE REF TO ZFACTORIAL. CREATE OBJECT MY_OBJ. CALL METHOD MY_OBJ->SET_FACT( EXPORTING NUM = NUMBER ). CALL METHOD MY_OBJ->GET_RESULT.

Ejecutamos y veremos que el resultado es el mismo, aunque el código es mucho más corto y reutilizable.

352

Componentes Orientados a Objetos Crear un ALV Grid OO Ya vimos como crear un ALV, pero ahora veremos como se hace con ABAP Objects.

*=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_PROGRAMAS, ID_PROG TYPE ZPROGRAMAS-ID_PROG, NOM_PROG TYPE ZPROGRAMAS-NOM_PROG, NOMBRE TYPE ZLENGUAJES_PROG-NOMBRE, ENTORNO TYPE ZLENGUAJES_PROG-ENTORNO, CONEX_SAP TYPE ZLENGUAJES_PROG-CONEX_SAP, END OF TY_PROGRAMAS. *=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS, GS_LAYOUT TYPE LVC_S_LAYO, GT_FIELDCAT TYPE LVC_T_FCAT, GT_SORT TYPE LVC_T_SORT, GS_VARIANT TYPE DISVARIANT.

353

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: MYCONTAINER TYPE SCRFNAME VALUE 'CUSTOM_ALV', CUSTOM_CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER, GRID1 TYPE REF TO CL_GUI_ALV_GRID, X_SAVE. *=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. PERFORM CARGAR_DATOS. PERFORM FILL_LAYOUT. PERFORM FILL_CATALOG. PERFORM LLAMAR_ALV. CALL SCREEN 0100. *&----------------------------------------------------* *&

Form

cargar_datos

*

*&----------------------------------------------------* FORM CARGAR_DATOS. SELECT ID_PROG NOM_PROG NOMBRE ENTORNO CONEX_SAP INTO TABLE T_PROGRAMAS FROM ( ZPROGRAMAS INNER JOIN ZLENGUAJES_PROG ON ZPROGRAMAS~ID EQ ZLENGUAJES_PROG~ID ). ENDFORM.

" cargar_datos

354

*&----------------------------------------------------* *&

Form

fill_layout

*

*&----------------------------------------------------* FORM FILL_LAYOUT. GS_LAYOUT-SEL_MODE = 'A'. ENDFORM.

" fill_layout

*&----------------------------------------------------* *&

Form

fill_catalog

*

*&----------------------------------------------------* FORM FILL_CATALOG. DATA: GS_FIELDCAT TYPE LVC_S_FCAT. CLEAR GS_FIELDCAT. GS_FIELDCAT-TABNAME = 'T_PROGRAMAS'. GS_FIELDCAT-FIELDNAME = 'ID_PROG'. GS_FIELDCAT-REPTEXT = 'Id'. GS_FIELDCAT-COL_POS = 1. GS_FIELDCAT-OUTPUTLEN = 5. APPEND GS_FIELDCAT TO GT_FIELDCAT. CLEAR GS_FIELDCAT. GS_FIELDCAT-TABNAME = 'T_PROGRAMAS'. GS_FIELDCAT-FIELDNAME = 'NOM_PROG'. GS_FIELDCAT-REPTEXT = 'Nombre Programa'. GS_FIELDCAT-COL_POS = 2. GS_FIELDCAT-OUTPUTLEN = 15. APPEND GS_FIELDCAT TO GT_FIELDCAT.

355

CLEAR GS_FIELDCAT. GS_FIELDCAT-TABNAME = 'T_PROGRAMAS'. GS_FIELDCAT-FIELDNAME = 'NOMBRE'. GS_FIELDCAT-REPTEXT = 'Nombre Lenguaje'. GS_FIELDCAT-COL_POS = 3. GS_FIELDCAT-OUTPUTLEN = 15. APPEND GS_FIELDCAT TO GT_FIELDCAT. CLEAR GS_FIELDCAT. GS_FIELDCAT-TABNAME = 'T_PROGRAMAS'. GS_FIELDCAT-FIELDNAME = 'ENTORNO'. GS_FIELDCAT-REPTEXT = 'Entorno'. GS_FIELDCAT-COL_POS = 4. GS_FIELDCAT-OUTPUTLEN = 10. APPEND GS_FIELDCAT TO GT_FIELDCAT. CLEAR GS_FIELDCAT. GS_FIELDCAT-TABNAME = 'T_PROGRAMAS'. GS_FIELDCAT-FIELDNAME = 'CONEX_SAP'. GS_FIELDCAT-REPTEXT = 'Conexión con SAP'. GS_FIELDCAT-COL_POS = 5. GS_FIELDCAT-OUTPUTLEN = 15. APPEND GS_FIELDCAT TO GT_FIELDCAT. ENDFORM.

" fill_catalog

*&----------------------------------------------------* *&

Form

llamar_alv

*

*&----------------------------------------------------* FORM LLAMAR_ALV. IF CUSTOM_CONTAINER IS INITIAL.

356

CREATE OBJECT CUSTOM_CONTAINER EXPORTING CONTAINER_NAME

= MYCONTAINER

EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR

= 2

CREATE_ERROR

= 3

LIFETIME_ERROR

= 4

LIFETIME_DYNPRO_DYNPRO_LINK = 5. ENDIF. CREATE OBJECT GRID1 EXPORTING I_PARENT = CUSTOM_CONTAINER. CALL METHOD GRID1->SET_TABLE_FOR_FIRST_DISPLAY EXPORTING IS_VARIANT

= GS_VARIANT

I_SAVE

= X_SAVE

I_DEFAULT

= 'X'

IS_LAYOUT

= GS_LAYOUT

CHANGING IT_FIELDCATALOG = GT_FIELDCAT IT_SORT

= GT_SORT[]

IT_OUTTAB

= T_PROGRAMAS[].

CALL METHOD GRID1->SET_READY_FOR_INPUT EXPORTING I_READY_FOR_INPUT = 0. ENDFORM.

" llamar_alv

357

*&----------------------------------------------------* *&

Module

STATUS_0100

OUTPUT

*

*&----------------------------------------------------* MODULE STATUS_0100 OUTPUT. SET PF-STATUS '100'. SET TITLEBAR '100'. ENDMODULE.

" STATUS_0100

OUTPUT

*&----------------------------------------------------* *&

Module

USER_COMMAND_0100

INPUT

*

*&----------------------------------------------------* MODULE USER_COMMAND_0100 INPUT. DATA: OK_CODE TYPE SY-UCOMM. OK_CODE = SY-UCOMM. CASE OK_CODE. WHEN 'BACK' OR 'EXIT' OR 'CANCEL'. SET SCREEN 0. LEAVE SCREEN. CLEAR SY-UCOMM. ENDCASE. ENDMODULE.

" USER_COMMAND_0100

INPUT

El código no deja de ser un poco extenso, así que vamos a revisarlo por partes. Aunque antes de continuar, hay un detalle muy importante que debemos tener en cuenta. Debemos crear un Dynpro asociado a esta pantalla, en donde el único componente será un

358

Custom Control (Control Personalizado) al cual llamaremos CUSTOM_ALV.

Continuemos con el código.

*=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_PROGRAMAS, ID_PROG TYPE ZPROGRAMAS-ID_PROG, NOM_PROG TYPE ZPROGRAMAS-NOM_PROG, NOMBRE TYPE ZLENGUAJES_PROG-NOMBRE, ENTORNO TYPE ZLENGUAJES_PROG-ENTORNO, CONEX_SAP TYPE ZLENGUAJES_PROG-CONEX_SAP, END OF TY_PROGRAMAS.

Declaramos un TYPE incluyendo los campos que queremos mostrar en el ALV.

359

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS, GS_LAYOUT TYPE LVC_S_LAYO, GT_FIELDCAT TYPE LVC_T_FCAT, GT_SORT TYPE LVC_T_SORT, GS_VARIANT TYPE DISVARIANT.

Declaramos algunas tablas internas, T_PROGRAMAS donde van a estar los registros para el ALV, GS_LAYOUT donde vamos a especificar como se visualiza el ALV, GT_FIELDCAT donde va a estar el catálogo de campos, GT_SORT donde se indican los criterios de ordenación y GS_VARIANT que indica el manejo de variantes. Si bien hay tablas que no vamos a utilizar, debemos declararlas y llamarlas de todos modos, porque el ALV así nos lo exige.

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: MYCONTAINER TYPE SCRFNAME VALUE 'CUSTOM_ALV', CUSTOM_CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER, GRID1 TYPE REF TO CL_GUI_ALV_GRID, X_SAVE.

MYCONTAINER es un tipo de variable que guarda el nombre de nuestro CUSTOM_CONTROL, CUSTOM_CONTAINER hace 360

referencia al contenedor del ALV, GRID1 es un objeto de la clase CL_GUI_ALV_GRID y X_SAVE es una variable utilizada para determinar si se graban o no las variantes y de que modo.

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. PERFORM CARGAR_DATOS. PERFORM FILL_LAYOUT. PERFORM FILL_CATALOG. PERFORM LLAMAR_ALV. CALL SCREEN 0100.

Tenemos varios PERFORM’s, el primero carga los datos, el segundo determina el formato del ALV, el tercero llena el catálogo y el cuarto llama al ALV. Como creamos un Dynpro, debemos llamarlo.

*&----------------------------------------------------* *&

Form

cargar_datos

*

*&----------------------------------------------------* FORM CARGAR_DATOS. SELECT ID_PROG NOM_PROG NOMBRE ENTORNO CONEX_SAP INTO TABLE T_PROGRAMAS FROM ( ZPROGRAMAS INNER JOIN ZLENGUAJES_PROG ON ZPROGRAMAS~ID EQ ZLENGUAJES_PROG~ID ). ENDFORM.

" cargar_datos

361

Hacemos un INNER JOIN para seleccionar los datos de la tablas.

*&----------------------------------------------------* *&

Form

fill_layout

*

*&----------------------------------------------------* FORM FILL_LAYOUT. GS_LAYOUT-SEL_MODE = 'A'. ENDFORM.

" fill_layout

Establecemos el modo se selección.

*&----------------------------------------------------* *&

Form

fill_catalog

*

*&----------------------------------------------------* FORM FILL_CATALOG. DATA: GS_FIELDCAT TYPE LVC_S_FCAT. CLEAR GS_FIELDCAT. GS_FIELDCAT-TABNAME = 'T_PROGRAMAS'. GS_FIELDCAT-FIELDNAME = 'ID_PROG'. GS_FIELDCAT-REPTEXT = 'Id'. GS_FIELDCAT-COL_POS = 1. GS_FIELDCAT-OUTPUTLEN = 5. APPEND GS_FIELDCAT TO GT_FIELDCAT. CLEAR GS_FIELDCAT. GS_FIELDCAT-TABNAME = 'T_PROGRAMAS'. GS_FIELDCAT-FIELDNAME = 'NOM_PROG'.

362

GS_FIELDCAT-REPTEXT = 'Nombre Programa'. GS_FIELDCAT-COL_POS = 2. GS_FIELDCAT-OUTPUTLEN = 15. APPEND GS_FIELDCAT TO GT_FIELDCAT. CLEAR GS_FIELDCAT. GS_FIELDCAT-TABNAME = 'T_PROGRAMAS'. GS_FIELDCAT-FIELDNAME = 'NOMBRE'. GS_FIELDCAT-REPTEXT = 'Nombre Lenguaje'. GS_FIELDCAT-COL_POS = 3. GS_FIELDCAT-OUTPUTLEN = 15. APPEND GS_FIELDCAT TO GT_FIELDCAT. CLEAR GS_FIELDCAT. GS_FIELDCAT-TABNAME = 'T_PROGRAMAS'. GS_FIELDCAT-FIELDNAME = 'ENTORNO'. GS_FIELDCAT-REPTEXT = 'Entorno'. GS_FIELDCAT-COL_POS = 4. GS_FIELDCAT-OUTPUTLEN = 10. APPEND GS_FIELDCAT TO GT_FIELDCAT. CLEAR GS_FIELDCAT. GS_FIELDCAT-TABNAME = 'T_PROGRAMAS'. GS_FIELDCAT-FIELDNAME = 'CONEX_SAP'. GS_FIELDCAT-REPTEXT = 'Conexión con SAP'. GS_FIELDCAT-COL_POS = 5. GS_FIELDCAT-OUTPUTLEN = 15. APPEND GS_FIELDCAT TO GT_FIELDCAT. ENDFORM.

" fill_catalog

Llenamos el catálogo del ALV.

363

*&----------------------------------------------------* *&

Form

llamar_alv

*

*&----------------------------------------------------* FORM LLAMAR_ALV. IF CUSTOM_CONTAINER IS INITIAL. CREATE OBJECT CUSTOM_CONTAINER EXPORTING CONTAINER_NAME

= MYCONTAINER

EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR

= 2

CREATE_ERROR

= 3

LIFETIME_ERROR

= 4

LIFETIME_DYNPRO_DYNPRO_LINK = 5. ENDIF. CREATE OBJECT GRID1 EXPORTING I_PARENT = CUSTOM_CONTAINER. CALL METHOD GRID1->SET_TABLE_FOR_FIRST_DISPLAY EXPORTING IS_VARIANT

= GS_VARIANT

I_SAVE

= X_SAVE

I_DEFAULT

= 'X'

IS_LAYOUT

= GS_LAYOUT

CHANGING IT_FIELDCATALOG = GT_FIELDCAT IT_SORT

= GT_SORT[]

IT_OUTTAB

= T_PROGRAMAS[].

364

CALL METHOD GRID1->SET_READY_FOR_INPUT EXPORTING I_READY_FOR_INPUT = 0. ENDFORM.

" llamar_alv

Creamos el objeto CUSTOM_CONTAINER apuntando a nuestro CUSTOM_CONTROL (Cuyo nombre está almacenado en la variable MYCONTAINER). Creamos el objeto GRID1, especificando que se debe mostrar dentro del contenedor CUSTOM_CONTAINER. Llamamos al

método

SET_TABLE_FOR_FIRST_DISPLAY

pasándole los parámetros correspondientes. Llamamos al método SET_READY_FOR_INPUT pasando 0 como parámetro, lo cual significa que el ALV no podrá ser modificado.

*&----------------------------------------------------* *&

Module

STATUS_0100

OUTPUT

*

*&----------------------------------------------------* MODULE STATUS_0100 OUTPUT. SET PF-STATUS '100'. SET TITLEBAR '100'. ENDMODULE.

" STATUS_0100

OUTPUT

Establecemos el menú y el título de la aplicación.

365

*&----------------------------------------------------* *&

Module

USER_COMMAND_0100

INPUT

*

*&----------------------------------------------------* MODULE USER_COMMAND_0100 INPUT. DATA: OK_CODE TYPE SY-UCOMM. OK_CODE = SY-UCOMM. CASE OK_CODE. WHEN 'BACK' OR 'EXIT' OR 'CANCEL'. SET SCREEN 0. LEAVE SCREEN. CLEAR SY-UCOMM. ENDCASE. ENDMODULE.

" USER_COMMAND_0100

INPUT

Definimos las acciones de los códigos de función.

Como ven, el ALV está listo para salir a producción.

366

Agregar validaciones y eventos Si bien el programa funciona, no es muy útil que digamos puesto que no hace...nada...Así que vamos a agregar algunas cosas adicionales... Primero, nos vamos al Menu Painter (SE41) y agregamos un nuevo botón.

Debemos llamar a la clase LCL_EVENT_RECIEVER, pero al mismo tiempo debemos modificar su estructura (Solamente dentro de nuestro programa, claro está...)

CLASS LCL_EVENT_RECEIVER DEFINITION DEFERRED.

Agregamos un nuevo campos a nuestro TYPE TY_PROGRAMAS y creamos un nuevo TYPE llamado TY_NOMBRE.

*=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_PROGRAMAS, ID_PROG TYPE ZPROGRAMAS-ID_PROG, NOM_PROG TYPE ZPROGRAMAS-NOM_PROG, NOMBRE TYPE ZLENGUAJES_PROG-NOMBRE,

367

ENTORNO TYPE ZLENGUAJES_PROG-ENTORNO, CONEX_SAP TYPE ZLENGUAJES_PROG-CONEX_SAP, ID TYPE ZPROGRAMAS-ID, END OF TY_PROGRAMAS. TYPES: BEGIN OF TY_NOMBRE, ID TYPE ZLENGUAJES_PROG-ID, NOMBRE TYPE ZLENGUAJES_PROG-NOMBRE, END OF TY_NOMBRE.

Agregamos algunas tablas internas. *=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS, T_NOMBRE TYPE STANDARD TABLE OF TY_NOMBRE, LT_F4 TYPE LVC_T_F4 WITH HEADER LINE, RETURN_TAB TYPE STANDARD TABLE OF DDSHRETVAL WITH HEADER LINE, T_STABLE TYPE STANDARD TABLE OF LVC_S_STBL WITH HEADER LINE, GS_LAYOUT TYPE LVC_S_LAYO, GT_FIELDCAT TYPE LVC_T_FCAT, GT_SORT TYPE LVC_T_SORT, GS_VARIANT TYPE DISVARIANT.

T_NOMBRE nos sirve para crear una ayuda de búsqueda personalizada.

368

LT_F4 nos sirve para determinar que campos van a tener asignada una ayuda de búsqueda. RETURN_TAB es la tabla que nos devuelve el valor seleccionado por la función para crear ayudas de búsqueda. T_STABLE

nos permite que el ALV mantenga su posición al

momento de actualizar los datos. Agregamos algunas variables.

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: MYCONTAINER TYPE SCRFNAME VALUE 'CUSTOM_ALV', CUSTOM_CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER, GRID1 TYPE REF TO CL_GUI_ALV_GRID, EVENT_RECEIVER TYPE REF TO LCL_EVENT_RECEIVER, X_SAVE, W_ERROR TYPE C, L_VALID(1) TYPE C.

EVENT_RECIEVER nos permite crear una referencia para llamar eventos en el ALV. W_ERROR nos indica si hay algún error en la modificación de datos. L_VALID indica si la operación es válida o no.

369

Agregamos algunos Field-Symbols.

*=====================================================* * DECLARACION DE FIELD-SYMBOLS

*

*=====================================================* FIELD-SYMBOLS: LIKE LINE OF T_NOMBRE, LIKE LINE OF T_PROGRAMAS.

Redefinimos la clase LCL_EVENT_RECIEVER.

*-----------------------------------------------------* *

CLASS LCL_EVENT_RECEIVER DEFINITION

*

*-----------------------------------------------------* CLASS LCL_EVENT_RECEIVER DEFINITION. PUBLIC SECTION. METHODS: HANDLE_DATA_CHANGED FOR EVENT DATA_CHANGED OF CL_GUI_ALV_GRID IMPORTING ER_DATA_CHANGED, HANDLE_F4_HELP FOR EVENT ONF4 OF CL_GUI_ALV_GRID IMPORTING E_FIELDNAME ES_ROW_NO ER_EVENT_DATA. ENDCLASS.

"LCL_EVENT_RECEIVER DEFINITION

Definimos dos métodos, HANDLE_DATA_CHANGED (Que valida

si

algún

campo

ha

cambiado

su

valor)

y

HANDLE_F4_HELP que sirve para asignar las ayudas de búsqueda dinámicas.

370

*-----------------------------------------------------* *

CLASS lcl_event_receiver IMPLEMENTATION

*

*-----------------------------------------------------* CLASS LCL_EVENT_RECEIVER IMPLEMENTATION. METHOD HANDLE_DATA_CHANGED. PERFORM DATA_CHANGED USING ER_DATA_CHANGED. ENDMETHOD.

"HANDLE_DATA_CHANGED

METHOD HANDLE_F4_HELP. PERFORM HANDLE_ONF4 USING E_FIELDNAME ES_ROW_NO. ER_EVENT_DATA->M_EVENT_HANDLED = 'X'. ENDMETHOD.

"HANDLE_F4_HELP

ENDCLASS.

"LCL_EVENT_RECEIVER IMPLEMENTATION

Dentro del método HANDLE_DATA_CHANGED llamamos al FORM DATA_CHANGED. Dentro del método HANDLE_F4_HELP llamamos al FORM HANDEL_ONF4. ER_EVENT_DATA->M_EVENT_HANDLED

=

‘X’,

especificamos que queremos activar el evento.

*&----------------------------------------------------* *&

Form

cargar_datos

*

*&----------------------------------------------------* FORM CARGAR_DATOS. SELECT ID_PROG NOM_PROG NOMBRE ENTORNO CONEX_SAP INTO TABLE T_PROGRAMAS FROM ( ZPROGRAMAS INNER JOIN ZLENGUAJES_PROG ON ZPROGRAMAS~ID EQ ZLENGUAJES_PROG~ID ).

371

SELECT ID NOMBRE INTO TABLE T_NOMBRE FROM ZLENGUAJES_PROG. ENDFORM.

" cargar_datos

Agregamos un nuevo select, esta vez a ZLENGUAJES_PROG para poder obtener los valores necesarios para nuestra ayuda de búsqueda dinámica.

CLEAR GS_FIELDCAT. GS_FIELDCAT-TABNAME = 'T_PROGRAMAS'. GS_FIELDCAT-FIELDNAME = 'NOMBRE'. GS_FIELDCAT-EDIT

= 'X'.

GS_FIELDCAT-F4AVAILABL = 'X'. GS_FIELDCAT-REPTEXT = 'Nombre Lenguaje'. GS_FIELDCAT-COL_POS = 3. GS_FIELDCAT-OUTPUTLEN = 15. APPEND GS_FIELDCAT TO GT_FIELDCAT.

Dentro del catálogo, solamente vamos a modificar un campo, por lo que agregamos EDIT para que pueda modificarse y F4AVAILABL para que acepte la ayuda de búsqueda dinámica.

*&----------------------------------------------------* *&

Form

llamar_alv

*

*&----------------------------------------------------* FORM LLAMAR_ALV.

372

IF CUSTOM_CONTAINER IS INITIAL. CREATE OBJECT CUSTOM_CONTAINER EXPORTING CONTAINER_NAME

= MYCONTAINER

EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR

= 2

CREATE_ERROR

= 3

LIFETIME_ERROR

= 4

LIFETIME_DYNPRO_DYNPRO_LINK = 5. ENDIF. CREATE OBJECT GRID1 EXPORTING I_PARENT = CUSTOM_CONTAINER. CREATE OBJECT EVENT_RECEIVER. LT_F4-FIELDNAME = 'NOMBRE'. LT_F4-REGISTER = 'X' . LT_F4-GETBEFORE = 'X' . LT_F4-CHNGEAFTER = 'X' . APPEND LT_F4. SET HANDLER EVENT_RECEIVER>HANDLE_DATA_CHANGED FOR GRID1. SET HANDLER EVENT_RECEIVER->HANDLE_F4_HELP FOR GRID1. CALL METHOD GRID1->REGISTER_F4_FOR_FIELDS EXPORTING IT_F4 = LT_F4[].

373

IF SY-BATCH IS INITIAL. CALL METHOD GRID1->REGISTER_EDIT_EVENT EXPORTING I_EVENT_ID = CL_GUI_ALV_GRID=>MC_EVT_MODIFIED. ENDIF. CALL METHOD GRID1->SET_TABLE_FOR_FIRST_DISPLAY EXPORTING IS_VARIANT

= GS_VARIANT

I_SAVE

= X_SAVE

I_DEFAULT

= 'X'

IS_LAYOUT

= GS_LAYOUT

CHANGING IT_FIELDCATALOG = GT_FIELDCAT IT_SORT

= GT_SORT[]

IT_OUTTAB

= T_PROGRAMAS[].

CALL METHOD GRID1->SET_READY_FOR_INPUT EXPORTING I_READY_FOR_INPUT = 1. ENDFORM.

" llamar_alv

Creamos el objeto EVENT_RECIEVER para poder asignar los eventos al ALV. Llenamos la tabla LT_F4, con el campo que tendrá la ayuda de búsqueda dinámica. Establecemos los eventos utilizando el comando SET HANDLER.

374

La variable SY-BATCH nos indica si estamos ejecutando el programa en fondo o en modo directo, si es en modo directo, entonces registramos el evento de modificación.

*&----------------------------------------------------* *&

Module

USER_COMMAND_0100

INPUT

*

*&----------------------------------------------------* MODULE USER_COMMAND_0100 INPUT. DATA: OK_CODE TYPE SY-UCOMM. OK_CODE = SY-UCOMM. CLEAR SY-UCOMM. CASE OK_CODE. WHEN 'BACK' OR 'EXIT' OR 'CANCEL'. SET SCREEN 0. LEAVE SCREEN. WHEN 'SAVE'. CALL METHOD GRID1->CHECK_CHANGED_DATA IMPORTING E_VALID = L_VALID. IF L_VALID EQ 'X'. PERFORM GRABAR_DATOS. ENDIF. ENDCASE. ENDMODULE.

" USER_COMMAND_0100

INPUT

Agregamos el código de función SAVE. Validamos que los registros del ALV hayan cambiado de valor y llamamos al FORM GRABAR_DATOS para guardar los cambios en la Base de Datos. 375

*&----------------------------------------------------* *&

Form

data_changed

*

*&----------------------------------------------------* FORM DATA_CHANGED USING RR_DATA_CHANGED TYPE REF TO CL_ALV_CHANGED_DATA_PROTOCOL. DATA: W_NEW, LS_MOD_CELLS TYPE LVC_S_MODI, LS_CELLS TYPE LVC_S_MODI. DATA: L_NOMBRE TYPE ZLENGUAJES_PROG-NOMBRE. LOOP AT RR_DATA_CHANGED->MT_GOOD_CELLS INTO LS_MOD_CELLS. CASE LS_MOD_CELLS-FIELDNAME. WHEN 'NOMBRE'. CALL METHOD RR_DATA_CHANGED->GET_CELL_VALUE EXPORTING I_ROW_ID

= LS_MOD_CELLS-ROW_ID

I_FIELDNAME = LS_MOD_CELLS-FIELDNAME IMPORTING E_VALUE

= L_NOMBRE.

IF L_NOMBRE IS INITIAL. CALL METHOD RR_DATA_CHANGED->ADD_PROTOCOL_ENTRY EXPORTING I_MSGID

= '0K'

I_MSGNO

= '000'

I_MSGTY

= 'E'

I_MSGV1

= 'Seleccione algún nombre'

I_FIELDNAME = LS_MOD_CELLS-FIELDNAME

376

I_ROW_ID

= LS_MOD_CELLS-ROW_ID.

W_ERROR = 'X'. ELSE. READ TABLE T_NOMBRE WITH KEY NOMBRE = L_NOMBRE ASSIGNING . IF SY-SUBRC NE 0. CALL METHOD RR_DATA_CHANGED->ADD_PROTOCOL_ENTRY EXPORTING I_MSGID

= '0K'

I_MSGNO

= '000'

I_MSGTY

= 'E'

I_MSGV1

= 'Nombre ingresado no existe'

I_FIELDNAME = LS_MOD_CELLS-FIELDNAME I_ROW_ID

= LS_MOD_CELLS-ROW_ID.

W_ERROR = 'X'. ELSE. CALL METHOD RR_DATA_CHANGED->MODIFY_CELL EXPORTING I_ROW_ID

= LS_MOD_CELLS-ROW_ID

I_FIELDNAME = 'ID' I_VALUE

= -ID.

ENDIF. ENDIF. ENDCASE. ENDLOOP. ENDFORM.

Hacemos

"data_changed

un

LOOP

a

RR_DATA_CHANGED-

>MT_GOOD_CELLS, es decir a los campos que han cambiado de

377

valor. Llamamos al método GET_CELL_VALUE para validar el nuevo contenido del campo. Si está vacío, mostramos un mensaje de error con el método ADD_PROTOCOL_ENTRY, en caso contrario leemos la tabla interna T_NOMBRE para validar que el valor exista. Si no hay más problemas, llamamos al método MODIFY_CELL para modificar el campo ID (Puesto que nosotros hemos modificado el campo NOMBRE, pero necesitamos que a su vez se modifique también el campo ID).

*&----------------------------------------------------* *&

Form

handle_onf4

*

*&----------------------------------------------------* FORM HANDLE_ONF4 USING P_E_FIELDNAME P_ES_ROW_NO STRUCTURE LVC_S_ROID. CASE P_E_FIELDNAME. WHEN 'NOMBRE'. CALL FUNCTION 'F4IF_INT_TABLE_VALUE_REQUEST' EXPORTING RETFIELD

= 'NOMBRE'

VALUE_ORG

= 'S'

TABLES VALUE_TAB

= T_NOMBRE

RETURN_TAB

= RETURN_TAB

EXCEPTIONS PARAMETER_ERROR = 1 NO_VALUES_FOUND = 2 OTHERS

= 3.

378

IF NOT RETURN_TAB[] IS INITIAL. READ TABLE RETURN_TAB INDEX 1. READ TABLE T_PROGRAMAS INDEX P_ES_ROW_NO-ROW_ID ASSIGNING . READ TABLE T_NOMBRE WITH KEY NOMBRE = RETURN_TAB-FIELDVAL ASSIGNING . -NOMBRE = RETURN_TAB-FIELDVAL. -ID = -ID. CALL METHOD GRID1->REFRESH_TABLE_DISPLAY EXPORTING IS_STABLE = T_STABLE. ENDIF. ENDCASE. ENDFORM.

"handle_onf4

Llamamos al método F4IF_INT_TABLE_VALUE_REQUEST para poder mostrar la ayuda de búsqueda dinámica. Llamamos al método REFRESH_TABLE_DISPLAY para refrescar el contenido del ALV.

*&----------------------------------------------------* *&

Form

grabar_datos

*

*&----------------------------------------------------* FORM GRABAR_DATOS. LOOP AT T_PROGRAMAS ASSIGNING WHERE ID NE SPACE. UPDATE ZPROGRAMAS SET ID = -ID WHERE ID_PROG EQ -ID_PROG.

379

IF SY-SUBRC EQ 0. COMMIT WORK. ELSE. ROLLBACK WORK. ENDIF. ENDLOOP. ENDFORM.

" GRABAR_DATOS

Recorremos todos los registros de nuestra tabla interna, que tengan un valor en el campo ID, luego utilizamos un UPDATE para actualizar los valores de la Base de Datos. Grabamos, activamos y ejecutamos.

380

Crear un ALV Tree OO Un ALV Tree es un tipo de reporte jerárquico, en donde un nodo principal agrupa subnodos u hojas. Este tipo de reporte no es muy utilizado, pero de todos modos es muy intersante y vale la pena revisarlo. Este es el código.

REPORT ZDUMMY_PRIMER_PROGRAMA. *=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_PROGRAMAS, LENGUAJE TYPE ZVLENGUAJES_PROG-LENGUAJE, NOM_PROG TYPE ZVLENGUAJES_PROG-NOM_PROG, ENTORNO TYPE ZVLENGUAJES_PROG-ENTORNO, END OF TY_PROGRAMAS. TYPES: BEGIN OF TY_HEADER, LENGUAJE TYPE ZVLENGUAJES_PROG-LENGUAJE, END OF TY_HEADER.

381

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS, T_TREE TYPE STANDARD TABLE OF TY_PROGRAMAS WITH HEADER LINE, T_HEADER TYPE STANDARD TABLE OF TY_HEADER, GT_FIELDCAT_TREE TYPE LVC_T_FCAT. *=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: OK_CODE TYPE SY-UCOMM, G_ALV_TREE TYPE REF TO CL_GUI_ALV_TREE, G_CUSTOM_CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER, L_HIERARCHY_HEADER TYPE TREEV_HHDR. *=====================================================* * DECLARACION DE FIELD-SYMBOLS

*

*=====================================================* FIELD-SYMBOLS: LIKE LINE OF T_PROGRAMAS, LIKE LINE OF T_HEADER, LIKE LINE OF T_PROGRAMAS.

382

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. PERFORM CARGAR_DATOS. PERFORM INIT_TREE. CALL SCREEN 0100. *&----------------------------------------------------* *&

Form

CARGAR_DATOS

*

*&----------------------------------------------------* FORM CARGAR_DATOS. SELECT LENGUAJE NOM_PROG ENTORNO INTO TABLE T_PROGRAMAS FROM ZVLENGUAJES_PROG. LOOP AT T_PROGRAMAS ASSIGNING . APPEND INITIAL LINE TO T_HEADER ASSIGNING . -LENGUAJE = -LENGUAJE. ENDLOOP. SORT T_HEADER. DELETE ADJACENT DUPLICATES FROM T_HEADER. ENDFORM.

" CARGAR_DATOS

383

*&----------------------------------------------------* *&

Module

STATUS_0100

OUTPUT

*

*&----------------------------------------------------* MODULE STATUS_0100 OUTPUT. SET PF-STATUS '100'. SET TITLEBAR '100'. ENDMODULE.

" STATUS_0100

OUTPUT

*&----------------------------------------------------* *&

Module

USER_COMMAND_0100

INPUT

*

*&----------------------------------------------------* MODULE USER_COMMAND_0100 INPUT. OK_CODE = SY-UCOMM. CLEAR SY-UCOMM. CASE OK_CODE. WHEN 'BACK' OR 'STOP' OR 'CANCEL'. SET SCREEN 0. LEAVE SCREEN. ENDCASE. ENDMODULE.

" USER_COMMAND_0100

INPUT

384

*&----------------------------------------------------* *&

Form

INIT_TREE

*

*&----------------------------------------------------* FORM INIT_TREE. DATA: L_TREE_CONTAINER_NAME(30) TYPE C. L_TREE_CONTAINER_NAME = 'CUSTOM_ALV'. CREATE OBJECT G_CUSTOM_CONTAINER EXPORTING CONTAINER_NAME

=

L_TREE_CONTAINER_NAME EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR

= 2

CREATE_ERROR

= 3

LIFETIME_ERROR

= 4

LIFETIME_DYNPRO_DYNPRO_LINK = 5. CREATE OBJECT G_ALV_TREE EXPORTING PARENT

= G_CUSTOM_CONTAINER

NODE_SELECTION_MODE

=

CL_GUI_COLUMN_TREE=>NODE_SEL_MODE_SINGLE ITEM_SELECTION

= ''

NO_HTML_HEADER

= 'X'

NO_TOOLBAR

= ''

EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR

= 2

CREATE_ERROR

= 3

385

LIFETIME_ERROR

= 4

ILLEGAL_NODE_SELECTION_MODE = 5 FAILED

= 6

ILLEGAL_COLUMN_NAME

= 7.

PERFORM FILL_CATALOG_TREE. PERFORM BUILD_HIERARCHY_HEADER CHANGING L_HIERARCHY_HEADER. CALL METHOD G_ALV_TREE->SET_TABLE_FOR_FIRST_DISPLAY EXPORTING IS_HIERARCHY_HEADER = L_HIERARCHY_HEADER CHANGING IT_OUTTAB

= T_TREE[]

IT_FIELDCATALOG

= GT_FIELDCAT_TREE.

PERFORM CREATE_HIERARCHY. ENDFORM.

" INIT_TREE

*&----------------------------------------------------* *&

Form

fill_catalog_tree

*

*&----------------------------------------------------* FORM FILL_CATALOG_TREE. DATA: GS_FIELDCAT TYPE LVC_S_FCAT. CLEAR GS_FIELDCAT. GS_FIELDCAT-COL_POS

= 1.

GS_FIELDCAT-FIELDNAME = 'NOM_PROG'. GS_FIELDCAT-SCRTEXT_S = 'Nom. Programa'. GS_FIELDCAT-TABNAME

= 'T_PROGRAMAS'.

GS_FIELDCAT-OUTPUTLEN = 20.

386

APPEND GS_FIELDCAT TO GT_FIELDCAT_TREE. CLEAR GS_FIELDCAT. GS_FIELDCAT-COL_POS

= 2.

GS_FIELDCAT-FIELDNAME = 'ENTORNO'. GS_FIELDCAT-SCRTEXT_S = 'Entorno'. GS_FIELDCAT-TABNAME

= 'T_PROGRAMAS'.

GS_FIELDCAT-OUTPUTLEN = 15. APPEND GS_FIELDCAT TO GT_FIELDCAT_TREE. ENDFORM.

"fill_catalog_tree

*&----------------------------------------------------* *

FORM build_hierarchy_header

*

*&----------------------------------------------------* FORM BUILD_HIERARCHY_HEADER CHANGING P_HIERARCHY_HEADER TYPE TREEV_HHDR. CLEAR P_HIERARCHY_HEADER. P_HIERARCHY_HEADER-HEADING = 'Código'(300). P_HIERARCHY_HEADER-WIDTH = 60. P_HIERARCHY_HEADER-WIDTH_PIX = ' '. ENDFORM.

"BUILD_HIERARCHY_HEADER

*&----------------------------------------------------* *&

Form

create_hierarchy

*

*&----------------------------------------------------* FORM CREATE_HIERARCHY. DATA: L_ROOT_KEY TYPE LVC_NKEY, L_NEXT_KEY TYPE LVC_NKEY,

387

L_LAST_KEY TYPE LVC_NKEY, HEADER TYPE STRING, W_MENGE_TEXT(13) TYPE C. CLEAR L_ROOT_KEY.

LOOP AT T_HEADER ASSIGNING . HEADER = -LENGUAJE. CLEAR L_ROOT_KEY. CLEAR L_NEXT_KEY. PERFORM ADD_NODE USING HEADER L_ROOT_KEY CHANGING L_NEXT_KEY. LOOP AT T_PROGRAMAS ASSIGNING WHERE LENGUAJE EQ -LENGUAJE. MOVE-CORRESPONDING TO T_TREE. PERFORM ADD_LEAF USING T_TREE L_NEXT_KEY CHANGING L_LAST_KEY. ENDLOOP. ENDLOOP. CALL METHOD G_ALV_TREE->FRONTEND_UPDATE. ENDFORM.

"CREATE_HIERARCHY

*&----------------------------------------------------* *

FORM ADD_NODE

*

*&----------------------------------------------------* FORM ADD_NODE USING L_NAME L_ROOT_KEY CHANGING L_NEXT_KEY.

388

DATA: L_NODE_TEXT TYPE LVC_VALUE, LS_TREE TYPE TY_PROGRAMAS. L_NODE_TEXT =

L_NAME.

CALL METHOD G_ALV_TREE->ADD_NODE EXPORTING I_RELAT_NODE_KEY = L_ROOT_KEY I_RELATIONSHIP

=

CL_GUI_COLUMN_TREE=>RELAT_LAST_CHILD I_NODE_TEXT

= L_NODE_TEXT

IS_OUTTAB_LINE

= LS_TREE

IMPORTING E_NEW_NODE_KEY

= L_NEXT_KEY.

ENDFORM.

"ADD_NODE

*&----------------------------------------------------* *

FORM ADD_LEAF

*

*&----------------------------------------------------* FORM ADD_LEAF USING L_TREE TYPE TY_PROGRAMAS L_NEXT_KEY CHANGING L_LAST_KEY. CALL METHOD G_ALV_TREE->ADD_NODE EXPORTING I_RELAT_NODE_KEY = L_NEXT_KEY I_RELATIONSHIP

=

CL_GUI_COLUMN_TREE=>RELAT_LAST_CHILD IS_OUTTAB_LINE

= T_TREE

IMPORTING E_NEW_NODE_KEY

= L_LAST_KEY.

389

ENDFORM.

"ADD_LEAF

Revisemos el código paso a paso.

*=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_PROGRAMAS, LENGUAJE TYPE ZVLENGUAJES_PROG-LENGUAJE, NOM_PROG TYPE ZVLENGUAJES_PROG-NOM_PROG, ENTORNO TYPE ZVLENGUAJES_PROG-ENTORNO, END OF TY_PROGRAMAS. TYPES: BEGIN OF TY_HEADER, LENGUAJE TYPE ZVLENGUAJES_PROG-LENGUAJE, END OF TY_HEADER.

Declaramos dos TYPE’s, uno para crear la tabla que guardará los datos del ALV y otro para crear la tabla que guardará los datos de la cabecera del programa.

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS, T_TREE TYPE STANDARD TABLE OF TY_PROGRAMAS WITH HEADER LINE, T_HEADER TYPE STANDARD TABLE OF

390

TY_HEADER, GT_FIELDCAT_TREE TYPE LVC_T_FCAT.

Declaramos algunas tablas internas, T_PROGRAMAS que guarda los datos del ALV, T_TREE que es una tabla intermedia para mostrar los datos del ALV, T_HEADER es la cabecera del ALV, GT_FIELDCAT_TREE es el catálogo del ALV.

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: OK_CODE TYPE SY-UCOMM, G_ALV_TREE TYPE REF TO CL_GUI_ALV_TREE, G_CUSTOM_CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER, L_HIERARCHY_HEADER TYPE TREEV_HHDR.

Declaramos algunas variables. OK_CODE para guardar el código de función, G_ALV_TREE que es un objeto de la clase CL_GUI_ALV_TREE, G_CUSTOM_CONTAINER que es un contenedor para el ALV y L_HIERARCHY_HEADER que sirve para definir la cabecera.

*=====================================================* * DECLARACION DE FIELD-SYMBOLS

*

*=====================================================* FIELD-SYMBOLS: LIKE LINE OF T_PROGRAMAS, LIKE LINE OF

391

T_HEADER, LIKE LINE OF T_PROGRAMAS.

Declaramos algunos Field-Symbols.

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. PERFORM CARGAR_DATOS. PERFORM INIT_TREE. CALL SCREEN 0100.

Llamamos dos FORM’s CARGAR_DATOS e INIT_TREE, llamamos a la pantalla 100.

*&----------------------------------------------------* *&

Form

CARGAR_DATOS

*

*&----------------------------------------------------* FORM CARGAR_DATOS. SELECT LENGUAJE NOM_PROG ENTORNO INTO TABLE T_PROGRAMAS FROM ZVLENGUAJES_PROG. LOOP AT T_PROGRAMAS ASSIGNING . APPEND INITIAL LINE TO T_HEADER ASSIGNING . -LENGUAJE = -LENGUAJE.

392

ENDLOOP. SORT T_HEADER. DELETE ADJACENT DUPLICATES FROM T_HEADER. ENDFORM.

" CARGAR_DATOS

Seleccionamos los datos de la vista ZVLENGUAJES_PROG. Luego asignamos el campo LENGUAJE a la tabla T_HEADER y eliminamos los registros duplicados. *&----------------------------------------------------* *&

Module

STATUS_0100

OUTPUT

*

*&----------------------------------------------------* MODULE STATUS_0100 OUTPUT. SET PF-STATUS '100'. SET TITLEBAR '100'. ENDMODULE.

" STATUS_0100

OUTPUT

Establecemos el menú y título del programa.

*&----------------------------------------------------* *&

Module

USER_COMMAND_0100

INPUT

*

*&----------------------------------------------------* MODULE USER_COMMAND_0100 INPUT. OK_CODE = SY-UCOMM. CLEAR SY-UCOMM.

393

CASE OK_CODE. WHEN 'BACK' OR 'STOP' OR 'CANCEL'. SET SCREEN 0. LEAVE SCREEN. ENDCASE. ENDMODULE.

" USER_COMMAND_0100

INPUT

Asignamos acciones para el código de funciones.

*&----------------------------------------------------* *&

Form

INIT_TREE

*

*&----------------------------------------------------* FORM INIT_TREE. DATA: L_TREE_CONTAINER_NAME(30) TYPE C. L_TREE_CONTAINER_NAME = 'CUSTOM_ALV'. CREATE OBJECT G_CUSTOM_CONTAINER EXPORTING CONTAINER_NAME

=

L_TREE_CONTAINER_NAME EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR

= 2

CREATE_ERROR

= 3

LIFETIME_ERROR

= 4

LIFETIME_DYNPRO_DYNPRO_LINK = 5. CREATE OBJECT G_ALV_TREE

394

EXPORTING PARENT

= G_CUSTOM_CONTAINER

NODE_SELECTION_MODE

=

CL_GUI_COLUMN_TREE=>NODE_SEL_MODE_SINGLE ITEM_SELECTION

= ''

NO_HTML_HEADER

= 'X'

NO_TOOLBAR

= ''

EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR

= 2

CREATE_ERROR

= 3

LIFETIME_ERROR

= 4

ILLEGAL_NODE_SELECTION_MODE = 5 FAILED

= 6

ILLEGAL_COLUMN_NAME

= 7.

PERFORM FILL_CATALOG_TREE. PERFORM BUILD_HIERARCHY_HEADER CHANGING L_HIERARCHY_HEADER. CALL METHOD G_ALV_TREE->SET_TABLE_FOR_FIRST_DISPLAY EXPORTING IS_HIERARCHY_HEADER = L_HIERARCHY_HEADER CHANGING IT_OUTTAB

= T_TREE[]

IT_FIELDCATALOG

= GT_FIELDCAT_TREE.

PERFORM CREATE_HIERARCHY. ENDFORM.

" INIT_TREE

395

Creamos el contenedor G_CUSTOM_CONTAINER pasando como parámetro el nombre de nuestro CUSTOM_CONTROL contenido en la variable L_TREE_CONTAINER_NAME. Creamos

el

objeto

G_ALV_TREE

asignándolo

a

nuestro

contenedor G_CUSTOM_CONTAINER. Llamámos al FORM FILL_CATALOG_TREE donde llenaremos el catálogo del ALV. Llamámos al FORM BUILD_HIERARCHY_HEADER, donde establecemos los detalles de la cabecera. Llamámos al método SET_TABLE_FOR_FIRST_DISPLAY para llamar al ALV. Finalmemte llamámos al FORM CREATE_HIERARCHY, donde creamos la estructura del ALV.

*&----------------------------------------------------* *&

Form

fill_catalog_tree

*

*&----------------------------------------------------* FORM FILL_CATALOG_TREE. DATA: GS_FIELDCAT TYPE LVC_S_FCAT. CLEAR GS_FIELDCAT. GS_FIELDCAT-COL_POS

= 1.

GS_FIELDCAT-FIELDNAME = 'NOM_PROG'. GS_FIELDCAT-SCRTEXT_S = 'Nom. Programa'. GS_FIELDCAT-TABNAME

= 'T_PROGRAMAS'.

GS_FIELDCAT-OUTPUTLEN = 20. APPEND GS_FIELDCAT TO GT_FIELDCAT_TREE.

396

CLEAR GS_FIELDCAT. GS_FIELDCAT-COL_POS

= 2.

GS_FIELDCAT-FIELDNAME = 'ENTORNO'. GS_FIELDCAT-SCRTEXT_S = 'Entorno'. GS_FIELDCAT-TABNAME

= 'T_PROGRAMAS'.

GS_FIELDCAT-OUTPUTLEN = 15. APPEND GS_FIELDCAT TO GT_FIELDCAT_TREE. ENDFORM.

"fill_catalog_tree

Creamos el catálogo del ALV.

*&----------------------------------------------------* *

FORM build_hierarchy_header

*

*&----------------------------------------------------* FORM BUILD_HIERARCHY_HEADER CHANGING P_HIERARCHY_HEADER TYPE TREEV_HHDR. CLEAR P_HIERARCHY_HEADER. P_HIERARCHY_HEADER-HEADING = 'Código'(300). P_HIERARCHY_HEADER-WIDTH = 60. P_HIERARCHY_HEADER-WIDTH_PIX = ' '. ENDFORM.

"BUILD_HIERARCHY_HEADER

La cabecera del ALV va a tener el título “Código”, una longitud de 60.

*&----------------------------------------------------* *&

Form

create_hierarchy

*

397

*&----------------------------------------------------* FORM CREATE_HIERARCHY. DATA: L_ROOT_KEY TYPE LVC_NKEY, L_NEXT_KEY TYPE LVC_NKEY, L_LAST_KEY TYPE LVC_NKEY, HEADER TYPE STRING, W_MENGE_TEXT(13) TYPE C. CLEAR L_ROOT_KEY. LOOP AT T_HEADER ASSIGNING . HEADER = -LENGUAJE. CLEAR L_ROOT_KEY. CLEAR L_NEXT_KEY. PERFORM ADD_NODE USING HEADER L_ROOT_KEY CHANGING L_NEXT_KEY. LOOP AT T_PROGRAMAS ASSIGNING WHERE LENGUAJE EQ -LENGUAJE. MOVE-CORRESPONDING TO T_TREE. PERFORM ADD_LEAF USING T_TREE L_NEXT_KEY CHANGING L_LAST_KEY. ENDLOOP. ENDLOOP. CALL METHOD G_ALV_TREE->FRONTEND_UPDATE. ENDFORM.

"CREATE_HIERARCHY

398

Recorremos los registros de la tabla T_HEADER, asignamos el valor del campo LENGUAJE al campo HEADER (Esta variable dará un texto al NODO). Llamamos al FORM ADD_NODE para agregar un nuevo nodo. Luego recorremos todos los registros de la tabla T_PROGRAMAS buscando los registros que tengan el campo LENGUAJE igual que el nodo, esto para poder agregarlos utilizando el FORM ADD_LEAF. Una

vez

que

terminamos

llamámos

al

método

FRONTEND_UPDATE para refrescar los valores del ALV.

*&----------------------------------------------------* *

FORM ADD_NODE

*

*&----------------------------------------------------* FORM ADD_NODE USING L_NAME L_ROOT_KEY CHANGING L_NEXT_KEY. DATA: L_NODE_TEXT TYPE LVC_VALUE, LS_TREE TYPE TY_PROGRAMAS. L_NODE_TEXT =

L_NAME.

CALL METHOD G_ALV_TREE->ADD_NODE EXPORTING I_RELAT_NODE_KEY = L_ROOT_KEY I_RELATIONSHIP

=

CL_GUI_COLUMN_TREE=>RELAT_LAST_CHILD I_NODE_TEXT

= L_NODE_TEXT

IS_OUTTAB_LINE

= LS_TREE

IMPORTING E_NEW_NODE_KEY

= L_NEXT_KEY.

399

ENDFORM.

"ADD_NODE

Llamámos al método ADD_NODE para crear un nuevo nodo. *&----------------------------------------------------* *

FORM ADD_LEAF

*

*&----------------------------------------------------* FORM ADD_LEAF USING L_TREE TYPE TY_PROGRAMAS L_NEXT_KEY CHANGING L_LAST_KEY. CALL METHOD G_ALV_TREE->ADD_NODE EXPORTING I_RELAT_NODE_KEY = L_NEXT_KEY I_RELATIONSHIP

=

CL_GUI_COLUMN_TREE=>RELAT_LAST_CHILD IS_OUTTAB_LINE

= T_TREE

IMPORTING E_NEW_NODE_KEY ENDFORM.

= L_LAST_KEY. "ADD_LEAF

Llamámos al método ADD_NODE para crear un nuevo nodo. Aunque claro, pasamos otro parámetro para que se cree como subnodo. Grabamos, activamos y ejecutamos.

400

Agregar validaciones y eventos Si bien el programa funciona, no es muy útil que digamos puesto que no hace...nada...Así que vamos a agregar algunas cosas adicionales...

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: OK_CODE TYPE SY-UCOMM, G_ALV_TREE TYPE REF TO CL_GUI_ALV_TREE, G_CUSTOM_CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER, L_HIERARCHY_HEADER TYPE TREEV_HHDR, G_LINE_BEHAVIOUR TYPE REF TO CL_DRAGDROP, G_FAV_BEHAVIOUR TYPE REF TO CL_DRAGDROP, L_LAST_KEY TYPE LVC_NKEY, W_NODE_KEY TYPE LVC_NKEY.

401

Agregamos algunas variables, G_LINE_BEHAVIOUR objeto que determina que el nodo puede moverse. G_FAV_BEHAVIOUR objeto que determina que el folder puede aceptar un nuevo nodo. L_LAST_KEY determina el nodo padre. W_NODE_KEY variable para almacenar el nodo que se ha movido de carpeta.

*-----------------------------------------------------* *

CLASS LCL_DRAGDROPOBJ DEFINITION

*

*-----------------------------------------------------* CLASS LCL_DRAGDROPOBJ DEFINITION. PUBLIC SECTION. DATA: CPS_PROGRAMAS TYPE TY_PROGRAMAS, CP_NODE_TEXT TYPE LVC_VALUE, CP_NODE_KEY TYPE LVC_NKEY. ENDCLASS.

"LCL_DRAGDROPOBJ DEFINITION

Definimos algunas variables de clase. CPS_PROGRAMAS sirve como una cabecera con los datos de los nodos. CP_NODE_TEXT almacena el texto del nodo y CP_NODE_KEY almacena el índice del nodo.

*-----------------------------------------------------* *

CLASS lcl_dnd_event_receiver DEFINITION

*

*-----------------------------------------------------* CLASS LCL_DND_EVENT_RECEIVER DEFINITION. PUBLIC SECTION. METHODS:

402

HANDLE_LINE_DRAG FOR EVENT ON_DRAG OF CL_GUI_ALV_TREE IMPORTING SENDER NODE_KEY FIELDNAME DRAG_DROP_OBJECT, HANDLE_FAV_DROP FOR EVENT ON_DROP OF CL_GUI_ALV_TREE IMPORTING SENDER NODE_KEY DRAG_DROP_OBJECT. ENDCLASS.

"lcl_dnd_event_receiver DEFINITION

Declaramos dos métodos para manejar el Drag & Drop (Arrastrar y soltar) en el ALV.

*-----------------------------------------------------* *

CLASS lcl_dnd_event_receiver IMPLEMENTATION

*

*-----------------------------------------------------* CLASS LCL_DND_EVENT_RECEIVER IMPLEMENTATION. METHOD HANDLE_LINE_DRAG. DATA: DATAOBJ TYPE REF TO LCL_DRAGDROPOBJ. CREATE OBJECT DATAOBJ. DATAOBJ->CP_NODE_KEY = NODE_KEY. CALL METHOD SENDER->GET_OUTTAB_LINE EXPORTING I_NODE_KEY

= NODE_KEY

IMPORTING E_OUTTAB_LINE = DATAOBJ->CPS_PROGRAMAS E_NODE_TEXT

= DATAOBJ->CP_NODE_TEXT.

403

DRAG_DROP_OBJECT->OBJECT = DATAOBJ. ENDMETHOD.

"HANDLE_LINE_DRAG

METHOD HANDLE_FAV_DROP. DATA: DATAOBJ TYPE REF TO LCL_DRAGDROPOBJ, L_NEW_KEY TYPE LVC_NKEY. CATCH SYSTEM-EXCEPTIONS MOVE_CAST_ERROR = 1. DATAOBJ ?= DRAG_DROP_OBJECT->OBJECT. W_NODE_KEY = DATAOBJ->CP_NODE_KEY. PERFORM ADD_LEAF USING DATAOBJ->CPS_PROGRAMAS NODE_KEY CHANGING DATAOBJ->CP_NODE_KEY. CALL METHOD G_ALV_TREE->DELETE_SUBTREE EXPORTING I_NODE_KEY = W_NODE_KEY. CALL METHOD SENDER->FRONTEND_UPDATE. ENDCATCH. IF SY-SUBRC 0. CALL METHOD DRAG_DROP_OBJECT->ABORT. ENDIF. ENDMETHOD. ENDCLASS.

"HANDLE_FAV_DROP "lcl_dnd_event_receiver IMPLEMENTATION

En el método HANDLE_LINE_DRAG, creamos un objeto DATAOBJ y le asignamos el contenido del nodo. Con el método GET_OUTTAB_LINE

llenamos

las

variables 404

CPS_PROGRAMAS y CP_NODE_TEXT con los valores correspondientes al nodo. Asignamos el nodo al evento de Drag & Drop. En el método HANDLE_FAV_DROP, hacemos un CATCH SYSTEM-EXCEPTION por si hay algún problema al mover el nodo de una carpeta a otra. Asignamos el evento de Drag & Drop a una variable. Asignamos el valor del nodo que hemos movido a una variable auxiliar W_NODE_KEY. Llamamos al FORM ADD_LEAF con el nodo que hemos movido de una carpeta a otra. Llamamos al método DELETE_SUBTREE con el valor de la variable W_NODE_KEY para poder eliminar al nodo de su posición original. Llamamos al método FRONTEND_UPDATE para actualizar el ALV.

PERFORM DEFINE_DND_BEHAVIOUR.

DATA: LT_EVENTS TYPE CNTL_SIMPLE_EVENTS, L_EVENT TYPE CNTL_SIMPLE_EVENT, L_DND_EVENT_RECEIVER TYPE REF TO LCL_DND_EVENT_RECEIVER. CALL METHOD G_ALV_TREE->GET_REGISTERED_EVENTS IMPORTING EVENTS = LT_EVENTS. CALL METHOD G_ALV_TREE->SET_REGISTERED_EVENTS

405

EXPORTING EVENTS

= LT_EVENTS

EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR

= 2

ILLEGAL_EVENT_COMBINATION = 3. CREATE OBJECT L_DND_EVENT_RECEIVER. SET HANDLER L_DND_EVENT_RECEIVER->HANDLE_FAV_DROP FOR G_ALV_TREE. SET HANDLER L_DND_EVENT_RECEIVER->HANDLE_LINE_DRAG FOR G_ALV_TREE. CALL METHOD G_ALV_TREE->FRONTEND_UPDATE.

Al final de INIT_TREE, agregamos este código. El FORM DEFINE_DND_BEHAVIOUR determina el tipo de Drag & Drop que se utilizará tanto para los nodos como para las carpetas. Las

tablas

internas

LT_EVENTS,

L_EVENT,

L_DND_EVENT_RECIEVER

y

LCL_DND_EVENT_RECIEVER, nos sirven para registrar y utilizar

los

eventos.

Para

ello,

utilizamos

los

métodos

GET_REGISTERED_EVENTS SET_REGISTERED_EVENTS.

y Además,

establecemos

los

Handlers (Manejadores) para los eventos HANDLE_FAV_DROP y HANDLE_LINE_DRAG.

406

Finalmente, llamamos al método FRONTEND_UPDATE para actualizar el ALV.

*&----------------------------------------------------* *

FORM ADD_NODE

*

*&----------------------------------------------------* FORM ADD_NODE USING L_NAME L_ROOT_KEY CHANGING L_NEXT_KEY. DATA: L_NODE_TEXT TYPE LVC_VALUE, LS_TREE TYPE TY_PROGRAMAS, L_LAYOUT_NODE TYPE LVC_S_LAYN, L_HANDLE_FAVOURITE_FOLDER TYPE I. CALL METHOD G_FAV_BEHAVIOUR->GET_HANDLE IMPORTING HANDLE = L_HANDLE_FAVOURITE_FOLDER. L_LAYOUT_NODE-DRAGDROPID = L_HANDLE_FAVOURITE_FOLDER. L_LAYOUT_NODE-ISFOLDER = 'X'. L_NODE_TEXT =

L_NAME.

CALL METHOD G_ALV_TREE->ADD_NODE EXPORTING I_RELAT_NODE_KEY = L_ROOT_KEY I_RELATIONSHIP

=

CL_GUI_COLUMN_TREE=>RELAT_LAST_CHILD I_NODE_TEXT

= L_NODE_TEXT

IS_NODE_LAYOUT

= L_LAYOUT_NODE

IS_OUTTAB_LINE

= LS_TREE

IMPORTING E_NEW_NODE_KEY

= L_NEXT_KEY.

407

ENDFORM.

"ADD_NODE

Modificamos un poco el FORM ADD_NODE. Las variables L_LAYOUT y L_HANDLE_FAVOURITE_FOLDER, nos van a ayudar para establecer que los nodos son carpetas, y que pueden aceptar un subnodo dentro de ellas. Utilizamos el método GET_HANDLE para obtener el manejador que nos permita asignar un ID al nodo. Pasamos

el

valor

de

L_HANDLE_FAVOURITE_FOLDER

la hacia

variable el

campo

DRAGDROPID de la variable L_LAYOUT_NODE, además, asignamos el valor “X” al campo ISFOLDER de la misma variable. Debemos pasar el parámetro IS_NODE_LAYOUT al método ADD_NODE.

*&----------------------------------------------------* *

FORM ADD_LEAF

*

*&----------------------------------------------------* FORM ADD_LEAF USING L_TREE TYPE TY_PROGRAMAS L_NEXT_KEY CHANGING L_LAST_KEY. DATA: L_LAYOUT_NODE TYPE LVC_S_LAYN, L_HANDLE_LINE TYPE I. CALL METHOD G_LINE_BEHAVIOUR->GET_HANDLE IMPORTING HANDLE = L_HANDLE_LINE.

408

L_LAYOUT_NODE-DRAGDROPID = L_HANDLE_LINE. CALL METHOD G_ALV_TREE->ADD_NODE EXPORTING I_RELAT_NODE_KEY = L_NEXT_KEY I_RELATIONSHIP

=

CL_GUI_COLUMN_TREE=>RELAT_LAST_CHILD IS_OUTTAB_LINE

= L_TREE

IS_NODE_LAYOUT

= L_LAYOUT_NODE

IMPORTING E_NEW_NODE_KEY

= L_LAST_KEY.

ENDFORM.

"ADD_LEAF

Modificamos un poco el FORM ADD_LEAF. Las variables L_LAYOUT_NODE y L_HANDLE_LINE nos va a servir para establecer que los nodos pueden moverse de una hacia otra carpeta. Utilizando el método GET_HANDLE asignamos un manejador a la variable

L_LAYOUT_NODE-DRAGDROPID.

Pasamos

el

parámetro IS_NODE_LAYOUT al método ADD_NODE.

*&----------------------------------------------------* *&

Form

DEFINE_DND_BEHAVIOUR

**&---------------------------------------------------* FORM DEFINE_DND_BEHAVIOUR. DATA: EFFECT TYPE I. CREATE OBJECT G_LINE_BEHAVIOUR. EFFECT = CL_DRAGDROP=>COPY. CALL METHOD G_LINE_BEHAVIOUR->ADD

409

EXPORTING FLAVOR

= 'X'

DRAGSRC

= 'X'

"#EC NOTEXT

DROPTARGET = ' ' EFFECT

= EFFECT.

CREATE OBJECT G_FAV_BEHAVIOUR. EFFECT = CL_DRAGDROP=>COPY. CALL METHOD G_FAV_BEHAVIOUR->ADD EXPORTING FLAVOR

= 'X'

DRAGSRC

= ' '

"#EC NOTEXT

DROPTARGET = 'X' EFFECT

= EFFECT.

ENDFORM.

Creamos

" DEFINE_DND_BEHAVIOUR

dos

objetos

G_LINE_BEHAVIOUR

y

G_FAV_BEHAVIOUR. Con cada uno de ellos, llamaremos al método ADD, determinando cual se puede arrastrar y en donde se puede soltar. Es decir, cual funciona como objeto y cual como contenedor.

410

Crear un ALV Object Model Así como el ABAP evolucionó para mejor, el ALV hizo lo propio y ahora tenemos disponible el ALV Object Model, una forma de crear ALV’s más orientada a objetos que la anterior (Si es que esto puede ser posible). Este es el código.

*=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_PROGRAMAS, ID_PROG TYPE ZVLENGUAJES_PROG-ID_PROG, LENGUAJE TYPE ZVLENGUAJES_PROG-LENGUAJE,

411

NOM_PROG TYPE ZVLENGUAJES_PROG-NOM_PROG, ENTORNO TYPE ZVLENGUAJES_PROG-ENTORNO, END OF TY_PROGRAMAS. *=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS, T_TABLE TYPE REF TO CL_SALV_TABLE, T_FUNCTIONS TYPE REF TO CL_SALV_FUNCTIONS, T_DSPSET TYPE REF TO CL_SALV_DISPLAY_SETTINGS, LR_COLUMN TYPE REF TO CL_SALV_COLUMN_TABLE, LV_SALV_COLUMNS_TABLE TYPE REF TO CL_SALV_COLUMNS_TABLE.

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. PERFORM CARGAR_DATOS. PERFORM LLAMAR_ALV. *&----------------------------------------------------* *&

Form

CARGAR_DATOS

*

*&----------------------------------------------------* FORM CARGAR_DATOS. SELECT ID_PROG LENGUAJE NOM_PROG ENTORNO INTO TABLE T_PROGRAMAS FROM ZVLENGUAJES_PROG.

412

ENDFORM.

" CARGAR_DATOS

*&----------------------------------------------------* *&

Form

LLAMAR_ALV

*

*&----------------------------------------------------* FORM LLAMAR_ALV. TRY. CL_SALV_TABLE=>FACTORY( IMPORTING R_SALV_TABLE

= T_TABLE

CHANGING T_TABLE

= T_PROGRAMAS ).

CATCH CX_SALV_MSG . ENDTRY. LV_SALV_COLUMNS_TABLE = T_TABLE->GET_COLUMNS( ). LR_COLUMN ?= LV_SALV_COLUMNS_TABLE->GET_COLUMN( 'ID_PROG' ). LR_COLUMN->SET_LONG_TEXT( 'Id' ). LV_SALV_COLUMNS_TABLE = T_TABLE->GET_COLUMNS( ). LR_COLUMN ?= LV_SALV_COLUMNS_TABLE->GET_COLUMN( 'NOM_PROG' ). LR_COLUMN->SET_LONG_TEXT( 'Programa' ). T_FUNCTIONS = T_TABLE->GET_FUNCTIONS( ). T_FUNCTIONS->SET_ALL( ABAP_TRUE ). T_DSPSET = T_TABLE->GET_DISPLAY_SETTINGS( ). T_DSPSET->SET_LIST_HEADER( 'Programas' ).

413

T_TABLE->DISPLAY( ). ENDFORM.

" LLAMAR_ALV

Como se darán cuenta, es un código bastante pequeño, pero de todos modos vamos a revisarlo un poco. *=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_PROGRAMAS, ID_PROG TYPE ZVLENGUAJES_PROG-ID_PROG, LENGUAJE TYPE ZVLENGUAJES_PROG-LENGUAJE, NOM_PROG TYPE ZVLENGUAJES_PROG-NOM_PROG, ENTORNO TYPE ZVLENGUAJES_PROG-ENTORNO, END OF TY_PROGRAMAS.

Declaramos un TYPES con todos los campos de la vista ZVLENGUAJES_PROG.

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS, T_TABLE TYPE REF TO CL_SALV_TABLE, T_FUNCTIONS TYPE REF TO CL_SALV_FUNCTIONS, T_DSPSET TYPE REF TO CL_SALV_DISPLAY_SETTINGS, LR_COLUMN TYPE REF TO CL_SALV_COLUMN_TABLE, LV_SALV_COLUMNS_TABLE TYPE REF TO CL_SALV_COLUMNS_TABLE.

414

Declaramos varias tablas internas, T_PROGRAMAS que guardará los datos que obtengamos de la vista. T_TABLE es un parámetro para el método FACTORY y sirve para crear la estructura del ALV. T_FUNCTIONS sirve para asignar las funciones de la barra de menús del ALV. T_DSPSET nos sirve para las opciones de visualización. LR_COLUMN nos permite tomar las características de una columna del ALV. LV_SALV_COLUMNS_TABLE nos permite tomar todas las columnas de ALV.

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. PERFORM CARGAR_DATOS. PERFORM LLAMAR_ALV.

Tenemos dos FORM’s CARGAR_DATOS (Donde cargamos los datos) y LLAMAR_ALV (Donde llamamos al ALV)...Bastante obvio no?

*&----------------------------------------------------* *&

Form

CARGAR_DATOS

*

*&----------------------------------------------------* FORM CARGAR_DATOS. SELECT ID_PROG LENGUAJE NOM_PROG ENTORNO INTO TABLE T_PROGRAMAS FROM ZVLENGUAJES_PROG.

415

ENDFORM.

" CARGAR_DATOS

Seleccionamos

todos

los

campos

de

la

vista

ZVLENGUAJES_PROG.

*&----------------------------------------------------* *&

Form

LLAMAR_ALV

*

*&----------------------------------------------------* FORM LLAMAR_ALV. TRY. CL_SALV_TABLE=>FACTORY( IMPORTING R_SALV_TABLE

= T_TABLE

CHANGING T_TABLE

= T_PROGRAMAS ).

CATCH CX_SALV_MSG . ENDTRY. LV_SALV_COLUMNS_TABLE = T_TABLE->GET_COLUMNS( ). LR_COLUMN ?= LV_SALV_COLUMNS_TABLE->GET_COLUMN( 'ID_PROG' ). LR_COLUMN->SET_LONG_TEXT( 'Id' ). LV_SALV_COLUMNS_TABLE = T_TABLE->GET_COLUMNS( ). LR_COLUMN ?= LV_SALV_COLUMNS_TABLE->GET_COLUMN( 'NOM_PROG' ). LR_COLUMN->SET_LONG_TEXT( 'Programa' ). T_FUNCTIONS = T_TABLE->GET_FUNCTIONS( ). T_FUNCTIONS->SET_ALL( ABAP_TRUE ).

416

T_DSPSET = T_TABLE->GET_DISPLAY_SETTINGS( ). T_DSPSET->SET_LIST_HEADER( 'Programas' ). T_TABLE->DISPLAY( ). ENDFORM.

Llamámos

" LLAMAR_ALV

al

método

estático

FACTORY

de

la

clase

CL_SALV_TABLE, pasando dos únicos parámetros, T_TABLE que contendrá la estructura del ALV y T_PROGRAMAS, que tiene los registros obtenidos de la vista. Utilizando LV_SALV_COLUMNS_TABLE = T_TABLE->GET_COLUMNS( ).

Obtenemos todas las columnas del ALV. LR_COLUMN ?= LV_SALV_COLUMNS_TABLE>GET_COLUMN( 'ID_PROG' ).

Obtenemos las características de la columna ID_PROG. LR_COLUMN->SET_LONG_TEXT( 'Id' ).

Establecemos cual es texto de la columna. Porque hacemos esto? Pues muy simple, tanto ID_PROG como NOM_PROG no tienen asignado un elemento de datos, por lo tanto, el ALV no puede determinar cual es el texto que le corresponde, así que nos toca a nosotros asignarlo. Continuando, utilizamos T_FUNCTIONS = T_TABLE->GET_FUNCTIONS( ).

Para obtener las funciones standard del ALV T_FUNCTIONS->SET_ALL( ABAP_TRUE ).

Establecemos que queremos utilizar todas las funciones. T_DSPSET = T_TABLE->GET_DISPLAY_SETTINGS( ).

417

Obtenemos las características de salida. T_DSPSET->SET_LIST_HEADER( 'Programas' ).

Asignamos un título al reporte. T_TABLE->DISPLAY( ).

Llamamos y mostramos el ALV.

Agregar validaciones y eventos Si bien el programa funciona, no es muy útil que digamos puesto que no hace...nada...Así que vamos a agregar algunas cosas adicionales...

CLASS LCL_HANDLE_EVENTS DEFINITION DEFERRED.

Vamos a redefinir a la clase LCL_HANDLE_EVENTS.

418

*-----------------------------------------------------* *

CLASS lcl_handle_events DEFINITION

*

*-----------------------------------------------------* CLASS LCL_HANDLE_EVENTS DEFINITION. PUBLIC SECTION. METHODS: ON_DOUBLE_CLICK FOR EVENT DOUBLE_CLICK OF CL_SALV_EVENTS_TABLE IMPORTING ROW COLUMN. ENDCLASS.

"lcl_handle_events DEFINITION

Definimos un método llamado ON_DOUBLE_CLICK.

*-----------------------------------------------------* *

CLASS lcl_handle_events IMPLEMENTATION

*

*-----------------------------------------------------* CLASS LCL_HANDLE_EVENTS IMPLEMENTATION. METHOD ON_DOUBLE_CLICK. PERFORM MOSTRAR_DETALLE USING ROW COLUMN. ENDMETHOD.

"on_double_click

ENDCLASS.

Implementamos

"lcl_handle_events IMPLEMENTATION

el

método

llamado

al

FORM

MOSTRAR_DETALLE.

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_PROGRAMAS TYPE STANDARD TABLE OF TY_PROGRAMAS, T_TABLE TYPE REF TO CL_SALV_TABLE,

419

T_FUNCTIONS TYPE REF TO CL_SALV_FUNCTIONS, T_DSPSET TYPE REF TO CL_SALV_DISPLAY_SETTINGS, LR_COLUMN TYPE REF TO CL_SALV_COLUMN_TABLE, LV_SALV_COLUMNS_TABLE TYPE REF TO CL_SALV_COLUMNS_TABLE, GR_EVENTS TYPE REF TO LCL_HANDLE_EVENTS.

El objeto GR_EVENTS nos permitirá registrar los eventos del ALV.

*=====================================================* * DECLARACION DE FIELD-SYMBOLS

*

*=====================================================* FIELD-SYMBOLS: LIKE LINE OF T_PROGRAMAS.

Declaramos un Field-Symbol.

PERFORM PROCESS_EVENTS. T_TABLE->DISPLAY( ).

Dentro

de

LLAMAR_ALV,

agregamos

el

FORM

PROCESS_EVENTS, que lo que va a hacer es generar el evento del Doble Clic.

420

*-----------------------------------------------------* *&

Form

mostrar_detalle

*

*-----------------------------------------------------* FORM MOSTRAR_DETALLE USING P_ROW P_COLUMN. DATA: MENSAJE TYPE SHKONTEXT-MELDUNG. IF P_COLUMN EQ 'NOM_PROG'. READ TABLE T_PROGRAMAS INDEX P_ROW ASSIGNING . CONCATENATE -ID_PROG -NOM_PROG -LENGUAJE -ENTORNO INTO MENSAJE SEPARATED BY SPACE. CALL FUNCTION 'MESSAGE_TEXT_DISPLAY_WITH_PARA' EXPORTING TEXT = MENSAJE. ENDIF. ENDFORM.

" mostrar_detalle

Si hemos hecho doble clic sobre un registro de la columna NOM_PROG, entonces leemos el registro, concatenamos los valores en un cadena y llamamos al módulo de funciones MESSAGE_TEXT_DISPLAY_WITH_PARA para mostrar una ventana con los datos.

421

*&----------------------------------------------------* *&

Form

process_events

*

*&----------------------------------------------------* FORM PROCESS_EVENTS. DATA: LR_EVENTS TYPE REF TO CL_SALV_EVENTS_TABLE. LR_EVENTS = T_TABLE->GET_EVENT( ). CREATE OBJECT GR_EVENTS. SET HANDLER GR_EVENTS->ON_DOUBLE_CLICK FOR LR_EVENTS. ENDFORM.

" process_events

Utilizando el método estático GET_EVENT, obtenemos el evento definido para nuestro ALV. Creamos el objeto GR_EVENTS y le asignamos el HANDLER ON_DOUBLE_CLICK, es decir, le decimos que reaccione ante el doble clic. Este sería el resultado del reporte.

422

Cargar imágenes en Dynpros Esto quizás no era muy común o requerido en los tiempos anteriores a la orientación a objetos, pero ahora, creanmé que es muy útil. Este es el código.

*=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_GRAPHIC_TABLE, LINE(255) TYPE X, END OF TY_GRAPHIC_TABLE. *=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA:

T_GRAPHIC_TABLE TYPE STANDARD TABLE OF TY_GRAPHIC_TABLE.

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: OK_CODE TYPE SY-UCOMM, URL(255) TYPE C, CONTAINER1 TYPE REF TO CL_GUI_CUSTOM_CONTAINER, PICTURE TYPE REF TO CL_GUI_PICTURE, L_BYTECOUNT TYPE I, L_CONTENT TYPE STANDARD TABLE OF BAPICONTEN INITIAL SIZE 0, GRAPHIC_SIZE TYPE I.

423

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. CALL SCREEN 0100. *&----------------------------------------------------* *&

Module

STATUS_0100

OUTPUT

*

*&----------------------------------------------------* MODULE STATUS_0100 OUTPUT. SET PF-STATUS '100'. SET TITLEBAR '100'. PERFORM LOAD_IMAGE. ENDMODULE.

" STATUS_0100

OUTPUT

*&----------------------------------------------------* *&

Module

USER_COMMAND_0100

INPUT

*

*&----------------------------------------------------* MODULE USER_COMMAND_0100 INPUT. OK_CODE = SY-UCOMM. CLEAR SY-UCOMM. CASE OK_CODE. WHEN 'BACK' OR 'STOP' OR 'CANCEL'. SET SCREEN 0. LEAVE SCREEN. ENDCASE. ENDMODULE.

" USER_COMMAND_0100

INPUT

424

*&----------------------------------------------------* *&

Form

load_image

*

*&----------------------------------------------------* FORM LOAD_IMAGE. CREATE OBJECT: CONTAINER1 EXPORTING CONTAINER_NAME = 'CUSTOM_ALV', PICTURE EXPORTING PARENT = CONTAINER1. CALL FUNCTION 'SAPSCRIPT_GET_GRAPHIC_BDS' EXPORTING I_OBJECT

= 'GRAPHICS'

I_NAME

= 'ENJOY'

I_ID

= 'BMAP'

I_BTYPE

= 'BCOL'

IMPORTING E_BYTECOUNT

= L_BYTECOUNT

TABLES CONTENT

= L_CONTENT

EXCEPTIONS NOT_FOUND

= 1

BDS_GET_FAILED = 2 BDS_NO_CONTENT = 3 OTHERS

= 4.

CALL FUNCTION 'SAPSCRIPT_CONVERT_BITMAP' EXPORTING OLD_FORMAT

= 'BDS'

NEW_FORMAT

= 'BMP'

BITMAP_FILE_BYTECOUNT_IN = L_BYTECOUNT IMPORTING BITMAP_FILE_BYTECOUNT

= GRAPHIC_SIZE

425

TABLES BDS_BITMAP_FILE

= L_CONTENT

BITMAP_FILE

= T_GRAPHIC_TABLE

EXCEPTIONS OTHERS

= 1.

CALL FUNCTION 'DP_CREATE_URL' EXPORTING TYPE

= 'IMAGE'

SUBTYPE = 'BMP' TABLES DATA

= T_GRAPHIC_TABLE

CHANGING URL

= URL.

CALL METHOD PICTURE->LOAD_PICTURE_FROM_URL EXPORTING URL = URL. CALL METHOD PICTURE->SET_DISPLAY_MODE EXPORTING DISPLAY_MODE = PICTURE->DISPLAY_MODE_FIT_CENTER. ENDFORM.

" load_image

Revisemos el código por partes. Es un código sencillo y corto, así que será fácil entenderlo. *=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_GRAPHIC_TABLE, LINE(255) TYPE X, END OF TY_GRAPHIC_TABLE.

426

Declaramos un TYPE llamado TY_GRAPHIC_TABLE que contiene un único campo, llamado LINE de 255 caracteres y de tipo X. Esto es porque las imágenes de almacenan en formato hexadecimal.

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA:

T_GRAPHIC_TABLE TYPE STANDARD TABLE OF TY_GRAPHIC_TABLE.

Declaramos una tabla interna de tipo TY_GRAPHIC_TABLE.

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: OK_CODE TYPE SY-UCOMM, URL(255) TYPE C, CONTAINER1 TYPE REF TO CL_GUI_CUSTOM_CONTAINER, PICTURE TYPE REF TO CL_GUI_PICTURE, L_BYTECOUNT TYPE I, L_CONTENT TYPE STANDARD TABLE OF BAPICONTEN INITIAL SIZE 0, GRAPHIC_SIZE TYPE I.

OK_CODE almacena el valor del código de función. URL almacena una dirección donde se guarda la imagen en forma temporal.

427

CONTAINER1

contiene

el

nombre

de

nuestro

CUSTOM_CONTROL. PICTURE objeto que cargará y mostrará la imagen en el Dynpro. L_BYTECOUNT cuenta el número de bytes de la imagen que queremos visualizar. L_CONTENT almacena el contenido de la imagen. GRAPHIC_SIZE es el tamaño de la imagen luego de convetirla a BMP.

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. CALL SCREEN 0100.

Llamamos al Dynpro 100 para mostrar la imagen.

*&----------------------------------------------------* *&

Module

STATUS_0100

OUTPUT

*

*&----------------------------------------------------* MODULE STATUS_0100 OUTPUT. SET PF-STATUS '100'. SET TITLEBAR '100'. PERFORM LOAD_IMAGE. ENDMODULE.

" STATUS_0100

OUTPUT

428

Llamamos a la barra de menús y al título del programa. Además, en el FORM LOAD_IMAGE vamos a cargar la imagen en el Dynpro.

*&----------------------------------------------------* *&

Form

load_image

*

*&----------------------------------------------------* FORM LOAD_IMAGE. CREATE OBJECT: CONTAINER1 EXPORTING CONTAINER_NAME = 'CUSTOM_ALV', PICTURE EXPORTING PARENT = CONTAINER1. CALL FUNCTION 'SAPSCRIPT_GET_GRAPHIC_BDS' EXPORTING I_OBJECT

= 'GRAPHICS'

I_NAME

= 'ENJOY'

I_ID

= 'BMAP'

I_BTYPE

= 'BCOL'

IMPORTING E_BYTECOUNT

= L_BYTECOUNT

TABLES CONTENT

= L_CONTENT

EXCEPTIONS NOT_FOUND

= 1

BDS_GET_FAILED = 2 BDS_NO_CONTENT = 3 OTHERS

= 4.

CALL FUNCTION 'SAPSCRIPT_CONVERT_BITMAP' EXPORTING OLD_FORMAT

= 'BDS'

NEW_FORMAT

= 'BMP'

429

BITMAP_FILE_BYTECOUNT_IN = L_BYTECOUNT IMPORTING BITMAP_FILE_BYTECOUNT

= GRAPHIC_SIZE

TABLES BDS_BITMAP_FILE

= L_CONTENT

BITMAP_FILE

= T_GRAPHIC_TABLE

EXCEPTIONS OTHERS

= 1.

CALL FUNCTION 'DP_CREATE_URL' EXPORTING TYPE

= 'IMAGE'

SUBTYPE = 'BMP' TABLES DATA

= T_GRAPHIC_TABLE

CHANGING URL

= URL.

CALL METHOD PICTURE->LOAD_PICTURE_FROM_URL EXPORTING URL = URL. CALL METHOD PICTURE->SET_DISPLAY_MODE EXPORTING DISPLAY_MODE = PICTURE->DISPLAY_MODE_FIT_CENTER. ENDFORM.

" load_image

Creamos los objetos CONTAINER1 (Asociado a nuestro CUSTOM_CONTROL) y PICTURE (Mostrará la imagen en el contenedor).

430

Llamamos a la función SAPSCRIPT_GET_GRAPHIC_BDS para obtener los datos de la imagen que queremos mostrar, que en este caso es “ENJOY”. Llamamos a la función SAPSCRIPT_CONVERT_BITMAP para convertir la imagen a un Bitmap. Llamamos a la función DP_CREATE_URL para generar una dirección donde se encuentra almacenada temporalmente la imagen BMP. Llamamos al método LOAD_PICTURE_FROM_URL para cargar la imagen en memoria. Llamamos al método SET_DISPLAY_MODE para mostrar la imagen.

431

Leer PDF’s Bueno, esto si que no creo que lo lleguen a necesitar alguna vez...Pero bueno, vale la pena conocerlo. Este es el código.

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_FILETAB TYPE FILETABLE. *=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: OK_CODE TYPE SY-UCOMM, MY_PDF_VIEWER TYPE REF TO CL_GUI_PDFVIEWER, CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER, W_SUBRC TYPE SY-SUBRC, V_URL TYPE CHAR255. *=====================================================* * DECLARACION DE FIELD-SYMBOLS

*

*=====================================================* FIELD-SYMBOLS: LIKE LINE OF T_FILETAB. *&----------------------------------------------------* *&

SELECTION-SCREEN

*

*&----------------------------------------------------* SELECTION-SCREEN BEGIN OF BLOCK TEST WITH FRAME. PARAMETERS: FILE LIKE RLGRAP-FILENAME.

432

SELECTION-SCREEN END OF BLOCK TEST. *=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. CALL SCREEN 0100. *&----------------------------------------------------* *&

AT SELECTION-SCREEN

*

*&----------------------------------------------------* AT SELECTION-SCREEN ON VALUE-REQUEST FOR FILE. CALL METHOD CL_GUI_FRONTEND_SERVICES=>FILE_OPEN_DIALOG EXPORTING WINDOW_TITLE

= 'Seleccionar archivo'

DEFAULT_FILENAME = '*.pdf' FILE_FILTER

= '*.pdf'

CHANGING FILE_TABLE

= T_FILETAB

RC

= W_SUBRC.

READ TABLE T_FILETAB INDEX 1 ASSIGNING . FILE = . IF FILE IS INITIAL. EXIT. ENDIF.

433

*&----------------------------------------------------* *&

Module

STATUS_0100

OUTPUT

*

*&----------------------------------------------------* MODULE STATUS_0100 OUTPUT. SET PF-STATUS '100'. SET TITLEBAR '100'. IF CONTAINER IS INITIAL. CREATE OBJECT CONTAINER EXPORTING CONTAINER_NAME = 'CUSTOM_ALV' EXCEPTIONS CNTL_ERROR

= 1

OTHERS

= 2.

CREATE OBJECT MY_PDF_VIEWER EXPORTING PARENT

= CONTAINER

EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR = 2 OTHERS

= 3.

IF NOT MY_PDF_VIEWER->HTML_VIEWER IS INITIAL. CLEAR V_URL. V_URL = FILE. CALL METHOD MY_PDF_VIEWER->OPEN_DOCUMENT EXPORTING URL = V_URL. ENDIF.

434

ENDIF. ENDMODULE.

" STATUS_0100

OUTPUT

*&----------------------------------------------------* *&

Module

USER_COMMAND_0100

INPUT

*

*&----------------------------------------------------* MODULE USER_COMMAND_0100 INPUT. OK_CODE = SY-UCOMM. CLEAR SY-UCOMM. CASE OK_CODE. WHEN 'BACK' OR 'STOP' OR 'CANCEL'. SET SCREEN 0. LEAVE SCREEN. ENDCASE. ENDMODULE.

" USER_COMMAND_0100

INPUT

Revisemos un poco el código.

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_FILETAB TYPE FILETABLE.

Declaramos la tabla interna T_FILETAB de tipo FILETABLE, que no es más que una tabla con un campo de 1024 caracteres de longitud.

435

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: OK_CODE TYPE SY-UCOMM, MY_PDF_VIEWER TYPE REF TO CL_GUI_PDFVIEWER, CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER, W_SUBRC TYPE SY-SUBRC, V_URL TYPE CHAR255.

OK_CODE almacena el código de función. MY_PDF_VIEWER es un objeto que nos permite visualizar un ALV. CONTAINER

almacena

el

nombre

de

nuestro

CUSTOM_CONTROL. W_SUBRC almacena el valor de retorno de llamar al método FILE_OPEN_DIALOG. V_URL contiene la ruta donde se encuentra el ALV.

*=====================================================* * DECLARACION DE FIELD-SYMBOLS

*

*=====================================================* FIELD-SYMBOLS: LIKE LINE OF T_FILETAB.

Declaramos un Field-Symbol para la tabla T_FILETAB.

436

*&----------------------------------------------------* *&

SELECTION-SCREEN

*

*&----------------------------------------------------* SELECTION-SCREEN BEGIN OF BLOCK TEST WITH FRAME. PARAMETERS: FILE LIKE RLGRAP-FILENAME. SELECTION-SCREEN END OF BLOCK TEST.

Este parámetro, recogerá la ruta donde se encuentra el PDF que queremos visualizar.

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. CALL SCREEN 0100.

Llamamos a la pantalla 100 de nuestro Dynpro. El evento AT-SELECION-SCREEN se ejecuta cuando llamamos al parámetro FILE.

*&----------------------------------------------------* *&

AT SELECTION-SCREEN

*

*&----------------------------------------------------* AT SELECTION-SCREEN ON VALUE-REQUEST FOR FILE. CALL METHOD CL_GUI_FRONTEND_SERVICES=>FILE_OPEN_DIALOG EXPORTING WINDOW_TITLE

= 'Seleccionar archivo'

DEFAULT_FILENAME = '*.pdf'

437

FILE_FILTER

= '*.pdf'

CHANGING FILE_TABLE

= T_FILETAB

RC

= W_SUBRC.

READ TABLE T_FILETAB INDEX 1 ASSIGNING . FILE = . IF FILE IS INITIAL. EXIT. ENDIF.

El método FILE_OPEN_DIALOG nos muestra una ventana donde podemos elegir la ubicación del archivo. Debemos leer la tabla interna T_FILETAB y asignar su contenido al parámetro FILE.

*&----------------------------------------------------* *&

Module

STATUS_0100

OUTPUT

*

*&----------------------------------------------------* MODULE STATUS_0100 OUTPUT. SET PF-STATUS '100'. SET TITLEBAR '100'. IF CONTAINER IS INITIAL. CREATE OBJECT CONTAINER EXPORTING CONTAINER_NAME = 'CUSTOM_ALV' EXCEPTIONS

438

CNTL_ERROR

= 1

OTHERS

= 2.

CREATE OBJECT MY_PDF_VIEWER EXPORTING PARENT

= CONTAINER

EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR = 2 OTHERS

= 3.

IF NOT MY_PDF_VIEWER->HTML_VIEWER IS INITIAL. CLEAR V_URL. V_URL = FILE. CALL METHOD MY_PDF_VIEWER->OPEN_DOCUMENT EXPORTING URL = V_URL. ENDIF. ENDIF. ENDMODULE.

" STATUS_0100

OUTPUT

Además de asignar la barra de menús y el título al programa, creamos el objeto CONTAINER que hace referencia a nuesteo CUSTOM_CONTROL, creamos el objeto MY_PDF_VIEWER que se mostrará dentro del objeto CONTAINER y llamamos al método OPEN_DOCUMENT para abrir el archivo PDF que cargamos.

439

Comprimir (zip) archivos Este es otro de esos programas que no utilizaremos casi nunca, pero que definitivamente, agregan un gusto especial a la programación en ABAP. Este es el código. *=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: T_XLINE(2048) TYPE X.

440

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: DATA_TAB TYPE STANDARD TABLE OF T_XLINE, T_FILETAB TYPE FILETABLE, RESULT_TAB TYPE MATCH_RESULT_TAB. *=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: CL_ZIP TYPE REF TO CL_ABAP_ZIP, SIZE TYPE I, INPUT_X TYPE XSTRING, OUTPUT_X TYPE XSTRING, W_SUBRC TYPE SY-SUBRC, W_FILE_IN TYPE STRING, W_FILE_OUT TYPE STRING, W_SIZE TYPE I, W_LINE TYPE STRING, W_EXT(4) TYPE C. *=====================================================* * DECLARACION DE FIELD-SYMBOLS

*

*=====================================================* FIELD-SYMBOLS: LIKE LINE OF T_FILETAB, LIKE LINE OF RESULT_TAB. *&----------------------------------------------------* *&

SELECTION-SCREEN

*

*&----------------------------------------------------* SELECTION-SCREEN BEGIN OF BLOCK TEST WITH FRAME. PARAMETERS: FILE_IN LIKE RLGRAP-FILENAME,

441

FILE_OUT LIKE RLGRAP-FILENAME. SELECTION-SCREEN END OF BLOCK TEST. *=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. FIND ALL OCCURRENCES OF '\' IN W_FILE_IN RESULTS RESULT_TAB. DESCRIBE TABLE RESULT_TAB LINES W_SIZE. READ TABLE RESULT_TAB INDEX W_SIZE ASSIGNING . W_SIZE = STRLEN( W_FILE_IN ) – -OFFSET. -OFFSET = -OFFSET + -LENGTH. W_SIZE

= W_SIZE - -LENGTH.

W_LINE = W_FILE_IN+-OFFSET(W_SIZE). CALL METHOD CL_GUI_FRONTEND_SERVICES=>GUI_UPLOAD EXPORTING FILENAME

= W_FILE_IN

FILETYPE

= 'BIN'

IMPORTING FILELENGTH = SIZE CHANGING DATA_TAB

= DATA_TAB.

442

CALL FUNCTION 'SCMS_BINARY_TO_XSTRING' EXPORTING INPUT_LENGTH = SIZE IMPORTING BUFFER

= INPUT_X

TABLES BINARY_TAB

= DATA_TAB.

IF CL_ZIP IS INITIAL. CREATE OBJECT CL_ZIP. ENDIF. CALL METHOD CL_ZIP->ADD EXPORTING NAME

= W_LINE

CONTENT = INPUT_X. CALL METHOD CL_ZIP->SAVE RECEIVING ZIP = OUTPUT_X. REFRESH DATA_TAB. CALL FUNCTION 'SCMS_XSTRING_TO_BINARY' EXPORTING BUFFER

= OUTPUT_X

IMPORTING OUTPUT_LENGTH = SIZE TABLES BINARY_TAB

= DATA_TAB.

443

CALL METHOD CL_GUI_FRONTEND_SERVICES=>GUI_DOWNLOAD EXPORTING BIN_FILESIZE = SIZE FILENAME

= W_FILE_OUT

FILETYPE

= 'BIN'

CHANGING DATA_TAB

= DATA_TAB.

*&----------------------------------------------------* *&

AT SELECTION-SCREEN

*

*&----------------------------------------------------* AT SELECTION-SCREEN ON VALUE-REQUEST FOR FILE_IN. CALL METHOD CL_GUI_FRONTEND_SERVICES=>FILE_OPEN_DIALOG EXPORTING WINDOW_TITLE

= 'Seleccionar archivo'

DEFAULT_FILENAME = '*.*' FILE_FILTER

= '*.*'

CHANGING FILE_TABLE

= T_FILETAB

RC

= W_SUBRC.

READ TABLE T_FILETAB INDEX 1 ASSIGNING . FILE_IN = . W_FILE_IN = FILE_IN. IF FILE_IN IS INITIAL. EXIT. ENDIF. CLEAR T_FILETAB.

444

REFRESH T_FILETAB. AT SELECTION-SCREEN ON VALUE-REQUEST FOR FILE_OUT. CALL METHOD CL_GUI_FRONTEND_SERVICES=>FILE_OPEN_DIALOG EXPORTING WINDOW_TITLE

= 'Seleccionar archivo'

DEFAULT_FILENAME = '*.zip' FILE_FILTER

= '*.zip'

CHANGING FILE_TABLE

= T_FILETAB

RC

= W_SUBRC.

READ TABLE T_FILETAB INDEX 1 ASSIGNING . FILE_OUT = . W_FILE_OUT = FILE_OUT. IF FILE_OUT IS INITIAL. EXIT. ELSE. W_SIZE = STRLEN( W_FILE_OUT ). W_SIZE = W_SIZE - 4. W_EXT = W_FILE_OUT+W_SIZE(4). IF W_EXT NE '.zip'. CONCATENATE W_FILE_OUT '.zip' INTO W_FILE_OUT. FILE_OUT = W_FILE_OUT. ENDIF. ENDIF.

Revisemos un poco el código. Nos es complicado, pero algunas cosas que pueden confundir un poco a simple vista.

445

*=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: T_XLINE(2048) TYPE X.

Declaramos un TYPE llamado T_XLINE de 2048 caracteres de tipo X, es decir Hexadecimal.

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: DATA_TAB TYPE STANDARD TABLE OF T_XLINE, T_FILETAB TYPE FILETABLE, RESULT_TAB TYPE MATCH_RESULT_TAB.

Declaramos una tabla interna llamada DATA_TAB que hace referencia al TYPE T_XLINE, otra tabla llamada T_FILETAB que hace referencia a FILETABLE, que nos sirve para guardar las rutas de

los

archivos

y

finalmente

RESULT_TAB

de

tipo

MATCH_RESULT_TAB, que nos sirve para almacenar las ocurrencias de una búsqueda de texto.

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: CL_ZIP TYPE REF TO CL_ABAP_ZIP, SIZE TYPE I, INPUT_X TYPE XSTRING, OUTPUT_X TYPE XSTRING,

446

W_SUBRC TYPE SY-SUBRC, W_FILE_IN TYPE STRING, W_FILE_OUT TYPE STRING, W_SIZE TYPE I, W_LINE TYPE STRING, W_EXT(4) TYPE C.

CL_ZIP es un objeto que nos permitirá comprimir el documento de entrada. SIZE determina el tamaño del archivo que está siendo subido al servidor. INPUT_X guarda el contenido de convertir el archivo de Binario a Hexadecimal (Archivo de entrada). OUTPUT_X guarda el contenido de convertir el archivo de Hexadecimal a Binario (Archivo de salida). W_SUBRC almacena el resultado de ejecutar la acción de subir los archivos. W_FILE_IN almacena la ruta del archivo de entrada. W_FILE_OUT almacena la ruta del archivo de salida. W_SIZE almacena el tamaño en líneas de la tabla interna RESULT_TAB. W_LINE almacena el nombre final que tendrá el archivo de salida. W_EXT almacena la extensión del archivo de salida.

*=====================================================* * DECLARACION DE FIELD-SYMBOLS

*

*=====================================================*

447

FIELD-SYMBOLS: LIKE LINE OF T_FILETAB, LIKE LINE OF RESULT_TAB.

Declaramos dos Field-Symbols, uno para FILETAB y otro para RESULT_TAB.

*&----------------------------------------------------* *&

SELECTION-SCREEN

*

*&----------------------------------------------------* SELECTION-SCREEN BEGIN OF BLOCK TEST WITH FRAME. PARAMETERS: FILE_IN LIKE RLGRAP-FILENAME, FILE_OUT LIKE RLGRAP-FILENAME. SELECTION-SCREEN END OF BLOCK TEST.

Declaramos dos parámetros, uno de entrada y otro de salida.

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. FIND ALL OCCURRENCES OF '\' IN W_FILE_IN RESULTS RESULT_TAB. DESCRIBE TABLE RESULT_TAB LINES W_SIZE. READ TABLE RESULT_TAB INDEX W_SIZE ASSIGNING .

448

W_SIZE = STRLEN( W_FILE_IN ) – -OFFSET. -OFFSET = -OFFSET + -LENGTH. W_SIZE

= W_SIZE - -LENGTH.

W_LINE = W_FILE_IN+-OFFSET(W_SIZE). CALL METHOD CL_GUI_FRONTEND_SERVICES=>GUI_UPLOAD EXPORTING FILENAME

= W_FILE_IN

FILETYPE

= 'BIN'

IMPORTING FILELENGTH = SIZE CHANGING DATA_TAB

= DATA_TAB.

CALL FUNCTION 'SCMS_BINARY_TO_XSTRING' EXPORTING INPUT_LENGTH = SIZE IMPORTING BUFFER

= INPUT_X

TABLES BINARY_TAB

= DATA_TAB.

IF CL_ZIP IS INITIAL. CREATE OBJECT CL_ZIP. ENDIF. CALL METHOD CL_ZIP->ADD EXPORTING NAME

= W_LINE

CONTENT = INPUT_X.

449

CALL METHOD CL_ZIP->SAVE RECEIVING ZIP = OUTPUT_X. REFRESH DATA_TAB. CALL FUNCTION 'SCMS_XSTRING_TO_BINARY' EXPORTING BUFFER

= OUTPUT_X

IMPORTING OUTPUT_LENGTH = SIZE TABLES BINARY_TAB

= DATA_TAB.

CALL METHOD CL_GUI_FRONTEND_SERVICES=>GUI_DOWNLOAD EXPORTING BIN_FILESIZE = SIZE FILENAME

= W_FILE_OUT

FILETYPE

= 'BIN'

CHANGING DATA_TAB

= DATA_TAB.

Buscamos todas las ocurrencias del símbolo “\” dentro de la variable W_FILE_IN y guardamos el resultado en la tabla interna RESULT_TAB utilizando FIND ALL OCCURRENCES. Utilizamos un DESCRIBE TABLE para determinar la cantidad de registros generados y lo guardamos en W_SIZE. Leemos el último registro de la tabla RESULT_TAB utilizando como índice la cantidad de registros almacenados en la variable W_SIZE.

450

Obtenemos la longitud del archivo de entrada con STRLEN y le restamos el valor OFFSET de la tabla RESULT_TAB (Este valor OFFSET es la ubicación del último símbolo “\” encontrado). Reemplazamos el valor del OFFSET, sumándole a este el valor del campo LENGTH (Es decir, la longitud del símbolo “\”). Restamos a W_SIZE el valor del campo LENGTH. Finalmente, guardamos en W_LINE la subcadena empezando con el valor del OFFSET y tomando el valor de W_SIZE en cantidad de caracteres. Llamamos al método GUI_UPLOAD para subir el contenido del archivo de entrada. Con el módulo de funciones SCMS_BINARY_TO_XSTRING convertimos el contenido del archivo de Binario a Hexadecimal. Creamos el objeto CL_ZIP y llamamos al método ADD para agregar el archivo de entrada al nuevo ZIP, llamamos al método SAVE para crear el archivo ZIP con el archivo de entrada. Con el método SCMS_XSTRING_TO_BINARY convertimos el archivo ZIP de Hexadecimal a Binario y finalmente llamamos al método GUI_DOWNLOAD para descargarlo con el nombre del archivo de salida.

*&----------------------------------------------------* *&

AT SELECTION-SCREEN

*

*&----------------------------------------------------* AT SELECTION-SCREEN ON VALUE-REQUEST FOR FILE_IN. CALL METHOD CL_GUI_FRONTEND_SERVICES=>FILE_OPEN_DIALOG EXPORTING WINDOW_TITLE

= 'Seleccionar archivo'

451

DEFAULT_FILENAME = '*.*' FILE_FILTER

= '*.*'

CHANGING FILE_TABLE

= T_FILETAB

RC

= W_SUBRC.

READ TABLE T_FILETAB INDEX 1 ASSIGNING . FILE_IN = . W_FILE_IN = FILE_IN. IF FILE_IN IS INITIAL. EXIT. ENDIF. CLEAR T_FILETAB. REFRESH T_FILETAB.

Llamamos

al

método

FILE_OPEN_DIALOG

para

poder

almacenar la ruta del archivo de entrada.

452

Crear un Control de Texto Este programa es bastante útil y necesario aunque que quizás no lo utilicen mucho. En lo personal, yo lo he utilizado para proyectos personales y no de trabajo. Este es el código.

*=====================================================* * DECLARACION DE CONSTANTES

*

*=====================================================* CONSTANTS: LINE_LENGTH TYPE I VALUE 254. *=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: OK_CODE TYPE SY-UCOMM, CUSTOM_CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER, TEXT_EDITOR TYPE REF TO CL_GUI_TEXTEDIT. *=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. CALL SCREEN 0100.

453

*&----------------------------------------------------* *&

Module

STATUS_0100

OUTPUT

*

*&----------------------------------------------------* MODULE STATUS_0100 OUTPUT. SET PF-STATUS '100'. SET TITLEBAR '100'. PERFORM CALL_EDITOR. ENDMODULE.

" STATUS_0100

OUTPUT

*&----------------------------------------------------* *&

Module

USER_COMMAND_0100

INPUT

*

*&----------------------------------------------------* MODULE USER_COMMAND_0100 INPUT. OK_CODE = SY-UCOMM. CLEAR SY-UCOMM. CASE OK_CODE. WHEN 'BACK' OR 'STOP' OR 'CANCEL'. SET SCREEN 0. LEAVE SCREEN. ENDCASE. ENDMODULE.

" USER_COMMAND_0100

INPUT

454

*&----------------------------------------------------* *&

Form

CALL_EDITOR

*

*&----------------------------------------------------* FORM CALL_EDITOR . IF TEXT_EDITOR IS INITIAL. CREATE OBJECT CUSTOM_CONTAINER EXPORTING CONTAINER_NAME

= 'CUSTOM_ALV'

EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR

= 2

CREATE_ERROR

= 3

LIFETIME_ERROR

= 4

LIFETIME_DYNPRO_DYNPRO_LINK = 5. CREATE OBJECT TEXT_EDITOR EXPORTING WORDWRAP_MODE

=

CL_GUI_TEXTEDIT=>WORDWRAP_AT_FIXED_POSITION WORDWRAP_POSITION

= LINE_LENGTH

WORDWRAP_TO_LINEBREAK_MODE = CL_GUI_TEXTEDIT=>TRUE PARENT

= CUSTOM_CONTAINER

EXCEPTIONS ERROR_CNTL_CREATE

= 1

ERROR_CNTL_INIT

= 2

ERROR_CNTL_LINK

= 3

ERROR_DP_CREATE

= 4

GUI_TYPE_NOT_SUPPORTED

= 5

OTHERS

= 6.

ENDIF.

455

CALL METHOD CL_GUI_CFW=>FLUSH. ENDFORM.

" CALL_EDITOR

Revisemos el código paso a paso.

*=====================================================* * DECLARACION DE CONSTANTES

*

*=====================================================* CONSTANTS: LINE_LENGTH TYPE I VALUE 254.

Declaramos una constante llamada LINE_LENGTH de tipo I con el valor 254. Esto determina cada cuantos caracteres debería hacer un salto de línea automático.

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: OK_CODE TYPE SY-UCOMM, CUSTOM_CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER, TEXT_EDITOR TYPE REF TO CL_GUI_TEXTEDIT.

La variable OK_CODE almacena el código de función. CUSTOM_CONTAINER es un objeto que creará el contenedor para nuestro control de texto. TEXT_EDITOR hace referencia al objeto que crea el control de texto.

456

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. CALL SCREEN 0100.

Llamamos a la pantalla 100 del Dynpro.

*&----------------------------------------------------* *&

Module

STATUS_0100

OUTPUT

*

*&----------------------------------------------------* MODULE STATUS_0100 OUTPUT. SET PF-STATUS '100'. SET TITLEBAR '100'. PERFORM CALL_EDITOR. ENDMODULE.

" STATUS_0100

OUTPUT

Asignamos el menú y el título del programa. Llamamos al FORM CALL_EDITOR. En donde llamamos al control de texto.

*&----------------------------------------------------* *&

Form

CALL_EDITOR

*

*&----------------------------------------------------* FORM CALL_EDITOR . IF TEXT_EDITOR IS INITIAL. CREATE OBJECT CUSTOM_CONTAINER

457

EXPORTING CONTAINER_NAME

= 'CUSTOM_ALV'

EXCEPTIONS CNTL_ERROR

= 1

CNTL_SYSTEM_ERROR

= 2

CREATE_ERROR

= 3

LIFETIME_ERROR

= 4

LIFETIME_DYNPRO_DYNPRO_LINK = 5. CREATE OBJECT TEXT_EDITOR EXPORTING WORDWRAP_MODE

=

CL_GUI_TEXTEDIT=>WORDWRAP_AT_FIXED_POSITION WORDWRAP_POSITION

= LINE_LENGTH

WORDWRAP_TO_LINEBREAK_MODE = CL_GUI_TEXTEDIT=>TRUE PARENT

= CUSTOM_CONTAINER

EXCEPTIONS ERROR_CNTL_CREATE

= 1

ERROR_CNTL_INIT

= 2

ERROR_CNTL_LINK

= 3

ERROR_DP_CREATE

= 4

GUI_TYPE_NOT_SUPPORTED

= 5

OTHERS

= 6.

ENDIF. CALL METHOD CL_GUI_CFW=>FLUSH. ENDFORM.

Creamos

" CALL_EDITOR

el

contenedor

para

el

control

de

texto

CUSTOM_CONTAINER. Creamos el objeto TEXT_EDITOR. 458

Limpiamos los controles con el método FLUSH.

Como podemos ver, contamos con dos botones Load Local File (Cargar archivo local) archivo local)

y Save as Local File (Grabar como

. Lo cual significa que podemos cargar cualquier

archivo, modificarlo y guardarlo.

459

WebDynpro Introducción El WebDynpro es la nueva forma de desarrollar aplicaciones en NetWeaver, esto es porque está completamente orientado a web y respeta el modelo MVC (Model View Controller – Modelo Vista Controlador). Aunque el Dynpro no está aún muy difundido, es una herramienta muy potente y bastante sencilla de usar.

Creando nuestro primer WebDynpro El WebDynpro, para mi gran tristeza no cuenta con una transacción propia, sino se desarrolla dentro del Object Navigator (Navegador de Objetos), transacción SE80.

460

Elegimos de la lista Web Dynpro Comp. / Intf. (Componente o Interface Web Dynpro).

Asignamos un nombre y presionamos el botón Display

.

Aceptamos en la ventana que nos muestra el sistema.

461

Asignamos una descripción y aceptamos.

Creamos una vista, donde van a estar los componentes que muestran la información. Para esto, hacemos un clic derecho sobre ZDUMMY_DYPRO y seleccionamos Create Æ View (Crear Æ Vista).

462

Asignamos un nombre y una descripción a nuestra vista.

En este momento, el servicio tiene que conectarse con el servidor, así que debemos ingresar nuestro usuario y password.

463

Una vez logeados, tenemos acceso al editor de vistas del WebDynpro.

Debemos ir a la pestaña Context (Contexto) del editor de vistas.

Debemos hacer clic derecho en Context y elegir Create Æ Node (Crear Æ Nodo)

464

Al nodo lo llamamos ZLENG_NODE y como Dictionary Structure (Estructura del Diccionario) le pasamos la vista ZVLENGUAJES_PROG.

Presionamos el botón Add Attribute from Structure (Agregar atributo desde estructura)

.

Seleccionamos todos los campos.

465

Como parámetro de entrada para nuestro programa, vamos a tener un campo que represente el ID, así que creamos un Attribute (Atributo) llamado ID_PROG.

466

Grabamos y regresamos a la pestaña Layout (Disposición). Seleccionamos Standard Container (Contenedor Estándar) del menú izquierdo y luego Group (Grupo).

En el panel derecho, seleccionamos Caption (Título) y cambiamos el atributo Text (Texto).

467

Seleccionamos

Create

Container

Form

(Crear

formulario

contenedor).

En la ventana que aparece a continuación, debemos presionar el botón Context

para poder asignar un valor a nuestro

contenedor.

468

Elegimos el nodo ID_PROG.

Seleccionamos el nodo ID_PROG_LABEL.

Y le asignamos el texto Id en la propiedad Text.

469

Finalmente, tendríamos esto.

Si queremos probar como va quedando nuestro programa, debemos hacer lo siguiente. Seleccionamos ZDUMMY_DYNPRO con un doble clic.

Y arrastramos ZDUMMY_VIEW hacia ZDUMMY_DYNPRO.

470

Grabamos y al momento de activar veremos una ventana como esta.

471

Seleccionamos todos y aceptamos. Una vez que todo se ha activado, debemos

crear

una

WebDynpro

Application

(Aplicación

WebDynpro).

Asignamos un nombre y una descripción y aceptamos.

En la siguiente pantalla, solamente debemos grabar y activar.

472

Presionamos el botón Test/Execute (Probar/Ejecutar)

o

presionamos F8. El navegador nos muestra una pantalla de login, así que debemos logearnos. Para esto, presionamos el botón Log On

.

473

Como podemos ver, nuestro parámetro de entrada aparece perfectamente en el browser de internet.

En este caso, el campo ID_PROG no tiene una ayúda de búsqueda asociada, por lo cual por mucho que presionemos F4, no va a pasar nada. Cerremos el browser y retornemos a nuestro programa.

474

Regresamos a la vista ZDUMMY_VIEW, pestaña Layout. Elegimos el componente Table (Tabla) de la pestaña Standard Complex (Complejos Estándar).

Le asignamos un título y hacemos clic derecho sobre el nodo TABLE para seleccionar Create Binding (Crear Atadura).

475

Escogemos el Context ZLENG_NODE y dejamos todos los registro seleccionados y continuamos.

Asignamos textos a las cabeceras y tendremos algo como esto.

476

Regresamos a la pestaña Standard Simple y seleccionamos el control Button (Botón), el cual tendrá el texto “Mostrar!”.

477

Debemos asignar una acción al botón, por lo cual presionamos el botón OnAction.

Asignamos el nombre SHOW y una descripción breve.

Nos vamos a la pestaña Actions (Acciones) y veremos nuestra acción.

478

Debemos presionar el botón ABAP Routine (Rutina ABAP) para poder asignar el código a nuestra acción.

METHOD ONACTIONSHOW. DATA: NODE_LENG TYPE REF TO IF_WD_CONTEXT_NODE, ITAB_LENG TYPE STANDARD TABLE OF ZVLENGUAJES_PROG. DATA: ID_PROG TYPE STRING. WD_CONTEXT->GET_ATTRIBUTE( exporting NAME = 'ID_PROG' importing VALUE = ID_PROG ). SELECT ID_PROG NOM_PROG LENGUAJE ENTORNO INTO TABLE ITAB_LENG FROM ZVLENGUAJES_PROG WHERE ID_PROG EQ ID_PROG. NODE_LENG =

479

WD_CONTEXT->GET_CHILD_NODE( NAME = 'ZLENG_NODE' ). NODE_LENG->BIND_TABLE( ITAB_LENG ). ENDMETHOD.

Declaramos la variable NODE_LENG que hace referencia a IF_WD_CONTEXT_NODE, esta es una interface que controla los nodos asociados a nuestro programa. ITAB_LENG es una tabla interna que hace referencia a la vista ZVLENGUAJES_PROG. ID_PROG es una variable de tipo String.

WD_CONTEXT->GET_ATTRIBUTE( exporting NAME = 'ID_PROG' importing VALUE = ID_PROG ).

Obtenemos el valor de atributo ID_PROG, es decir, de nuestro parámetro de entrada. Seleccionamos el registro de la vista ZVLENGUAJES_PROG siempre y cuando cumpla con el parámetro ID_PROG.

NODE_LENG = WD_CONTEXT->GET_CHILD_NODE( NAME = 'ZLENG_NODE' ). NODE_LENG->BIND_TABLE( ITAB_LENG ).

Con GET_CHILD_NODE obtenemos el contexto del nodo que estamos utilizando y con BIND_TABLE enviamos los datos de la tabla hacia nuestro table control.

480

Hay algo más que debemos hacer y que es muy importante. Debemos ir a la pestaña Methods (Métodos) y seleccionar WDDOINIT que es una acción que se lanza cuando se ejecuta el programa por primera vez.

method WDDOINIT. DATA: NODE_LENG TYPE REF TO IF_WD_CONTEXT_NODE, ITAB_LENG TYPE STANDARD TABLE OF ZVLENGUAJES_PROG. APPEND INITIAL LINE TO ITAB_LENG. NODE_LENG = WD_CONTEXT->GET_CHILD_NODE( NAME = 'ZLENG_NODE' ). NODE_LENG->BIND_TABLE( ITAB_LENG ). endmethod.

Lo más resaltante de este código, es que agregamos una línea de cabecera a nuestra tabla interna, para que el Table Control tenga algo que mostrar cuando lanzemos el programa.

481

Todo se ve muy bien, pero estamos limitados a buscar solamente un valor...Así que vamos a agregar otra caja de texto para poder crear un rango de búsqueda. 482

Regresamos a la pestaña Context.

Renombramos el atributo ID_PROG a ID_PROG_INI, puesto que ahora vamos a tener dos atributos distintos. El nuevo atributo se llamará ID_PROG_FIN.

Ahora, creamos un nuevo nodo, llamado PARAMETROS que no contendrá referencia a ningún tabla. Solamente nos servirá como contenedor para los atributos.

483

Lo que hacemos es arrastrar los atributos a nuestro nodo PARAMETROS.

484

Regresamos al Layout y eliminamos el campo de texto ID_PROG. Creamos un nuevo Container Form.

Nuestro diseño quedará como se muestra a continuación, con dos parámetros de entrada, que nos servirán para definir nuestro rango.

485

Regresamos a la pestaña Actions y al código del método Show.

METHOD ONACTIONSHOW. DATA: NODE_LENG TYPE REF TO IF_WD_CONTEXT_NODE, ITAB_LENG TYPE STANDARD TABLE OF ZVLENGUAJES_PROG. DATA: ID_PROG_INI TYPE STRING, ID_PROG_FIN TYPE STRING. DATA: ID_PROG TYPE RANGE OF ZVLENGUAJES_PROG-ID_PROG. FIELD-SYMBOLS: LIKE LINE OF ID_PROG. NODE_LENG = WD_CONTEXT->GET_CHILD_NODE( NAME = 'PARAMETROS' ).

486

WD_CONTEXT->GET_ATTRIBUTE( EXPORTING NAME = 'ID_PROG_INI' IMPORTING VALUE = ID_PROG_INI ). WD_CONTEXT->GET_ATTRIBUTE( EXPORTING NAME = 'ID_PROG_FIN' IMPORTING VALUE = ID_PROG_FIN ). APPEND INITIAL LINE TO ID_PROG ASSIGNING . -SIGN = 'I'. -OPTION = 'BT'. -LOW = ID_PROG_INI. -HIGH = ID_PROG_FIN. SELECT ID_PROG NOM_PROG LENGUAJE ENTORNO INTO TABLE ITAB_LENG FROM ZVLENGUAJES_PROG WHERE ID_PROG IN ID_PROG. NODE_LENG = WD_CONTEXT->GET_CHILD_NODE( NAME = 'ZLENG_NODE' ). NODE_LENG->BIND_TABLE( ITAB_LENG ). ENDMETHOD.

Veamos un poco el código.

DATA: ID_PROG_INI TYPE STRING, ID_PROG_FIN TYPE STRING.

487

DATA: ID_PROG TYPE RANGE OF ZVLENGUAJES_PROG-ID_PROG. FIELD-SYMBOLS: LIKE LINE OF ID_PROG.

Declaramos dos variables, ID_PROG_INI e ID_PROG_FIN, cada cual almacenará el valor de su parámetro correspondiente. Declaramos la variable ID_PROG de tipo RANGE (Rango), con lo cual podremos definir un rango de valores para seleccionar de la vista. Declaramos un Field-Symbol.

NODE_LENG = WD_CONTEXT->GET_CHILD_NODE( NAME = 'PARAMETROS' ).

Asignamos

el

nodo

PARAMETROS

a

nuestra

variable

NODE_LENG.

NODE_LENG->GET_ATTRIBUTE( EXPORTING NAME = 'ID_PROG_INI' IMPORTING VALUE = ID_PROG_INI ). NODE_LENG->GET_ATTRIBUTE( EXPORTING NAME = 'ID_PROG_FIN' IMPORTING VALUE = ID_PROG_FIN ).

Recuperamos los valores de ambos parámetros.

APPEND INITIAL LINE TO ID_PROG ASSIGNING . -SIGN = 'I'. -OPTION = 'BT'.

488

-LOW = ID_PROG_INI. -HIGH = ID_PROG_FIN.

Agregamos una línea de cabecera a nuestro rango, y asignamos los valores correspondientes. SIGN es el tipo de valor, OPTION es el tipo de correspondencia entre los datos, donde BT Æ Between (Entre) y EQ Æ Equal (Igual). LOW es el valor inicial y HIGH es el valor final. SELECT ID_PROG NOM_PROG LENGUAJE ENTORNO INTO TABLE ITAB_LENG FROM ZVLENGUAJES_PROG WHERE ID_PROG IN ID_PROG.

En el select, reemplazamos el EQ por un IN, con lo cual podemos utilizar el rango para pasar más de un valor como parámetro.

489

490

BSP Introducción El BSP (Business Server Pages – Página del Servidor de Aplicaciones), viene a ser un lenguaje script basado en ABAP, es decir, una especio de ASP, JSP o PHP. El BSP es anterior al WebDynpro, aunque esto no quiere decir que sea obsoleto o menos completo, por el contrario, lo que no se puede hacer con WebDynpro, se hace con BSP.

Creando nuestro primer BSP Al igual que en el caso del WebDynpro, el BSP no tiene una transacción propia (Otra queja más para SAP), así que nuevamente debemos ir a la transacción SE80 y escoger BSP Application (Aplicación BSP) en el menú.

491

Creamos nuestra aplicación ZDUMMY_BSP.

Creamos una página llamada index.bsp haciendo clic derecho Create Æ Page (Crear Æ Página).

492

En la pestaña Layout (Disposición) colocamos el siguiente código.



Id





Tenemos ahora, dos parámetros ID_PROG_INI y ID_PROG_FIN. Vamos

a

la

pestaña

Event

Handler

y

al

evento

OnInputProcessing.

499

CASE EVENT_ID. WHEN 'show'. NAVIGATION->SET_PARAMETER('ID_PROG_INI'). NAVIGATION->SET_PARAMETER('ID_PROG_FIN'). NAVIGATION->NEXT_PAGE('SHOW_DATA'). ENDCASE.

Enviamos ambos parámetros a la página Show_Data.bsp En la pagína Show_Data.bsp hacemos un pequeños cambios.

get_form_field( 'ID_PROG_INI' ). w_id_prog_fin = request->get_form_field( 'ID_PROG_FIN' ). -SIGN = 'I'. -OPTION = 'BT'. -LOW =

W_ID_PROG_INI.

-HIGH = W_ID_PROG_FIN.

500

select ID_PROG NOM_PROG LENGUAJE ENTORNO INTO TABLE T_LENG FROM ZVLENGUAJES_PROG WHERE ID_PROG in r_leng. %>





IdNombre LenguajeEntorno


Creamos un rango R_LENG, un field-Symbol , asignamos los parámetros de entrada W_ID_PROG_INI y W_ID_PROG_FIN al rango y lo utilizamos para nuestro select.

501

502

ABAP y XML El XML (Extensible Markup Language – Lenguaje extensible de marcas) es un formato de intercambio de archivos muy utilizado en al actualidad, puesto que es sumamente flexible. Como era de esperarse, el ABAP nos permite trabajar con archivos XML de una manera bastante simple.

*=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_TAB, LINE(100) TYPE C, END OF TY_TAB. *=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_TAB TYPE STANDARD TABLE OF TY_TAB, T_ZLENG TYPE STANDARD TABLE OF ZVLENGUAJES_PROG, T_FILETAB TYPE FILETABLE.

*=====================================================* * DECLARACION DE FIELD-SYMBOLS

*

*=====================================================* FIELD-SYMBOLS: LIKE LINE OF T_FILETAB.

503

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: XML_OUT TYPE STRING, W_SUBRC TYPE SY-SUBRC, SIZE TYPE I, W_FILE TYPE STRING. *&----------------------------------------------------* *&

SELECTION-SCREEN

*

*&----------------------------------------------------* SELECTION-SCREEN BEGIN OF BLOCK TEST WITH FRAME. PARAMETERS: FILE LIKE RLGRAP-FILENAME. SELECTION-SCREEN END OF BLOCK TEST. *=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. PERFORM OBTENER_DATOS. PERFORM GENERAR_XML. PERFORM DESCARGAR_XML. *&----------------------------------------------------* *&

AT SELECTION-SCREEN

*

*&----------------------------------------------------* AT SELECTION-SCREEN ON VALUE-REQUEST FOR FILE. CALL METHOD CL_GUI_FRONTEND_SERVICES=>FILE_OPEN_DIALOG EXPORTING WINDOW_TITLE

= 'Seleccionar archivo'

DEFAULT_FILENAME = '*.xml'

504

FILE_FILTER

= '*.xml'

CHANGING FILE_TABLE

= T_FILETAB

RC

= W_SUBRC.

READ TABLE T_FILETAB INDEX 1 ASSIGNING . FILE = . IF FILE IS INITIAL. EXIT. ELSE. W_FILE = FILE. ENDIF. *&----------------------------------------------------* *&

Form

OBTENER_DATOS

*

*&----------------------------------------------------* FORM OBTENER_DATOS. SELECT ID_PROG NOM_PROG LENGUAJE ENTORNO INTO TABLE T_ZLENG FROM ZVLENGUAJES_PROG. ENDFORM.

" OBTENER_DATOS

*&----------------------------------------------------* *&

Form

GENERAR_XML

*

*&----------------------------------------------------* FORM GENERAR_XML. CALL TRANSFORMATION ('ID')

505

SOURCE TAB = T_ZLENG[] RESULT XML XML_OUT. ENDFORM.

" GENERAR_XML

*&----------------------------------------------------* *&

Form

DESCARGAR_XML

*

*&----------------------------------------------------* FORM DESCARGAR_XML. CALL FUNCTION 'SWA_STRING_TO_TABLE' EXPORTING CHARACTER_STRING = XML_OUT IMPORTING CHARACTER_TABLE

= T_TAB.

CALL METHOD CL_GUI_FRONTEND_SERVICES=>GUI_DOWNLOAD EXPORTING BIN_FILESIZE = SIZE FILENAME

= W_FILE

FILETYPE

= 'BIN'

CHANGING DATA_TAB

= T_TAB.

ENDFORM.

" DESCARGAR_XML

Revisemos el código fuente.

506

*=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_TAB, LINE(100) TYPE C, END OF TY_TAB.

Declaramos un TYPE que contiene un solo campo llamado LINE de 100 de longitud y tipo C.

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_TAB TYPE STANDARD TABLE OF TY_TAB, T_ZLENG TYPE STANDARD TABLE OF ZVLENGUAJES_PROG, T_FILETAB TYPE FILETABLE.

Creamos algunas tablas internas, una para guardar el XML, otra para guardar los datos de la Base de Datos y el último para guardar la ruta del archivo de salida.

*=====================================================* * DECLARACION DE FIELD-SYMBOLS

*

*=====================================================* FIELD-SYMBOLS: LIKE LINE OF T_FILETAB.

Declaramos un field-symbol.

507

*=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: XML_OUT TYPE STRING, W_SUBRC TYPE SY-SUBRC, SIZE TYPE I, W_FILE TYPE STRING.

XML_OUT es una cadena que contendrá la información para generar el XML.

*&----------------------------------------------------* *&

SELECTION-SCREEN

*

*&----------------------------------------------------* SELECTION-SCREEN BEGIN OF BLOCK TEST WITH FRAME. PARAMETERS: FILE LIKE RLGRAP-FILENAME. SELECTION-SCREEN END OF BLOCK TEST.

Declaramos un parámetro de entrada para poder obtener la ruta donde se generará el archivo XML.

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. PERFORM OBTENER_DATOS. PERFORM GENERAR_XML. PERFORM DESCARGAR_XML.

508

OBTENER_DATOS nos sirve para seleccionar los datos de la vista. GENERAR_XML crea el archivo XML. DESCARGAR_XML guarda el archivo en la ruta especificada.

*&----------------------------------------------------* *&

AT SELECTION-SCREEN

*

*&----------------------------------------------------* AT SELECTION-SCREEN ON VALUE-REQUEST FOR FILE. CALL METHOD CL_GUI_FRONTEND_SERVICES=>FILE_OPEN_DIALOG EXPORTING WINDOW_TITLE

= 'Seleccionar archivo'

DEFAULT_FILENAME = '*.xml' FILE_FILTER

= '*.xml'

CHANGING FILE_TABLE

= T_FILETAB

RC

= W_SUBRC.

READ TABLE T_FILETAB INDEX 1 ASSIGNING . FILE = . IF FILE IS INITIAL. EXIT. ELSE. W_FILE = FILE. ENDIF.

Obtenemos la ruta del archivo a descargar.

509

*&----------------------------------------------------* *&

Form

OBTENER_DATOS

*

*&----------------------------------------------------* FORM OBTENER_DATOS. SELECT ID_PROG NOM_PROG LENGUAJE ENTORNO INTO TABLE T_ZLENG FROM ZVLENGUAJES_PROG. ENDFORM.

" OBTENER_DATOS

Seleccionamos los registros de la vista.

*&----------------------------------------------------* *&

Form

GENERAR_XML

*

*&----------------------------------------------------* FORM GENERAR_XML. CALL TRANSFORMATION ('ID') SOURCE TAB = T_ZLENG[] RESULT XML XML_OUT. ENDFORM.

" GENERAR_XML

CALL TRANSFORMATION nos sirve para llamar a una transformación que se encarga de convertir los datos de la tabla inerna en un cadena XML.

510

*&----------------------------------------------------* *&

Form

DESCARGAR_XML

*

*&----------------------------------------------------* FORM DESCARGAR_XML. CALL FUNCTION 'SWA_STRING_TO_TABLE' EXPORTING CHARACTER_STRING = XML_OUT IMPORTING CHARACTER_TABLE

= T_TAB.

CALL METHOD CL_GUI_FRONTEND_SERVICES=>GUI_DOWNLOAD EXPORTING BIN_FILESIZE = SIZE FILENAME

= W_FILE

FILETYPE

= 'BIN'

CHANGING DATA_TAB ENDFORM.

= T_TAB. " DESCARGAR_XML

SWA_STRING_TO_TABLE nos sirve para convertir la cadena XML_OUT a una tabla con los datos del XML. El método GUI_DOWNLOAD nos permite descargar el archivo XML.

511

Ahora que ya vimos como podemos generar un archivo XML a partir de una tabla interna, veamos como hacer lo contrario, es decir, generemos una tabla interna en base a un archivo XML. El código es practicamente el mismo, solo cambia el orden.

*=====================================================* * DECLARACION DE TYPES

*

*=====================================================* TYPES: BEGIN OF TY_TAB, LINE(100) TYPE C, END OF TY_TAB.

512

*=====================================================* * DECLARACION DE TABLAS INTERNAS

*

*=====================================================* DATA: T_TAB TYPE STANDARD TABLE OF TY_TAB, T_ZLENG TYPE STANDARD TABLE OF ZVLENGUAJES_PROG, T_FILETAB TYPE FILETABLE. *=====================================================* * DECLARACION DE FIELD-SYMBOLS

*

*=====================================================* FIELD-SYMBOLS: LIKE LINE OF T_FILETAB, LIKE LINE OF T_ZLENG. *=====================================================* * DECLARACION DE VARIABLES

*

*=====================================================* DATA: XML_OUT TYPE STRING, W_SUBRC TYPE SY-SUBRC, SIZE TYPE I, W_FILE TYPE STRING. *&----------------------------------------------------* *&

SELECTION-SCREEN

*

*&----------------------------------------------------* SELECTION-SCREEN BEGIN OF BLOCK TEST WITH FRAME. PARAMETERS: FILE LIKE RLGRAP-FILENAME. SELECTION-SCREEN END OF BLOCK TEST.

513

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. PERFORM CARGAR_XML. PERFORM GENERAR_TABLA_INTERNA. PERFORM IMPRIMIR. *&----------------------------------------------------* *&

AT SELECTION-SCREEN

*

*&----------------------------------------------------* AT SELECTION-SCREEN ON VALUE-REQUEST FOR FILE. CALL METHOD CL_GUI_FRONTEND_SERVICES=>FILE_OPEN_DIALOG EXPORTING WINDOW_TITLE

= 'Seleccionar archivo'

DEFAULT_FILENAME = '*.xml' FILE_FILTER

= '*.xml'

CHANGING FILE_TABLE

= T_FILETAB

RC

= W_SUBRC.

READ TABLE T_FILETAB INDEX 1 ASSIGNING . FILE = . IF FILE IS INITIAL. EXIT. ELSE. W_FILE = FILE. ENDIF.

514

*&----------------------------------------------------* *&

Form

GENERAR_TABLA_INTERNA

*

*&----------------------------------------------------* FORM GENERAR_TABLA_INTERNA. CALL TRANSFORMATION ('ID') SOURCE XML XML_OUT RESULT TAB = T_ZLENG[]. ENDFORM.

" GENERAR_TABLA_INTERNA

*&----------------------------------------------------* *&

Form

CARGAR_XML

*

*&----------------------------------------------------* FORM CARGAR_XML. CALL METHOD CL_GUI_FRONTEND_SERVICES=>GUI_UPLOAD EXPORTING FILENAME

= W_FILE

FILETYPE

= 'BIN'

CHANGING DATA_TAB

= T_TAB.

CALL FUNCTION 'SWA_STRING_FROM_TABLE' EXPORTING CHARACTER_TABLE

= T_TAB

IMPORTING CHARACTER_STRING = XML_OUT. ENDFORM.

" CARGAR_XML

515

*&----------------------------------------------------* *&

Form

IMPRIMIR

*

*&----------------------------------------------------* FORM IMPRIMIR. LOOP AT T_ZLENG ASSIGNING . WRITE:/ -ID_PROG, -NOM_PROG, -LENGUAJE, -ENTORNO. ENDLOOP. ENDFORM.

" IMPRIMIR

Revisemos el código, pero solamente las partes que han cambiado.

*=====================================================* * START-OF-SELECTION

*

*=====================================================* START-OF-SELECTION. PERFORM CARGAR_XML. PERFORM GENERAR_TABLA_INTERNA. PERFORM IMPRIMIR.

CARGAR_XML carga el archivo y lo guarda en un tabla interna. (Con formato XML claro está). Luego, lo transforma en una cadena XML. GENERAR_TABLA_INTERNA transforma la cadena XML en una tabla interna. IMPRIMIR muestra el resultado en pantalla.

516

*&----------------------------------------------------* *&

Form

CARGAR_XML

*

*&----------------------------------------------------* FORM CARGAR_XML. CALL METHOD CL_GUI_FRONTEND_SERVICES=>GUI_UPLOAD EXPORTING FILENAME

= W_FILE

FILETYPE

= 'BIN'

CHANGING DATA_TAB

= T_TAB.

CALL FUNCTION 'SWA_STRING_FROM_TABLE' EXPORTING CHARACTER_TABLE

= T_TAB

IMPORTING CHARACTER_STRING = XML_OUT. ENDFORM.

" CARGAR_XML

Utilizamos el método GUI_UPLOAD para cargar el archivo XML. Con el módulo de funciones SWA_STRING_FROM_TABLE convertimos la tabla interna en una cadena XML.

517

*&----------------------------------------------------* *&

Form

GENERAR_TABLA_INTERNA

*

*&----------------------------------------------------* FORM GENERAR_TABLA_INTERNA. CALL TRANSFORMATION ('ID') SOURCE XML XML_OUT RESULT TAB = T_ZLENG[]. ENDFORM.

" GENERAR_TABLA_INTERNA

Utilizamos el CALL TRANSFORMATION para recibir una cadena XML y retornar una tabla interna.

*&----------------------------------------------------* *&

Form

IMPRIMIR

*

*&----------------------------------------------------* FORM IMPRIMIR. LOOP AT T_ZLENG ASSIGNING . WRITE:/ -ID_PROG, -NOM_PROG, -LENGUAJE, -ENTORNO. ENDLOOP. ENDFORM.

" IMPRIMIR

Imprimimos el contenido de la tabla interna. Como pueden ver, es bastante sencillo trabajar con XML y ABAP.

518

519

Scripting in a Box Scripting in a Box es una herramienta creada por Craig Cmehil de SAP AG. Esta herramienta basada en Eclipse, reune a todos los lenguajes script que pueden conectados con NetWeaver, como PHP, Ruby, Rails, Perl y Python. De muy sencilla instalación, esta herramienta es indispensable para el trabajo con lenguajes script. La instalación es lo mejor de todo, puesto que no existe una instalación, al igual que el Eclipse, el Scripting in a Box simplementese descarga y se configura. Como era de suponerse, cuenta además con varios ejemplos (Incluyendo mi emulador de SE16 en PHP), lo cual lo hace excelente para aquellas personas que nunca han hecho una integración con NetWeaver. Para descargarlo, debemos ingresar a la siguiente dirección (Debemos ser usuarios del SDN – SAP Developer Network). https://www.sdn.sap.com/irj/sdn/go/portal/prtroot/docs/library/uuid/e5 0bd86e-0a01-0010-53bd-857585234a6a O, ingresar a http://sdn.sap.com Æ Downloads Æ More Downloadable Tools for Developers Æ Scripting in a Box. Veamos algunas imagenes de Scripting a in Box.

520

521

522

SAPLink SAPLink es una herramienta creada por Daniel McWeeney y Ed Herrmann de Colgate - Palmolive. Esta herramienta, desarrollada en ABAP, nos permite crear una verdadera librería de códigos para poder transportar o compartir con los demás. Creando paquetes basados en código XML, podemos almacenar Programas, Dynpros, WebDynpros, SmartForms y un largo etcétera. Si bien existen muchas herramientas para descargar y cargar elementos de NetWeaver (Yo he creado varias de ellas), no hay hasta el momento ninguna que pueda competir con SAPLink, puesto que SAPLink nos brinda una interfaz muy sencilla, opción de descargar Plug-Ins y un constante monitoreo de los posibles errores. Podemos descargarla, debemos ingresar a la siguiente dirección. http://code.google.com/p/saplink/ Y para los Plug-Ins, deberemos ingresar a la dirección. http://code.google.com/p/saplink/wiki/pluginList Veamos algunas cuantas imágenes.

523

524

525

526

Integración PHP-NetWeaver Introducción El PHP (PHP Preprocessor), es un lenguaje de programación para páginas web dinámicas. Lleva varios años en el mercado, es open source y en su versión 5 en adelante, es un lenguaje orientado a objetos. Su sintaxis es bastante similar a la de C++, por lo cual, conociendo C++ o Java, es muy fácil de aprender. Gracias a Eduard Koucky, existe un conector llamado SAPRFC, que nos permite que las páginas web que desarrollemos en PHP, se conecten con NetWeaver, para obtener datos de tablas, ejecutar funciones, BAPI’s, etc. Es decir, hacer lo mismo que hacemos todos los días, pero accediendo a través de una interface web. Si se preguntan porque utilizar PHP con NetWeaver, en vez de utilizar WebDynpro o BSP, la respuesta es muy simple: “PHP es gratuito, fácil de aprender y configurar”. En todo caso, ya depende de cada uno que tecnología utilizar. Lo único que necesitamos para poder integrar PHP con NetWeaver es lo siguiente (Si no hemos instalado Scripting in a Box claro está): •

PHP versión 5.1.2 Æ http://www.php.net



Apache WebServer versión 2.0.55 Æ http://httpd.apache.org



SAPRFC 1.4-5.0.4 Æ http://saprfc.sourceforge.net/



NetWeaver Sneak Preview o SAP NetWeaver o MiniSAP o MiniWas. 527

Como este es un libro sobre NetWeaver y no sobre PHP (Para eso está, El Arte de Programar PHP), no vamos a enseñar a instalar el PHP o el Apache, ni tampoco a programar en PHP, lo que vamos a hacer es configurar el SAPRFC e integrarlo con NetWeaver.

Instalando el SAPRFC Simplemente debemos descomprimir el archivo .ZIP y copiar el archivo php_saprfc.dll, dentro de la carpeta /ext del PHP. En el PHP.ini debemos agregar la siguiente línea.

extension = php_saprfc.dll;

Reiniciamos el servicio del Apache y estamos listos para comenzar. Para

comprobar

que

todo

está

funcionando

correctamente,

simplemente llamamos a una página con el comando PHP_INFO().

528

Estableciendo la comunicación con NetWeaver Vamos a crear un ejemplo simple (En realidad, es lo primero que hice yo con PHP y NetWeaver), un emulador de SE16. Lo primero que debemos hacer, es crear nuestra clase para poder logearnos a NetWeaver. La llamamos Login_Class.php