You are on page 1of 124

Cloud Computing: Implementacion

de flujos de integracion de datos


entre soluciones SAP onPremise y
onCloud
Perez Perez, Juan Jose
Diciembre de 2014
Indice
1. Introduccion y Objetivos. 6

2. Que es SAP? 8
2.1. Soluciones y productos tradicionales de SAP . . . . . . . . . . . . . . . . . 8
2.1.1. SAP ERP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.1.2. SAP CRM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.1.3. SAP SRM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.1.4. SAP SCM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.1.5. SAP PLM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2. Una breve historia de SAP . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2.1. Los primeros anos: 1972-1981 . . . . . . . . . . . . . . . . . . . . . 13
2.2.2. La era de SAP R/2: 1982-1991 . . . . . . . . . . . . . . . . . . . . . 14
2.2.3. La era de SAP R/3: 1992-2001 . . . . . . . . . . . . . . . . . . . . . 16

3. Presente y futuro de SAP. HANA. Cloud Computing. 19


3.1. SAP HANA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.1.1. Arquitectura interna de HANA . . . . . . . . . . . . . . . . . . . . 20
3.1.2. Lenguajes asociados a HANA . . . . . . . . . . . . . . . . . . . . . 24
3.1.3. Benchmarks de HANA . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.2. Cloud Computing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.2.1. Introduccion y conceptos basicos . . . . . . . . . . . . . . . . . . . 30
3.2.2. Estrategia y soluciones de SAP respecto a la nube . . . . . . . . . . 32
3.3. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

4. Proyecto tecnico. Flujo de integracion de datos entre un ERP y HANA


Cloud Platform. 35
4.1. Introduccion: Arquitectura . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.2. Modulo onPremise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.2.1. SAP HANA Cloud Connector . . . . . . . . . . . . . . . . . . . . . 37
4.2.2. Codigo necesario en el ERP. ABAP. RFC. . . . . . . . . . . . . . . 39
4.2.3. Vista general del modulo . . . . . . . . . . . . . . . . . . . . . . . . 43
4.3. Modulo de integracion onCloud . . . . . . . . . . . . . . . . . . . . . . . . 44
4.3.1. ControllerServlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.3.2. AbapDAO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.3.3. HanaDAO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.3.4. Vista general del Modulo . . . . . . . . . . . . . . . . . . . . . . . . 50
4.4. Modulo de presentacion onCloud: Aplicacion Web . . . . . . . . . . . . . . 52
4.4.1. Archivos de proyecto HANA XS . . . . . . . . . . . . . . . . . . . . 52
4.4.2. Lado cliente: HTML, CSS . . . . . . . . . . . . . . . . . . . . . . . 53
4.4.3. Lado cliente: Pares VC SAPUI5 . . . . . . . . . . . . . . . . . . . . 55
4.4.4. Lado servidor: Modelo XS . . . . . . . . . . . . . . . . . . . . . . . 59
4.4.5. Vista general del modulo . . . . . . . . . . . . . . . . . . . . . . . . 61

5. Comparativa con otras soluciones en el mercado. 63


5.1. Netweaver Gateway . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
5.2. HANA Cloud Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
5.3. Dell Boomi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
5.4. SAP PI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
5.5. SAP Landscape Transformation . . . . . . . . . . . . . . . . . . . . . . . . 65

6. Conclusiones 66

Referencias 68

Apendices 70

A. Codigo fuente del modulo onPremise: ABAP. 70


A.1. Z RFC HR READ PAYROLL ENTRIES . . . . . . . . . . . . . . . . . . . 70
A.2. Z RFC HR READ PAYROLL RESULTS . . . . . . . . . . . . . . . . . . . 71
A.3. Z RFC HR READ ORG STRUCTURE . . . . . . . . . . . . . . . . . . . 72

B. Codigo fuente del modulo de integracion: Java. 73


B.1. ControllerServlet.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
B.2. AbapDAO.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
B.3. HanaDAO.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
B.4. AbapData.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

C. Archivos nativos de HANA XS. 90


C.1. .xsapp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
C.2. .xsaccess . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
C.3. .xsprivileges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
C.4. model access.hdbrole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

D. Codigo fuente del modulo de presentacion: Javascript, CSS3, HTML5. 94


D.1. index.html . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
D.2. customStyles.css . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
D.3. shell.controller.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
D.4. shell.view.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
D.5. table.controller.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
D.6. table.view.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
D.7. cluster.controller.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
D.8. cluster.view.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
D.9. result.controller.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
D.10.result.view.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
D.11.tree.controller.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
D.12.tree.view.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
D.13.model.json.xsjs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
1. Introduccion y Objetivos.
Antes de empezar a describir el proyecto en s, conviene que dediquemos algunos
parrafos a describir cuales son los objetivos que se pretenden cubrir mediante este proyecto,
y cual es su alcance.
Este proyecto de fin de Carrera intenta primeramente, dar una introduccion al ecosis-
tema economico y tecnico de SAP, la tercera compana de software mas grande del mundo
y, paradojicamente, una de las mas desconocidas fuera del mundo de la gran empresa (por
motivos que se detallaran en el propio proyecto).
Intentaremos dar una introduccion historica a SAP, explicar sus soluciones principales
(con especial enfasis en su ERP), y como se relacionan con el mundo de la contabilidad,
las finanzas, y la administracion de Empresas en general. A esto dedicaremos el segundo
captulo (ya que el primero es esta misma introduccion).
Despues de esto explicaremos el cambio tan disruptivo que la llegada del Cloud com-
puting ha tenido sobre las empresas IT, y como ha cambiado SAP su modelo de negocio
en estos ultimos anos, debido a la necesidad de reaccionar ante ello. Hablaremos sobre
SAP HANA, tanto del producto en s como del ecosistema que se ha formado en torno
a su marca (al que pertenecen productos como Fiori, Lumira, etc.) para ilustrar la reac-
cion que SAP ha tenido ante la nube. Detallaremos, dentro de este nuevo paradigma, la
creacion de nuevas oportunidades y nichos de mercado en el mundillo de la consultora
SAP, especialmente en lo referente a puestos de trabajo y proyectos dedicados a HANA
y a la migracion de datos entre los servidores SAP internos a una empresa (onPremise)
y los servicios SAP en la nube (onCloud), dentro de los cuales se situa este proyecto.
Especialmente importante en esta parte de cara al proyecto tecnico es la introduccion a
HANA Cloud Platform (o HCP). Dedicaremos a esto el tercer captulo.
Una vez establecido bien el contexto en el cual nos encontramos, pasaremos a describir
el proyecto tecnico en s. Este proyecto trata de implementar de diversas maneras distin-
tas una migracion de datos (financieros, contables, de personal, de materiales) entre un
servidor ERP en la intranet de una pequena empresa con la que hemos colaborado para
realizar este proyecto, y una cuenta en HCP. Una vez esten estos datos subidos, implemen-
taremos una interfaz web para poder interactuar con ellos, permitiendonos as programar
diversas aplicaciones utiles desde el punto de vista de los Recursos Humanos tales como
la visualizacion de la estructura organizativa de una empresa, el historial salarial de un
empleado, y la generacion automatica de su nomina en un documento PDF. Hablaremos
sobre la implementacion tecnica de este proyecto, los distintos protocolos que hemos teni-
do que emplear, y la arquitectura de nuestros programas empleados. Dedicaremos a esto
el cuarto captulo.
Una vez descrito el proyecto tecnico, estableceremos una pequena comparativa con
otras posibles arquitecturas de integracion de datos, con sus virtudes y sus inconvenien-
tes, de tal forma que el lector pueda hacerse una idea de donde se situa la solucion

6
implementada dentro del mercado, por que funciona bien para nuestros propositos, y
que alternativas deberan considerarse en caso de que, por ejemplo, tuviesemos que dar
soporte a mas empleados o tuviesemos que manejar un volumen de datos mas grande.
Esto sera el quinto captulo.
Por ultimo, escribiremos una breve conclusion detallando como cumple el proyecto
realizado los objetivos aqu descritos, as como algunas posibles crticas que se le podran
hacer desde el punto de vista tecnico, con el objetivo de justificar las decisiones de diseno
realizadas. A esto dedicaremos el sexto y ultimo captulo (sin contar con los apendices,
que contienen el codigo fuente de la aplicacion creada).

7
2. Que es SAP?
Para entender bien el contexto en el que este proyecto ha sido realizado, debemos
entender exactamente que es lo que hace SAP y cuales son las soluciones que ofrece desde
el punto de vista empresarial.
SAP SE (Siglas de Systems, Applications & Products in Data Processing) es actual-
mente la tercera compana de software mas grande del mundo, solo por detras de Oracle
y Microsoft. Sin embargo, al contrario que estas dos, SAP no es apenas conocido fuera
del mundo de la gran empresa. Por que ocurre esto?
La respuesta es que el target de SAP no es el hombre de la calle. SAP es una compana
dedicada a hacer software exclusivamente para otras empresas, nunca para el consumidor
final. Ademas, el perfil tradicional de customer SAP es el de una empresa grande, mercado
en el cual ha cosechado un gran exito. Actualmente el 86 % de las empresas Fortune 500
tienen al menos un sistema SAP en sus premisas [18].
Pero exactamente en que manera puede ayudar un sistema SAP al proceso de negocio
de una gran empresa? Intentaremos responder a esta pregunta mediante un repaso a
las soluciones tradicionales mas importantes de la compana, tras lo cual daremos una
pequena introduccion historica.

2.1. Soluciones y productos tradicionales de SAP


Las soluciones que SAP ofrece son casi tan variadas como las empresas a las que da
servicio, sin embargo, hay varias de ellas que destacan por ser casi omnipresentes en toda
empresa que contrata sus servicios. Estas soluciones son parte de lo que se denomina como
business suite, y son:

SAP ERP

SAP CRM

SAP SRM

SAP SCM

SAP PLM

Estas 5 soluciones no dependen unas de otras, sino que mas bien se complementan
entre s. Una empresa customer de SAP puede disponer de todas, varias, o ninguna de
ellas.
Ademas de estos productos hay varios otros que, sin ser tan universalmente adoptados
por el mercado, merecen tambien mencion, como por ejemplo BW + Business Objects,
HCM, y otros. Pasamos pues a detallarlos:

8
2.1.1. SAP ERP
No es una exageracion decir que no se puede hablar de SAP sin hablar de su ERP. De
una forma o de otra (ya sea llamandose R/1, R/2, R/3, o ERP), el ERP de SAP es el
producto mas antiguo desarrollado por esta, y el nucleo de toda instalacion SAP que se
precie.
Pero, que es un ERP? ERP son las siglas de Enterprise Resource Planning, Planifi-
cacion de Recursos de la Empresa en espanol.
Podemos decir que el ERP provee una vision integrada de los procesos de negocio mas
importantes, a menudo en tiempo real, usando bases de datos comunes mantenidas por
un sistema de gestion. Los sistemas ERP monitorean a los distintos recursos de negocio
(dinero, materiales, capacidad productiva) y al estado de los compromisos de negocio:
ordenes de venta, salarios, etc. Las aplicaciones que componen al sistema comparten datos
entre los distintos departamentos (manufacturacion, compras, ventas, contabilidad, etc.)
que las proveen de datos [21].
Es decir, es esencialmente, una aplicacion sobre una base de datos centralizada, que
contiene toda la informacion de la empresa en materias como recursos humanos, gestion
de materiales, finanzas, contabilidad, y otros ambitos que detallaremos mas adelante.
Esto permite que, por ejemplo, una factura elaborada en el ERP sobre una compra de
algun material en concreto actualice automaticamente los datos contables de la empresa,
su presupuesto, etc. Por poner otro, se puede integrar la estrategia de la compana en
el ERP de tal forma que, desde el nivel ejecutivo, llegue directamente hacia niveles de
gestion de talento en Recursos Humanos, por poner un ejemplo.

Figura 1: Modulos principales del ERP.

9
Como se consigue esto exactamente? Gracias a la estructura modular del ERP [5].
Como se puede observar en la figura 1, el ERP esta compuesto de tres secciones principales,
compuestas a su vez de una serie de modulos:
Seccion de Finanzas: Finanzas, Contabilidad, Gestion de activos Fijos, Sistema de
Proyecto
Seccion de Logstica: Ventas, Materiales, Planificacion de produccion, Control de
Calidad, Gestion de Plantas
Seccion de Recursos Humanos: Recursos Humanos
Todos estos modulos interaccionan entre s, dando lugar a un sistema altamente eficien-
te, que elimina bastante overhead relacionado con la comunicacion entre departamentos
y la gestion en general de los detalles de una empresa. Desafortunadamente esto no viene
sin un coste. Los proyectos de implementacion de SAP en companas Fortune 500 suelen
rondar entre los 100 millones de dolares, suelen tardar varios anos, y las bases de datos
suelen ser de Terabytes. Ademas, el manejo del ERP no es en absoluto algo trivial, siendo
necesaria una formacion de varios anos en lo que se conoce como consultora funcional
para llegar a ser un usuario proficiente en alguno de sus modulos. Tambien cabe destacar
que no son necesarios todos los modulos para que el sistema funcione, un ERP podra
tener solo los modulos FI, CO, y HR, por ejemplo.
Aun as, SAP es hoy en da, y previsiblemente durante un futuro bastante largo, la
compana lder en software empresarial, debido a la robustez de su producto y a sus anos
de experiencia como lder en el mercado. Ademas, como veremos en el captulo siguiente,
se estan dando pasos bastante importantes para resolver algunos de estos problemas.

2.1.2. SAP CRM


SAP CRM, sin ser tan esencial como el ERP, tambien es una solucion muy importante,
especialmente para empresas que interactuan mucho con el publico. CRM son las siglas de
Customer Relationship Management, o Gestion de relaciones con los clientes, en espanol.
Podemos definir CRM como un sistema para gestionar las interacciones de una com-
pana con sus clientes presentes y futuros. A menudo este sistema utiliza la tecnologa
para organizar, automatizar y sincronizar las ventas, el marketing, el servicio al cliente, y
el soporte tecnico [19].
El CRM de SAP es un sistema que guarda un historial de toda la clientela de una
empresa, conteniendo datos como el feedback de los clientes, su historial de compras, y
demas. A partir de ah, utilizando funciones de Business Intelligence, se tratan de predecir
tendencias de compra futuras, coordinar tareas de venta como promociones, call centers,
marketing, y posicionamiento del producto en diversas tiendas para maximizar en la
medida de lo posible las ventas de los productos de la compana y la satisfaccion del
cliente.

10
CRM tambien posee una estructura modular, aunque logicamente estos modulos estan
mas centrados en actividades de marketing y ventas. Algunos de los modulos mas impor-
tantes del CRM son:
Ventas
Marketing
Servicios
Analtica
Centro de Interaccion
Canal Web (que esta a cargo de cosas tales como el comercio electronico, marketing
por internet, etc.)
Actualmente SAP es el segundo proveedor de soluciones CRM del mercado, por detras
de SalesForce. [8]

2.1.3. SAP SRM


SAP SRM es otra solucion perteneciente a la business suite. SRM son las siglas de
Supplier Relationship Management, que signica en espanol Gestion de relaciones con el
proveedor.
Podemos definir SRM como la continua y sistematica valoracion de las capacidades y
los activos del proveedor con respecto a la estrategia de negocio general, la determinacion
de que actividades realizar con que proveedores, y la planificacion y ejecucion de todas
las interacciones con el proveedor, de una forma coordinada a lo largo del ciclo de vida de
la relacion, para maximizar el valor creado mediante dichas interacciones [3].
Habiendo visto el apartado anterior, es facil deducir que este modulo esta en gran
medida relacionado con CRM. Sin embargo, dada la distinta naturaleza y finalidad de
la relacion empresa-cliente, y la relacion empresa-proveedor, se puede ver que ambos
productos son bastante distintos.
En concreto, este producto esta fuertemente relacionado con el modulo de materiales
del ERP, mientras que el anterior lo estaba con el modulo de ventas.

2.1.4. SAP SCM


SAP SCM es otra solucion, centrada tambien en la provision de la compana. Con-
cretamente, SCM son las siglas de Supply Chain Management, que en espanol significa
Administracion de Cadena de Suministro.
SCM es la gestion del flujo de bienes y activos que pasan por la empresa. Incluye el
movimiento y el almacenamiento de materias primas, materiales intermedios, y productos

11
finalizados desde el punto de origen hasta el punto de consumicion. Redes interconecta-
das, canales y nodos de negocio estan envueltos en la provision de productos y servicios
requeridos por el consumidor final en una cadena de suministro.
De esta forma, se puede definir el SCM como el diseno, planificacion, ejecucion, control
y monitorizacion de las actividades de la cadena de provisionamiento con el objetivo de
crear un valor neto, construir una infraestructura competitiva, gestionar logstica alrede-
dor del globo, sincronizar oferta y demanda y medir el rendimiento globalmente [1].
Es evidente que el SCM de SAP guarda una gran relacion con los modulos de logstica
del ERP, hasta el punto de que si se tiene uno, normalmente se suele tener el otro,
suponiendo que la empresa sea lo suficientemente grande.

2.1.5. SAP PLM


La ultima solucion tradicional que veremos es SAP PLM. PLM es Product Lifecycle
Management, Administracion de Ciclo de Vida del Producto en espanol.
En la industria, PLM es el proceso de gestionar todo el ciclo de vida de un producto,
desde su diseno de ingeniera y manufactura, hasta el servicio y la eliminacion de desechos
manufacturados. PLM integra a los empleados, los datos, los procesos y los sistemas de
negocio y provee una informacion comprensiva del producto para las companas y sus
corporaciones [2].
Esta es la parte quiza mas relacionada con la organizacion industrial. En el ano 2013,
SAP estuvo en quinto lugar en el marketshare de PLM [22].

12
2.2. Una breve historia de SAP
A continuacion pasaremos a detallar brevemente la historia de SAP, desde los tiempos
de su fundacion hasta la actualidad. Yendonos a la pagina web corporativa de SAP [10],
vemos que la propia compana divide su historia en 4 etapas, que son:

Los primeros anos

La era de SAP R/2

La era de SAP R/3

Presente de la compana

Nos encargaremos de hablar del presente de la compana en el siguiente captulo,


dedicando esta seccion a las primeras tres etapas.

2.2.1. Los primeros anos: 1972-1981


La historia de SAP empieza en 1972, cuando 5 antiguos empleados de IBM empiezan
una compana a la que llaman Systemanalyse und Programmentwicklung (Analisis de
Sistemas y Desarrollo de Programas). La vision inicial de la compana es desarrollar
software de aplicacion estandar para el procesado de datos en tiempo real.
Tomando la forma inicial de una sociedad privada bajo el Codigo Civil Aleman, la
compana establece su sede en Weinheim, Alemania. Tras su primer ano en el negocio,
SAP emplea a otras 9 personas y genera 620.000 marcos de beneficios.
En su segundo ano como compana, SAP consigue desarrollar su primer sistema finan-
ciero contable, conocido como RF. Este sistema se convertira en el primero de los modulos
que compondran SAP R/1. Ademas, consigue expandirse por el suroeste aleman, convir-
tiendose en proveedor de companas como Knoll o Rothandle.
En 1976, la compana posee 25 empleados y genera 3.81 millones de marcos en bene-
ficios.
Al ano siguiente, SAP mueve su sede hacia su localizacion actual, en Walldorf. Empieza
a expandirse hacia otros pases, instalando 2 sistemas en Austria.
En 1978, completa el modulo de contabilidad de activos para SAP R/1 y un proyecto
de implementacion en otra compana. Asimismo, su software es traducido por primera
vez a otro idioma (el frances) por un manufacturador de maquinaria campestre conocido
como John Deere.
En 1979, se empiezan a centralizar los centros de datos de la compana, y se gestan
las primeras ideas de lo que sera SAP R/2.
Al ano siguiente se termina el proceso de centralizacion, se renueva el hardware del
centro de datos y se desarrolla el modulo RV, para ventas y distribucion.

13
En 1981 la base de clientes de SAP se expande a 200 companas, se desarrolla un
modulo de gestion de la produccion y, lo que es mas importante, SAP R/2 pasa a ser
estable, entrando en una nueva era de la compana.

2.2.2. La era de SAP R/2: 1982-1991


SAP R/2 pasa a ser el sucesor de SAP R/1. Lo que hace a SAP R/2 unico es que
es una aplicacion de software que procesaba en tiempo real en un ordenador central
tomando ventaja de la Time Sharing Option en sistemas IBM para conseguir que varias
personas pudiesen acceder a el concurrentemente. Ademas, integra todas las funciones de
una empresa, tales como contabilidad, procesos de manufacturacion, recursos humanos,
etc. Podemos ver aqu algunas de las caractersticas que llegaran al ERP.

Figura 2: Captura de pantalla de un Sistema R/2 en una terminal IBM.

En el ano 1982 SAP genera 24 millones de marcos en beneficios y alcanza la barrera


de los 100 empleados. El 96 % de sus clientes utilizan sus productos para gestionar sus
procesos de negocio. Uno de los fundadores de la compana, Claus Wellenreuther, se
marcha.
A mediados del 83, SAP esta empleando a mas de 125 personas y para final de ano
ha generado 41 millones de marcos.
En el 84, contrata a 48 nuevos empleados para desarrollar sus modulos RK, PPS, y
RP. SAP AG Internacional es fundada en Suiza. Se generan beneficios de 48 millones de
marcos, beneficio que pasa a ser de 61 millones al ano siguiente.
En el 86, SAP abre su primera oficina internacional en Austria. El stock de la compana
pasa a ser de 5 millones de marcos y se obtienen unos beneficios de 100 millones de marcos,

14
debido a un cambio en la legislacion alemana, que requera una supervision mas estricta
de las hojas de balance. El modulo HCM sale al mercado, tras 3 anos de desarrollo.
Al ano siguiente, SAP se expande al Reino Unido, Francia y Espana. A finales de
ano, se tienen mas de 500 empleados y se generan beneficios de 152 millones de marcos.
Ademas, la nueva generacion de servidores IBM hace que el software de SAP sea accesible
a empresas de tamano medio, generando entre 30 y 200 millones de marcos. Es en este
ano cuando se empieza a gestar lo que se convertira en SAP R/3, tras intentar normalizar
los procesos internos de produccion de software.
En 1988, SAP sale en bolsa, aumentando su stock de 5 a 60 millones. La accion sale
en octubre a 750 marcos. Se abren oficinas en Italia, Dinamarca, Suecia y los Estados
Unidos. Se alcanzan 1000 clientes.
Al ano siguiente se introduce una nueva interfaz para SAP R/2, mas amigable y menos
complicada. Tambien se empieza a gestar SAP R/3, con una inversion de 85 millones de
marcos, el 33 % de los beneficios de la compana. El datacenter pasa a tener mas de 1
GB de tamano. Continua la expansion hacia el extranjero, hacia pases como Canada,
Singapur, o Australia.

Figura 3: Captura de pantalla de la interfaz de un Sistema R/2 en un entorno grafico.

En 1991, tras la cada del telon de acero, SAP responde con numerosas actividades en
Europa del Este. Se consiguen unos beneficios de 707 millones de marcos y se alcanzan los
2.700 empleados. Durante este ano se empiezan a mostrar al publico algunas pinceladas
de lo que sera SAP R/3, predecesor directo del ERP actual.

15
2.2.3. La era de SAP R/3: 1992-2001
SAP R/3 se caracteriza por introducir la arquitectura cliente-servidor al sistema SAP,
permitiendo que llegue al escritorio. Ademas, soporta bases de datos relacionales, servi-
dores de distintas companas y ofrece una interfaz grafica uniforme.

Figura 4: Captura de pantalla de una de las primeras iteraciones de un Sistema R/3.

En el ano 92 se generan mas de 831 millones de marcos en beneficios, el 50 % fuera


de Alemania, y se alcanza la cifra de 3.157 empleados. Esperando una demanda alta
para R/3, SAP empieza a buscar partners de consultora para ofrecer soporte tecnico e
implementaciones.
Al ano siguiente, SAP empieza a trabajar con Microsoft para trasladar SAP R/3 a
Windows NT. Se anade soporte para kanji, permitiendo entrar en el mercado japones. Se
porta tambien R/3 al hardware de Sun, permitiendo que corra en casi todas las platafor-
mas RISC. Se alcanzan beneficios de 1.100 millones de marcos, y 3.600 trabajadores.
En 1994 SAP R/3 es lanzado para NT. SAP se expande hacia el mercado chino. Los
beneficios aumentan hasta 1.800 millones de marcos, el 34.3 % de los cuales viene de
Estados Unidos. A finales de ano, la compana esta empleando a 5.229 personas.
En el 96 SAP introduce su estrategia de internet conjuntamente a Microsoft. A traves
de interfaces abiertas, los clientes pueden conectarse al sistema R/3 mediante la red. Se
consiguen beneficios de 3.700 millones de marcos, y se tienen 9.202 empleados a finales
de ano.

16
Al ano siguiente, SAP empieza a entrar en el mercado de Stocks de Nueva York
(NYSE). Se consiguen clientes como Daimler-Benz, General Motors, o Deutsche Post
AG. Los beneficios de la compana han crecido en un 62 % hasta alcanzar 6.020 millones
de marcos. SAP cumple 25 anos.
En el 98, Dietmar Kopp y Klaus Tschira, dos de los fundadores, dejan el comite ejecuti-
vo para marcharse hacia el comite supervisor. Esta decision marca un cambio generacional
en la compana. Los directores ejecutivos son ahora Hasso Plattner (otro fundador) y Hen-
ning Kagermann. El 3 de agosto de ese mismo ano, se ven por primera vez las siglas SAP
en el NYSE, el mercado de stocks mas grande del mundo. Se contratan 6.500 emplea-
dos nuevos, trayendo la cifra total a mas de 19.000. Los beneficios en este ejercicio fiscal
superan los 4.300 millones de euros.
En el ano 99, Hasso Plattner anuncia una nueva estrategia que lleva a la compana y a
su flota de productos hacia un nuevo rumbo, se anuncia mySAP.com. Esta reorientacion
combinara soluciones de comercio electronico con las soluciones ERP de SAP mediante
las tecnologas Web.
Tambien cambia la imagen de la compana. En octubre de ese mismo ano, el Bayern
de Munich, MLP, y otros se apuntan a este nuevo servicio. En noviembre y diciembre les
siguen Ford, Hewlett-Packard, y Hoechst Marion Roussel, entre otros.

Figura 5: Captura de pantalla de mySAP.com.

Los siguientes dos anos ven una expansion de los beneficios de la compana, lo cual es
bastante remarcable teniendo en cuenta el estallido de la burbuja de las punto com y el

17
dano al ecosistema de la economa de internet que provoco. Se generan beneficios de 6.300
billones de euros a finales del ano 2000, y se consiguen clientes como Nestle, que firma el
contrato mas grande de la historia de la compana.
A partir de aqu termina la era de SAP R/3 y comienza la etapa actual de la compana,
con un cambio de nombre de su producto estrella (que pasa a llamarse SAP ERP) y una
apuesta radical por dos nuevas lneas de negocio que se complementan: SAP HANA, y el
Cloud Computing.
Le dedicaremos el siguiente captulo entero a describir el ecosistema actual, y por
que dentro de este nuestro proyecto tiene bastante relevancia y se posiciona positivamente
en la estrategia a futuros de SAP.

18
3. Presente y futuro de SAP. HANA. Cloud Compu-
ting.
En esta seccion pasaremos a detallar la estrategia actual de SAP mediante sus dos
lneas de negocio actuales mas importantes: SAP HANA y las soluciones Cloud.
Estas dos nuevas estrategias, aunque a primera vista pudiesen parecer muy diferentes
(una es una base de datos y la otra es una serie de productos en la nube), se complementan
entre s. Es por eso que no se puede hablar de la una sin la otra.
Dividiremos, por tanto, esta seccion en dos apartados. Uno tratara sobre HANA (as-
pectos estructurales, benchmarks, lenguajes y herramientas asociadas, etc.) y el otro tra-
tara, primeramente de dar una introduccion al Cloud Computing, justificando por que es
tan atractivo para la empresa actual (en especial la PYME), tras lo cual introducira algu-
nos conceptos del Cloud Computing como IaaS, PaaS, o SaaS; y por ultimo hablara sobre
las soluciones Cloud actuales de SAP (centrandonos sobre todo en HCP, ya que es la base
sobre la cual el proyecto tecnico esta fundamentado, pero tambien hablaremos sobre otras
soluciones como SuccessFactors, Ariba, Cloud for Customer, HANA One, etc.).

3.1. SAP HANA


Que es SAP HANA? SAP HANA es, en esencia, una base de datos in-memory. Esto
es, no depende de un disco duro para funcionar, sino que guarda todos sus datos en
memoria RAM. Representa la entrada de SAP en el mercado hardware, con lo cual es
de esperar que esta base de datos tenga una serie de propiedades que beneficien a sus
productos software.
Efectivamente, ese es el caso. Recordemos que R/3 y, mas tarde, el ERP, eran el nucleo
central de la arquitectura cliente-servidor de una instalacion SAP. Esto significa que es
esencial conseguir que el hardware sobre el que corre sea lo mas eficiente y lo mas rapido
posible, pues la velocidad del componente central del landscape limita la velocidad de
todo el sistema.
Desde hace bastante tiempo, se haba estado observando que el cuello de botella a
la hora de aumentar la eficiencia del servidor era la base de datos [23]. Un statement
SELECT sobre una tabla de decenas de GB poda llevar horas, lo cual, unido al hecho
de que esa misma base de datos por lo general tiene que dar soporte a centenas e incluso
millares de usuarios, resulta bastante problematico.
Es por eso que HANA fue desarrollado. HANA son las siglas de High-Performance
Analytic Appliance, que en espanol significa Aparato Analtico de Alto Rendimiento.
Como se puede ver en el propio nombre, HANA fue disenado para conseguir velocidades
de lectura y analisis de datos que se pensaban imposibles hace algunos anos, bajando el
tiempo necesario para realizar determinadas operaciones de horas, a segundos (lo cual ha
causado algun que otro problema entre los analistas de datos, acostumbrados a aprovechar

19
los periodos de inactividad para tomarse un cafe [6]). Esto, a su vez, ha agilizado bastante
la toma de decisiones basada en Big Data a nivel ejecutivo, haciendo que, por ejemplo,
una empresa de inversion sea capaz de decidir en segundos si les conviene o no meterse a
comprar acciones de una empresa, en vez de das (abriendo bastantes posibilidades gracias
a la fusion, ahora posible, de los campos del Big Data y el High Frequency Trading). HANA
es, actualmente, la base de datos mas rapida del mundo con bastante diferencia, llegando
a alcanzar en algunas empresas como Yodobashi una reduccion de tiempo de calculo por
un factor de 125.000 [7].
Esta tecnologa esta poco a poco cambiando las reglas del juego en el campo del
Business Intelligence y en otros aspectos, lo cual le gano a su principal promotor, el doctor
Vishal Sikka, el puesto de Director Ejecutivo de la compana. Este a su vez aprovecho la
marca HANA para promover una renovacion interna bastante importante y un cambio
radical en la forma de trabajar de SAP, un trabajo que su sucesor, Bill McDermott, ha
continuado. Veremos en mas detalle este aspecto del fenomeno HANA en el siguiente
apartado sobre Cloud Computing.
Ahora bien, como se ha conseguido esto? Que es lo que hace que HANA sea una base
de datos tan tremendamente rapida? A continuacion veremos algunos aspectos estructu-
rales que nos ayudaran a comprender por que se pueden conseguir estas velocidades.

3.1.1. Arquitectura interna de HANA


La arquitectura de HANA es demasiado compleja para poder ser descrita en detalle en
un proyecto de este alcance. Podemos, sin embargo, describir las partes mas importantes
de esta desde el punto de vista del proyecto tecnico, apoyandonos en el siguiente modelo,
proporcionado por Dell [20].
Vamos, pues, a describir las partes mas importantes:

3.1.1.1. Aplicaciones Cliente


HANA puede acomodar multiples aplicaciones cliente a traves de multiples instancias
para proveer acceso a sus datos a traves de varias aplicaciones distintas.

HANA tambien puede conectarse a una aplicacion cliente via un modulo opcional
(el motor XS) usando HTTP(S). Esto es util para obtener datos de terceras partes
o datos en varios lugares distintos.

Actualmente no esta recomendado acoplar mas de un ERP por instancia de HANA


debido al peligro de replicacion de tablas.

3.1.1.2. Servidor de preprocesamiento


Es utilizado por el servidor de indexacion para analizar datos en formato texto y extraer
datos de un texto cuando se le llama a traves de la funcion Search.

20
Figura 6: Arquitectura de SAP HANA.

3.1.1.3. Servidor estadstico


El servidor estadstico analiza y presenta metricas de rendimiento de todos los compo-
nentes de HANA listados aqu, as como tambien del Hardware y el Sistema Operativo
sobre el que corre.

3.1.1.4. Servidor de nombres


En este servidor se guarda la topologa de HANA. En un landscape distribuido HANA
multiservidor, el servidor de nombres guardara que datos estan en que servidor para
optimizar la operacion de re-indexado.

3.1.1.5. Motor XS
Las aplicaciones cliente pueden usar HTTP para transmitir datos via el motor opcional
XS, que utiliza SAP ICM como servidor HTTP. Este modulo provee de acceso a los datos
en HANA transformando el modelo de persistencia guardado en la base de datos (SQL)
en un modelo listo para ser consumido por aplicaciones cliente mediante HTTP (OData,
JSON, XML). En esencia, es una integracion de un servidor web en la propia base de
datos.

3.1.1.6. Servidor de indexacion


El servidor de indexacion es la parte mas importante de la arquitectura HANA, el nucleo
sobre el que todas las otras partes pueden funcionar.
El servidor de indexacion realiza 7 funciones clave para acelerar y optimizar la analtica
(es decir, la lectura y el procesado de sus datos). Juntas, estas 7 funciones proveen de una

21
Figura 7: Arquitectura del servidor de indexacion de SAP HANA.

seguridad robusta, proteccion de datos, y acceso mejorado de datos.

Gestion de conexion y de sesion


Este componente inicializa y gestiona sesiones y conexiones para la base de datos de
HANA usando parametros de sesion preestablecidos. SAP es conocido por su exce-
lencia en la gestion de sesiones mediante su integracion de SAProuter en SAPGUI,
usado como un front-end para acceder a la pila ABAP. SAP HANA conserva el po-
tencial de configurar sus parametros de conexion y sesion para acomodar polticas
complejas de seguridad y transferencia de datos.

Autenticacion
HANA posee un sistema de privilegios basados en roles y usuarios (aunque los usua-
rios, autorizaciones y roles del ERP no son directamente transferibles a HANA). El
modelo de autenticacion de HANA permite conceder privilegios a roles o a usuarios,
y un privilegio concede a un usuario la capacidad de ejecutar una operacion SQL
sobre un objeto especfico. HANA tambien utiliza una serie de privilegios analticos

22
que representan filtros o limitaciones jerarquicas hacia abajo hacia ciertas queries
analticas, con el objeto de proteger datos clasificados de usuarios no autorizados.
Este modelo fomenta una segregacion de tareas para clientes que tengan requeri-
mientos regulatorios para la seguridad de sus datos.

Procesador SQL
El procesador SQL segmenta las queries de datos y las dirige hacia motores de pro-
cesamiento especializado para aumentar la eficiencia. Tambien provee un corrector
de errores para hacer las queries mas flexibles y eficientes. Algunos de los motores
especializados de procesamiento dentro de esta parte son:

Motor de Expresiones Multidimensionales


Se encarga de manipular los datos multidimensionales guardados en cubos
OLAP (On-Line Analytical Processing). Genera vistas analticas.
Motor de Planificacion
Se encarga de las operaciones de planificacion financiera basica dentro de la
base de datos HANA.
Procesador de Procedimientos Almacenados
Se encarga de ejecutar llamadas a procesos optimizados sin reinterpretar nada
(e.g.: convertir un infocubo estandar en un infocubo SAP HANA optimizado).
Motor de calculo
Convierte los datos en modelos de calculo y crea planes de Ejecucion Logica pa-
ra soportar un procesado en paralelo. Nos podemos comunicar con el empleando
agregacion u otras operaciones similares en SQL, SQLScript, o externamente
mediante R. Genera vistas de calculo.

Almacenamientos Relacionales
Para acelerar aun mas el acceso a los datos In-Memory, SAP ha segmentado estos
datos en compartimentos dentro de la memoria. Esto permite un acceso rapido a
los datos mas relevantes. HANA posee cuatro almacenamientos relacionales que
optimizan la eficiencia de las queries:

Almacenamiento por lneas


Guarda los datos por lneas y esta optimizado para operaciones de escritura (es
decir, para operaciones transaccionales en vez de analticas). Es derivado del
sistema In-Memory de P-Time, adquirido por SAP en 2005. Esta en la RAM.
Almacenamiento por columnas
Guarda los datos por columnas y esta optimizado para operaciones de escritura.
Es derivado de TREX (Text Retrieval and Extraction), que fue desarrollado
por SAP.

23
Almacenamiento de Objetos
Es una integracion de la tecnologa Live Cache de SAP en HANA.
Almacenamiento basado en Disco
Es utilizado para datos que no hace falta guardar en RAM. Esta localizado
en un disco duro (como las bases de datos tradicionales) que saca los datos a
RAM cuando hace falta leerlos.

Manager de Transacciones
La base de datos HANA procesa instrucciones SQL como transacciones. El manager
de transacciones controla y coordina sus transacciones y enva los datos relevantes a
los motores de procesamiento apropiados y a la capa de Persistencia. Esta segmen-
tacion simplifica la administracion del sistema y la resolucion de problemas.

Capa de Persistencia
La capa de persistencia prporciona un servicio de recuperacion de datos en caso de
corte de luz u otras emergencias. Los algoritmos y la tecnologa estan basados en
MAX DB y aseguran que la base de datos es restaurada al estado guardado mas
reciente despues de un reinicio, planeado o no. Las copias de seguridad son guardadas
como puntos de recuperacion en los volumenes de datos va un coordinador de
puntos de recuperacion que tpicamente guarda el estado de la base de datos cada
10 minutos. Todos los puntos de cambio que suceden tras un punto de recuperacion
se denominan como transacciones no consignadas y se guardan en los Volumenes
de Logs de transacciones. Tpicamente, estos volumenes son guardados en medios
externos y enviados a otras localizaciones en caso de desastre.

Repositorio
El repositorio gestiona el versionado de objetos de metadatos como pueden ser
los Atributos, las vistas Analticas o los procedimientos almacenados. Se pueden
importar y exportar datos.

3.1.2. Lenguajes asociados a HANA


Una vez vistos los principales componentes en una arquitectura HANA podemos ha-
cernos la siguiente pregunta: como interactuamos con estos componentes?
Evidentemente no podemos usar el mismo lenguaje de programacion para referirnos
al almacenamiento por lneas que el que usamos para comunicarnos con XS, o con el
motor de calculo. A continuacion veremos cuales son los lenguajes necesarios para poder
interactuar con HANA. Antes de empezar con esta seccion, conviene destacar que la
mayora (no todos) de los lenguajes de programacion aqu listados son ya estandares del
mercado. En contraposicion a esto, el unico lenguaje con el que nos podemos comunicar
con el ERP es ABAP/4, un lenguaje propietario de SAP y utilizado casi exclusivamente

24
por los productos de la compana. Esto supone un ejemplo del cambio de la manera de
trabajar de SAP promovido por Vishal Sikka.

3.1.2.1. Almacenamientos Relacionales: SQL


No es de extranar que la manera de comunicarse con el almacenamiento de HANA sea
exactamente la misma que la de practicamente cualquier otra base de datos existente. SQL
es una solucion tan extendida que no hace falta haber trabajado con HANA previamente
para poder empezar a trabajar en esta parte. Sin embargo, como el lector probablemente
ya sabra, practicamente nadie usa el estandar promovido por la ISO a secas. Hay cientos
de dialectos distintos de SQL (mySQL, PostgreSQL, SQLite, entre muchos otros), que
responden a las distintas necesidades y arquitecturas de las bases de datos con las que se
comunican (evidentemente, una base de datos que corra sobre Android va a tener distintas
prioridades y necesidades que una que corra sobre DB2).
El dialecto que SAP utiliza fue desarrollado internamente (lo cual es logico, tenien-
do en cuenta las diferencias estructurales entre HANA y cualquier otra base de datos).
Actualmente carece de nombre, as que lo nombraremos simplemente como SQL en este
documento, pero de nuevo hemos de insistir: difiere del SQL estandar.
Como se puede observar en la gua de referencia de SAP [15], vemos que tenemos las
operaciones CRUD (Create, Read, Update, Delete), as como los Joins, Drops, y todas
las demas operaciones de cualquier dialecto SQL que se precie. Sin embargo, tambien
podemos observar algunos cambios bastante significativos, como la capacidad de insertar
datos en una tabla en forma de columnas, y no de filas, o la capacidad de crear lo que se
denominan como vistas.
Estos cambios responden a la necesidad de optimizar el almacenamiento de datos segun
la arquitectura interna de HANA. Es de destacar que el almacenado por columnas per-
mite, ademas de aumentar la velocidad de aplicaciones que tengan que leer unos cuantos
atributos de muchas entidades distintas, comprimir datos de una manera considerable,
debido a lo siguiente. Supongamos que tenemos en una tabla los datos de personal de
nuestra compana, de tal forma que cada fila es un empleado. Supongamos tambien que
por una casualidad, tenemos 3 empleados (filas) distintos que viven en la misma ciudad
(por ejemplo, Sevilla). Normalmente, en una tabla almacenada por filas, tendramos que
poner la misma ciudad 3 veces seguidas en las 3 filas, con lo que no podemos aprovechar
esta redundancia para comprimir datos. Un ejemplo de SQL sera en este caso:

INSERT INTO TABLE PERSONAL ( Numero , Nombre , Ciudad ) VALUES (01 , Fulano , Sevilla );
INSERT INTO TABLE PERSONAL ( Numero , Nombre , Ciudad ) VALUES (02 , Mengano , Sevilla );
INSERT INTO TABLE PERSONAL ( Numero , Nombre , Ciudad ) VALUES (03 , Zutano , Sevilla );

En una tabla fila, esto se guarda como: [01, Fulano, Sevilla][02, Mengano, Sevilla][03,
Zutano, Sevilla]. En cambio, en una tabla columna se guarda como [01, 02, 03][Fulano,
Mengano, Zutano][Sevilla(3)]. Como se puede observar, guardandolo como tabla columna

25
conseguimos aprovechar la redundancia en la informacion para reducir el tamano de los
datos guardados. El concepto de tabla columna no existe en SQL nativo. Para conseguir
crear tablas columna se utiliza la instruccion CREATE COLUMN TABLE.
Otro concepto nativo al SQL de HANA es el de las vistas. Hay 3 tipos de vistas:
Analticas, de Atributo, y de Calculo. Por lo general, una vista de Atributo captura un
unico Atributo de una tabla de datos Maestros, de tal forma que ese Atributo pueda
interactuar con todas las otras tablas y vistas del sistema. Una vista Analtica captura
un infocubo (es decir, una serie de atributos con todas las entidades que lo posean). Y
una vista de calculo parte de otras tablas y otras vistas para ejecutar algoritmos que
satisfagan requerimientos de negocio complejos. Para referirse a una vista (que no para
crearla, para eso necesitaremos HANA Studio o SQLScript, que veremos mas adelante)
se utiliza el smbolo ::, que, de nuevo, no esta en la implementacion estandar de SQL.
Y, por ultimo, tambien utilizamos SQL para conceder o quitar privilegios mediante el
comando GRANT, que, de nuevo, no esta en el estandar.

3.1.2.2. Motor de calculo: SQLScript, R, HANA Studio


Para interactuar con el motor de calculo de HANA tenemos varias opciones. Una de ellas
es HANA Studio, que permite generar vistas de calculo de manera grafica, con lo que
podemos crear agregaciones, joins y todo tipo de queries complejas que nos permiten
obtener resultados analticos que antes eran mucho mas difciles de conseguir.

Figura 8: Captura de pantalla de SAP HANA Studio.

Luego tenemos SQLScript. SQLScript [16] es un lenguaje propietario de SAP, con el

26
que se pueden implementar algoritmos de calculo complejos, y guardarlos en un wrapper
al que podemos llamar mediante SQL. Es, logicamente, mas dificil de manejar que SAP
HANA Studio, pero permite hacer calculos muchsimo mas complejos. Esto es debido,
entre otras cosas, a que mediante SQLScript se pueden crear wrappers de los algoritmos
almacenados en la Librera de Analisis Predictivo, o PAL [13] (de Predictive Analysis
Library). Esta librera posee algoritmos complejos para tareas como la prediccion de series
temporales (ARIMA, Suavizado Exponencial Triple), el clustering (k-means, mapas auto
organizados de Kohonen), la clasificacion y toma de decisiones (C4.5), y demas. Estos
algoritmos combinados con la velocidad de HANA son una herramienta muy poderosa,
pudiendo hacer segmentaciones de mercado, analisis de hashtags corporativos en Twitter,
relaciones de compras entre los productos de una compana, y predicciones de tendencias
en ventas, entre otras muchas cosas, en segundos.
Pero con eso y todo, el numero de algoritmos actualmente disponibles en la PAL
es limitado. Es por eso que disponemos de una herramienta aun mas poderosa si cabe,
conocida como R. R es un lenguaje estadstico de codigo abierto muy poderoso, hasta
el punto de que se ha impuesto a otros como SPSS y demas. HANA no es capaz de
interpretar R nativamente, pero mediante Rserver, puede transmititir los datos necesarios
a un servidor externo que s lo pueda interpretar, y este puede devolver los resultados [14].
Desafortunadamente, dado el volumen de datos con el que se suele trabajar en HANA,
tener que mandarlos hacia un servidor externo y traer los resultados de vuelta puede
suponer un cuello de botella bastante importante, dependiendo del volumen de datos
necesario para hacer el calculo. Pero aun as, para operar con algoritmos matematicos
muy complejos, esta es la manera de hacerlo.

3.1.2.3. HANA XS: Javascript, SAPUI5, XSJS


Por ultimo hablaremos de XS. Como ya habiamos perfilado anteriormente, XS actua a
modo de servidor web, consumiendo los datos de HANA mediante SQL y sirviendolos en la
nube mediante OData, JSON, XML, o incluso directamente como aplicacion web. Esto se
consigue mediante una estructura de paquetes, en los cuales se guardan diversos archivos
nativos a XS para detallar los permisos, la estructura web de la aplicacion, y diversos
otros archivos como CSS y demas. Despues de esto tenemos los archivos Javascript. Estos
se dividen en dos, archivos de lado cliente (en el patron de diseno MVC, corresponden
a la vista y al controlador) y archivos de lado servidor (en el patron de diseno MVC, se
corresponden al modelo).
Los archivos de lado cliente son llamados al navegador mediante HTML5. Estos archi-
vos usan, como ya hemos dicho, SAPUI5. SAPUI5 es un framework montado sobre JQuery
y Javascript, que simplifica bastante la creacion y el diseno de paginas web, dotandolas
de un aspecto profesional adecuado para aplicaciones de negocio. Actualmente hay un
subconjunto de SAPUI5 conocido como OpenUI5, de codigo abierto, que es gratuito y
accesible a cualquiera. OpenUI5 es lo que hemos empleado para crear nuestra aplicacion

27
Figura 9: Estructura de archivos de un proyecto HANA XS, mostrado en Eclipse.

web. SAPUI5 contiene a todo OpenUI5 y ademas anade algunos widgets.


En cuanto a los archivos de lado servidor, usan un dialecto de Server-Side Javascript
conocido como XSJS, que es derivado de Spidermonkey, el interprete de Javascript de
Mozilla. Al ser de codigo abierto y ser relativamente ligero y eficiente, fue el elegido
para ser modificado e integrado en HANA por encima de otras alternativas, quiza mas
populares, como Node.js. Si los archivos anteriores se encargaban de recibir y presentar
los datos, estos se encargan de transformarlos de formato SQL a un formato mas accesible
como puede ser JSON u OData antes de servirlos al cliente.

Figura 10: Captura de pantalla de una aplicacion web desarrollada en HANA XS, usando
SAPUI5.

28
3.1.3. Benchmarks de HANA
Si se tiene que sacar una unica conclusion de los dos subapartados anteriores, proba-
blemente sera esta: HANA es un aparato complejo y laberntico. Sin embargo, a pesar de
la dificultad intrnseca que supone desarrollar sobre esta plataforma, sigue siendo a da
de hoy la tecnologa con mas rapido crecimiento de SAP. Por que ocurre esto?
Como ya hemos adelantado en la introduccion a este apartado, es por la velocidad que
se puede llegar a alcanzar. Fijandonos en los benchmarks de HANA One [12], podemos ver
algunas cifras que nos ayudaran a ver exactamente de que ordenes de magnitud estamos
hablando.
Se empleo para este test una estrucuras de tabla creada durante diez anos de uso por
un modulo SD (Sales & Distribution) de una empresa cuyo nombre no se menciona en
el documento. Esta estructura estaba compuesta por una Fact Table, con 1,2 billones
de lneas, y varias otras tablas mas pequenas. El tamano original de los datos era de 1
PetaByte, o 1.024 TeraBytes. El sistema HANA que se utilizo para procesar esta informa-
cion estaba compuesto por 95 nodos (es decir, 95 aparatos HANA trabajando en paralelo).
Cada uno de estos nodos posea 4 CPUs con 10 nucleos por CPU y 2 Hyper-threads por
nucleo, 1 TB de RAM y 3.3 TB de Disco Duro.
Este volumen de datos se cargo en el sistema HANA usando 9 hilos distintos de sta-
tements SQL, llegando a cargar 53 millones de lneas por minuto. Una vez cargado en el
sistema HANA, este utilizo la tecnica de compresion que hemos detallado anteriormente
para aprovechar la redundancia existente en las columnas de la tabla. De 1 PB, la estruc-
tura fue comprimida a 49.2 TB, un factor de compresion de 20.8. Esto significo que se
pudieron guardar todos los datos en RAM, y lo que es mas, que solo ocuparon la mitad
de esta en cada nodo.
Una vez los datos fueron cargados y comprimidos, se les hicieron una serie de queries
empleadas frecuentemente en Business Intelligence, de Reporting, Drilldown y Analti-
ca. Todas ellas tardaron menos de 3.5 segundos en ejecutarse, menos de un segundo si
ignoramos las queries de analtica.
Como se puede ver, los resultados alcanzados son absolutamente monstruosos, ejecu-
tando queries que podran haber tardado semanas o incluso meses en otras bases de datos
en menos de 5 segundos. Es por esto que SAP esta empleando la marca HANA como un
ariete para intentar hacerse con un puesto de liderazgo en el mercado Cloud, utilizan-
do una estrategia agresiva de compra de soluciones existentes y desarrollo de soluciones
nuevas. Veremos en mas detalle esto en el siguiente apartado.

29
3.2. Cloud Computing
En esta seccion procederemos a hablar del Cloud Computing, introduciendo primero
algunos conceptos basicos, el impacto disruptivo que esta teniendo en el ecosistema em-
presarial de las tecnologas de la informacion, y despues pasaremos a detallar la estrategia
de SAP con respecto a esta nueva manera de ofrecer servicios, tras lo cual habremos
detallado en suficiente detalle el contexto alrededor del cual el proyecto tecnico se ha
desarrollado.

3.2.1. Introduccion y conceptos basicos


Que es el Cloud Computing y por que esta resultando ser tan disruptivo para las
empresas TIC? Principalmente por motivos de reduccion de costes, overhead administra-
tivo, y desaparicion de la necesidad de saber mantener una infraestructura. Pensemos por
ejemplo en un ERP. Para poder utilizarlo en las premisas de la compana necesitamos
comprar un servidor con bastante memoria, necesitamos contratar a una empresa de con-
sultora Basis que se dedique a instalar los componentes necesarios y configurar la VPN
de la compana para tener acceso, necesitamos tener a gente que sea capaz de mantener
dicho servidor y el software que se fundamenta en el...
Todo ello cuesta dinero y tiempo. Ahora bien, que ocurrira si en lugar de tener
que instalar el ERP en nuestras premisas podemos contratarlo como un servicio al que
podemos acceder desde nuestro navegador, como si fuese un cliente de correo web o un
editor de documentos como Google Docs?
Desaparecen una buena cantidad de dificultades. Ya no tenemos que comprar activos
como servidores ni tenemos que calcular reglas de depreciacion para ellos. No nos hace
falta instalar nada, ni mantener ningun equipo. Si tenemos que expandir la memoria del
sistema, solo tenemos que contactar a la empresa que nos suministra el servicio en vez de
tener que comprar otro servidor, instalarlo y mantenerlo. Esto supone un ahorro bastante
importante de costes y tiempo, lo cual abre un mundo de posibilidades para la pequena
empresa.
Habiendo establecido el por que del interes del Cloud Computing y por que es tan
disruptivo para la manera tradicional de hacer negocio de las TIC, pasaremos a introducir
algunos terminos basicos, necesarios para entender algunos conceptos del proyecto tecnico
mas adelante.

3.2.1.1. onPremise
Un servicio onPremise es un servicio cuya infraestructura fsica esta en manos de la
empresa que lo utiliza. Por ejemplo, un servidor en la empresa que tenga aplicaciones de
correccion de bugs, mantenimiento y versionado de codigo fuente, etc.

30
3.2.1.2. onCloud
Un servicio onCloud es aquel que es proporcionado por una empresa en la nube, y cuyos
servidores no estan en las Premisas de la compana cliente. Hay tres modalidades de
servicios onCloud:

Infrastructure as a Service
Es la forma mas basica de Cloud Computing. Consiste en arrendar al cliente el uso
de una maquina (ya sea fsica o virtual) en la nube. El proveedor se encarga de
mantener la infraestructura y el cliente ha de encargarse de todo lo demas, desde la
instalacion del sistema operativo hasta la gestion y mantenimiento del software que
haya decidido poner. Un ejemplo de IaaS es CloudFlare, que te da un servidor en la
nube mediante el cual puedes crear y exponer diversas aplicaciones web a la nube.

Platform as a Service
En este servicio se ofrece la infrastructura y ademas se ofrecen una serie de herra-
mientas de software, tales como sistemas operativos, compiladores, y demas. Este
servicio elimina la necesidad de contratar a administradores de sistemas, siendo ne-
cesarios unicamente los desarrolladores de aplicaciones web para montar una apli-
cacion sobre la plataforma.

Software as a Service
Esta modalidad de servicio nos ofrece ya directamente una aplicacion software desa-
rrollada por el proveedor en la nube. Ejemplos de este tipo de servicio podran ser
Gmail u Outlook (en contraposicion a aplicaciones como Thunderbird o Maildroid),
Google Docs (en contraposicion a Office), etc.

3.2.1.3. REST
REST son las siglas de Representational State Transfer, Transferencia de Estados de
Representacion en espanol. Es, esencialmente, una manera de interactuar con una base de
datos en remoto mediante un endpoint HTTP, con protocolos como SOAP u OData. Esto
quiere decir, que podemos, por ejemplo, anadir una lnea a una tabla de nuestra Base de
Datos de interes llamando mediante un mensaje POST al servicio OData (por ejemplo)
conectado a esta, con los datos que queremos insertar codificados como parametros en
la URL empleada para la llamada. En otras palabras, es una manera de interactuar en
remoto con una base de datos mediante llamadas a la URL del servicio conectado a ella,
en vez de utilizar SQL.

3.2.1.4. MVC
MVC (siglas de Modelo, Vista, Controlador) es un patron de diseno de aplicaciones web,
que aboga por separar la logica de negocio de la aplicacion (contenida en el controlador),
la interfaz con el back-end y la base de datos (contenida en el modelo), y la interfaz con

31
el usuario de la aplicacion (contenida en la vista). Por motivos estructurales evidentes,
tras pensarlo, el modelo siempre suele residir en el servidor, mientras que la vista suele
residir de lado cliente y el controlador puede estar en ambos, dependiendo del volumen
de informacion que tenga que intercambiar con el modelo o con la vista. En nuestro caso
el controlador reside en el lado del cliente.

3.2.2. Estrategia y soluciones de SAP respecto a la nube


La estrategia de SAP respecto al Cloud Computing se puede calificar como de expan-
sion agresiva [4]. Durante estos ultimos anos, ha estado desarrollando varias soluciones
en la nube alrededor de su marca HANA, tales como HANA Cloud Platform, HANA One,
HANA Enterprise Cloud, Cloud for Customer, HANA Cloud Integration, y otros. Pero
no ha parado ah, tambien ha estado comprando empresas como SuccessFactors, Ariba o,
mas recientemente, Concur. Todo esto es debido a la necesidad de reinventarse lo antes
posible, ya que debido a las ventajas que hemos comentado en el apartado anterior, pro-
ductos como el ERP van a acabar desapareciendo en la forma en que los conocemos hoy
en da.
Ademas de esto, hemos visto como se ha bajado en gran medida la barrera de entrada
al ecosistema SAP, mediante la utilizacion de estandares como Javascript, OData y otros,
y mediante iniciativas como OpenSAP, que ofrece cursos de formacion gratuita sobre
algunas de las lneas de negocio mas novedosas de SAP. Esto es debido a que con la
llegada del Cloud Computing se espera aumentar todava mas el volumen de negocio, lo
cual hace necesario tener mas partners capaces de ofrecer soluciones y servicio tecnico
sobre los nuevos productos de SAP.
Pasaremos, pues, a detallar algunas de las soluciones mas relevantes de SAP en la
nube, especialmente las relacionadas con el proyecto tecnico.

3.2.2.1. HANA Cloud Platform


HANA Cloud Platform es, como su propio nombre indica, un PaaS. Al ser el servicio
sobre el que se ha desarrollado la aplicacion web que vamos a tratar, nos meteremos en
mas detalle en los aspectos estructurales de este servicio.
Consiste basicamente en una instancia de HANA en la nube, junto a XS, y una serie de
herramientas que no vienen includas en HANA por s mismas, tales como una maquina
virtual de Java sobre instancias HANA o MaxDB (lo cual nos permite comunicarnos con la
base de datos empleando este lenguaje mediante JDBC o EJB, herramientas esenciales a
la hora de conseguir integrar los datos de nuestro sistema onPremise con este servicio), un
interprete HTML5 con un servidor de proxy inverso integrado (lo cual nos permite burlar
la poltica de seguridad de mismo Origen, tecnica necesaria para poder obtener datos de
servicios REST que no esten en el mismo servidor que nos ha servido la aplicacion web.
Una tecnica similar vera su uso mas adelante).

32
Ademas de esto, mediante HANA Cloud Platform podemos acceder a servicios como
HCI OData Provisioning, capaz de exponer BAPIs a la nube mediante formato OData, o a
servicios de autentificacion como OAuth o SAML. Es, en esencia, una caja de herramientas
asociada a HANA, mediante la cual se pueden implementar funcionalidades que no se
podran implementar utilizando solo HANA (como veremos mas adelante).
Actualmente HCI todava se esta haciendo un hueco en el mercado, y suele ser emplea-
do para implementar extensiones a SuccessFactors, comunicandose con este directamente
mediante APIs, o utilizando serrvicios como HANA Cloud Integration.

3.2.2.2. HANA One


HANA One es un IaaS ofrecido en colaboracion con Amazon, y es la forma mas barata hoy
por hoy de conseguir una instancia HANA. Fue desarrollado como reaccion de SAP para
intentar conseguir atraer startups, que por lo general suelen tener recursos limitados pero
que son capaces de ofrecer mucha mas innovacion y desarrollo que una empresa grande
que no este dispuesta a asumir ciertos riesgos. HANA One es una instancia virtual de
HANA y XS en la nube, con 30 GB de espacio, y el acceso cuesta aproximadamente 3.50$
por hora.

3.2.2.3. HANA Enterprise Cloud


Si la solucion anterior estaba pensada para la startup, esta esta pensada indudablemente
para la gran empresa. Es una instancia de HANA sobre la que se ha instalado uno o varios
componentes de la Business Suite, de tal forma que tenemos un SaaS, consiguiendo todas
las ventajas que ofrece HANA y SaaS en un solo paquete, siendo, eso s, bastante mas
caro que todas las otras soluciones listadas aqu.

3.2.2.4. HANA Cloud Integration


HANA Cloud Integration es la solucion cloud oficial de SAP para actuar como middle-
ware entre sus distintas soluciones (en contrapartida a SAP PI, su solucion onPremise).
Mediante HCI podemos realizar lareas de ETL entre diversas soluciones, ya sean onCloud
u onPremise, sin necesidad de saber ningun lenguaje de programacion (en teora). Si bien
esta es una solucion factible a la hora de transportar nuestros datos entre nuestro ERP y
nuestra instancia de HCI, no es esta la solucion que hemos empleado.

3.2.2.5. SuccessFactors, Ariba, Concur, etc


Estas soluciones en la nube proporcionan funcionalidades similares a las que SAP propor-
cionaba onPremise. Por ejemplo, SuccessFactors es una solucion HCM (Human Capital
Management) en la nube, en contraposicion a SAP HCM. Ariba es una solucion de logsti-
ca (Materiales, etc). Y Concur es una solucion de gestion de viajes. Todas estas soluciones
fueron desarrolladas por empresas que, posteriormente, han sido compradas por SAP. Suc-

33
cessFactors ha sido una de las mas exitosas en este sentido, lo que le valio a su Director
Ejecutivo una plaza en el consejo ejecutivo de SAP.

3.3. Conclusiones
Como se puede observar mediante la seccion anterior, este cambio tan radical de estra-
tegia esta generando un nuevo mercado en falta de nuevos profesionales con conocimientos
especializados, lo cual abre una serie de posibilidades para las empresas de consultora
dedicadas a SAP. En concreto, estamos empezando a ver cada vez mas proyectos rela-
cionados con la migracion de datos de sistemas onPremise hacia sistemas onCloud, con
HANA, y con otras soluciones como Fiori o Lumira. Para mantenernos al da con las habi-
lidades y conocimientos que requiere el mercado, hemos desarrollado una aplicacion, que,
mediante una interfaz web, es capaz de subir nuestras tablas de datos desde nuestro ERP
hasta nuestra instancia de HCP, y procesarlos. En el siguiente apartado veremos como
hemos desarrollado esta aplicacion, que tecnologas y herramientas han sido necesarias, y
que somos capaces de hacer con ella.

34
4. Proyecto tecnico. Flujo de integracion de datos
entre un ERP y HANA Cloud Platform.
En esta seccion pasaremos a detallar los detalles relacionados con las tecnologas y la
arquitectura necesaria para implementar un flujo de integracion entre un ERP y HANA
Cloud Platform. Podemos dividir el trabajo realizado en tres modulos:
Modulo onPremise: Cloud Connector, ABAP (RFC)

Modulo de integracion onCloud: Destinos HCP, Java (JCo, JDBC)

Modulo de presentacion onCloud: HANA XS, Javascript (SAPUI5, XSJS), HTML5


Se podran observar (sobre todo en el modulo de integracion) algunas decisiones de
diseno poco ortodoxas, teniendo en cuenta los estandares propuestos por SAP. Como se
vera mas adelante, hemos decidido desarrollar el programa con esta arquitectura para
minimizar tanto los cambios que tengamos que hacerle al ERP, como el coste monetario
del entorno de desarrollo. Veremos mas adelante que, si bien esta manera de desarrollar no
es ni mucho menos un estandar, es muy poco intrusiva y, lo que es quiza mas importante,
usa herramientas completamente gratuitas.

4.1. Introduccion: Arquitectura


Como ya hemos comentado anteriormente, podemos dividir en 3 modulos el trabajo
desarrollado, pero, como se complementan? Como interactuan entre s?
La figura 11 nos muestra una vista de aguila de las tecnologas empleadas y como in-
teractuan entre s. Podemos ver al ERP conectado a Cloud Connector mediante el router,
que se encarga de crear un tunel entre la red local y HCP. La maquina virtual de Java de
HCP tiene una aplicacion funcionando que recoge las tablas de datos en formato nativo
(mediante abapDAO), las transforma en statements SQL que son mandados hacia la base
de datos HANA (mediante hanaDAO) de tal forma que el modulo de presentacion puede
recogerlos y actuar sobre ellos. El modulo de presentacion y el modulo de integracion
se comunican entre s mediante HTTP y (necesariamente, al poseer dominios distintos,
aunque ambos esten en la misma plataforma) CORS. A partir de ah, el modulo de pre-
sentacion consume los datos obtenidos por el modulo de integracion y los presenta en
forma de tabla, arbol, grafica, u otras formas. Para poder presentar esos datos en forma
de aplicacion web, se usa SAPUI5, que, como veremos mas adelante, es un framework
montado sobre Javascript y jQuery disenado para facilitar la tarea de disenar una apli-
cacion web, dandole por defecto un formato profesional y admitiendo la personalizacion
de sus assets mediante CSS4. De esta forma, conseguimos una aplicacion capaz de subir
cualquier tabla a nuestra instancia de HCP dandole solo su nombre, ademas de poder
subir datos en formatos mas difciles de procesar como clusters, y utilizando esos datos

35
Figura 11: Arquitectura de la aplicacion desarrollada.

para, por ejemplo, poder visualizar la estructura organizativa de una empresa desde cual-
quier parte y poder visualizar una ficha resumen de cualquier empleado de la compana,
herramientas que resultaran utiles para, por ejemplo, ejecutivos de una compana grande.
En las siguientes secciones, pasaremos a detallar las tecnologas utilizadas para hacer
posible estas funcionalidades, por que han sido elegidas, y como interactuan entre s.

4.2. Modulo onPremise


La finalidad de este modulo consiste en dotar a un ERP con una version NetWeaver
7.2 y sin el addon IWBEP, de capacidad para exponer sus datos a la nube de una forma lo
mas generica posible, sin huecos de seguridad, y de una forma lo menos intrusiva posible
para el propio ERP (ya que es una herramienta crtica para muchos otros proyectos en
curso).
Para conseguir esto, empleamos una herramienta conocida como Cloud Connector, ca-
paz de recibir datos del ERP mediante el protocolo RFC y mandarlos mediante tunel SSL
a una instancia cualquiera de HCP. Cloud Connector no requiere de ninguna instalacion

36
en el ERP para funcionar, siendo simplemente un intermediario (o middleware) entre las
propias capacidades de transmision de datos en remoto del ERP y HCP. Sin embargo,
Cloud Connector no esta pensado, en un principio, para la transmision de datos, sino
mas bien para poder ejecutar codigo en remoto usando una aplicacion web. Veremos mas
adelante como hemos sido capaces de superar esta limitacion.

4.2.1. SAP HANA Cloud Connector


Fijandonos en la documentacion de SAP [11], Cloud Connector sirve como enlace entre
las aplicaciones desarrolladas en HANA Cloud Platform y sistemas onPremise existentes.
Mediante Cloud Connector puedes controlar exactamente que recursos estan disponibles
para poder ser observados desde la nube, pudiendo beneficiarte de tus recursos onPremise
sin tener que exponer el ERP entero.

Figura 12: Funcionalidad de Cloud Connector.

Para no modificar demasiado al ERP, decidimos instalar Cloud Connector en un or-


denador externo en vez de en el propio servidor. Esto implica que la comunicacion entre
el ERP y Cloud Connector, incluyendo los datos transmitidos al ERP, han de pegar unos
cuantos saltos por la red local antes de llegar a su destino, lo cual puede ser problematico
si queremos subir, digamos, unos cuantos GB. Sin embargo, dado que el volumen de datos
con el que estamos trabajando es relativamente pequeno, podemos ignorar esta limitacion
sin problemas.

37
La instalacion de Cloud Connector resulto ser relativamente sencilla, no muy distinta
de la instalacion de cualquier otra aplicacion en Windows. Tras la instalacion, pudimos
acceder a la interfaz de configuracion de Cloud Connector accediendo en el navegador a
la direccion https://localhost:8443.

Figura 13: Interfaz de configuracion de Cloud Connector.

En la interfaz de configuracion, Cloud Connector nos peda un nombre de usuario y


una contrasena para acceder al ERP, un tipo de comunicacion (HTTP o RFC) y la URL
o el socket necesario para localizar al ERP. Ademas, nos ofrece una opcion para mapear
la direccion IP y puerto locales del ERP a una direccion IP y un puerto virtuales, de tal
forma que no tengamos que exponer nuestros datos de conexion a la nube. Por el otro
lado de la comunicacion, nos exige nuestro nombre de usuario y contrasena de nuestra
instancia en HANA Cloud Platform.
Debido a que, por falta de varios addons no disponamos de NetWeaver Gateway, no
disponamos tampoco de un endpoint HTTP nativo, con lo que la unica forma disponible
para conectar Cloud Connector con el ERP es mediante RFC. RFC es un protocolo
propietario de SAP mediante el cual se puede llamar a una funcion del ERP en remoto y
recibir sus resultados. Se nos ofrece ademas la posibilidad de cifrar la comunicacion entre
el ERP y Cloud Connector va SNC, pero dado que esa comunicacion se produce dentro
de nuestra red local, no hemos considerado necesario hacerlo (la comunicacion entre Cloud
Connector y HCP siempre va cifrada, por defecto).
Esto nos dota de la posibilidad de ejecutar codigo y recibir los resultados desde nuestras
aplicaciones en la nube, con lo cual hemos completado una parte de la integracion. Pero
no podemos todava extraer los datos de nuestro ERP, al menos no directamente. Para
conseguirlo, tendremos que hallar, o programar, funciones en SAP capaces de extraer y
transmitir los datos de una tabla u otro tipo de estructuras de datos recibiendo como

38
argumento unicamente su nombre. Veremos en el siguiente apartado que tipo de funcion
es necesaria para poder realizar esta tarea.

4.2.2. Codigo necesario en el ERP. ABAP. RFC.


Para poder cumplir el proposito original de nuestro modulo onPremise, necesitaremos
hallar, o programar, funciones en el ERP que sean capaces de tomar como argumento una
referencia a la estructura de datos que queremos subir y que puedan devolver la estructura
de datos en s, mediante RFC. Atacaremos primero las tablas, que son las estructuras de
datos mas sencillas y aquellas sobre las que se fundamentan la mayora de transacciones
del ERP, y luego discutiremos como enfrentarnos a estructuras mas complicadas como
clusters salariales o estructuras organizativas.

4.2.2.1. Tablas
La estructura de tablas es la mas universal y utilizada en el ERP, de tal forma que si po-
demos subir nuestras tablas al ERP, podremos subir la mayora de datos que contiene. Las
tablas del ERP pueden ser observadas mediante la transaccion SE11 (Una transaccion es
la ejecucion de un programa. Hay varios tipos de transacciones, para registrar materiales,
contratar a un empleado, etc. Esta en concreto sirve para visualizar tablas).

Figura 14: Ejemplo de tabla visualizada mediante SE11.

Por suerte hay una funcion RFC ya includa en SAP capaz de tomar como argumento
el nombre de la tabla y devolver sus datos, conocida como RFC READ TABLE . Esta
funcion, aunque no sea ya mantenida por SAP, es sin embargo capaz de extraer datos de
nuestras tablas y exponerlas al modulo de integracion.
Sin embargo, tenemos varios problemas. Uno de ellos es que el formato en el que
mandamos los datos no se corresponde con ningun estandar, con lo cual nos resulta

39
difcil procesarlos y mandarlos a la base de datos. El segundo, mas acuciante, es que
RFC READ TABLE tiene un lmite de 128B por lnea. Esto, teniendo en cuenta que
muchas tablas en SAP pueden poseer mas de 50 columnas, es una limitacion bastante
importante. El primero de estos problemas sera tratado en el modulo de integracion, pero
el segundo tenemos que tratarlo aqu. Basicamente tenemos dos soluciones:

Enviar la tabla por fragmentos

Buscar otra funcion

Eventualmente elegimos la segunda opcion, por ser mas sencilla de programar. En-
contramos un sustituto ideal en la funcion /BODS/RFC READ TABLE2. Sin embargo,
esta funcion resulto no estar por defecto en el ERP. Tuvimos que instalar un modulo de
funcion en el ERP, para lo cual empleamos la transaccion CG3Z para subir los archivos
necesarios al servidor, y despues tuvimos que utilizar el sistema de transporte y correccion
(CTS) para poder acceder a ellos mediante el ERP. Esto aumento nuestro lmite de 128B
a 30KB, mas que suficiente para casi cualquier tabla.
Llamando a esta funcion en remoto y pasandole el nombre de la tabla que queremos
subir, podemos obtener los datos que queremos.

4.2.2.2. Clusters

Figura 15: Transaccion SE80. Editor de Objetos.

Un cluster es un tipo especial de tabla, que contiene una gran informacion sobre un
objeto de negocio (como un empleado, un material, etc). Esto hace que, por eficiencia, se
escriban de manera comprimida a la base de datos, lo cual acelera bastante la extraccion

40
de datos mediante una clave primaria y, lo que es quiza mas importante para nuestros
propositos, hace que este tipo de tablas no son visibles directamente mediante SE11, con
lo cual no podemos utilizar /BODS/RFC READ TABLE2. Esto implica que tendremos
que utilizar otro tipo de funciones.
Nos interesan en este caso los clusters salariales, con lo cual, tras investigar, hemos
visto que las funciones PYXX GET RELID FROM PERNR y CU READ RGDIR son
ideales para nuestros propositos, ya que pasando como parametro el numero del em-
pleado del que queremos observar los datos, se nos devuelve una estructura con to-
dos los clusters asociados a su historial salarial. Despues de eso empleamos la funcion
PYXX READ PAYROLL RESULT, para poder acceder a una vista mas detallada de los
periodos salariales del empleado en cuestion.
Para ser mas exactos, mediante la primera funcion obtenemos el ID relacional de la
estructura de clusters en la que se hallan los datos que nos interesan, pasando como
parametro el numero de empleado. Hecho eso, utilizamos el numero de secuencia de la
entrada del historial salarial sobre la que estemos mas interesados como argumento para
pasarle a la tercera funcion.
Sin embargo tuvimos un problema. Estas funciones estan disenadas para extraer datos
y presentarlos a otros procesos del propio ERP. Es decir, no son capaces de comunicarse
mediante RFC. Para solventarlo, tuvimos que crear una funcion Z que llamase a estas
funciones internamente y que s fuese capaz de utilizar RFC.
Que es una funcion Z? Es una funcion disenada internamente por la empresa usuaria
del ERP u otra a la que esta haya contratado, para su uso propio. Obviamente este tipo
de funciones no estan por defecto en el sistema SAP ni son soportadas por la compana,
pero son esenciales en cualquier ERP ya que mediante este tipo de funciones se puede
ajustar el ERP (o parametrizar, como se le conoce a esta practica en el mundillo de la
consultora SAP) a las necesidades especficas de cada cliente. A este tipo de funciones se
le llaman funciones Z debido a que su nombre suele empezar por Z, ya que SAP ya desde
sus inicios se comprometio a no implementar nunca un modulo de funcion que empezase
por esta letra, para evitar conflictos de nombres.
Para crear una funcion Z hemos de movernos a la transaccion SE80, el editor de
objetos, que es la transaccion mas utilizada para cualquier programador ABAP. Mediante
esta transaccion creamos primero un grupo de funciones, al que denominamos Z RFC HR,
en el que depositaramos estas funciones que emplearemos a modo de wrapper, y todas
las demas que nos vayan a hacer falta en un futuro.
Una vez creado este grupo de funciones, empezamos a poblarlo con 2 funciones Z pro-
gramadas por nosotros, Z RFC READ PAYROLL ENTRIES, y Z RFC READ PAYROLL
RESULTS. La primera de estas funciones actua como wrapper RFC para las funcio-
nes PYXX GET RELID FROM PERNR y CU READ RGDIR, mientras que la segunda
actua como wrapper para PYXX READ PAYROLL RESULT. El codigo fuente de estas
funciones esta disponible en el anexo.

41
Utilizando estas dos funciones, somos capaces de extraer los datos salariales de cual-
quier empleado usando solo el numero asociado a el, con lo cual ya somos capaces de
extraer clusters desde nuestro ERP.

Figura 16: IDE integrado en SE80. Editor de modulos de funcion.

4.2.2.3. Estructuras organizativas

Este tipo de estructuras de datos estan tambien guardadas como tablas en SAP. Sin
embargo, estan guardados de una forma que dificulta bastante la reconstruccion de los
arboles organizativos que querremos visualizar mediante el modulo de presentacion. Estos
arboles se pueden visualizar mediante la transaccion PPSM.
Ahora bien, que es un arbol organizativo? Un arbol organizativo muestra por lo
general una estructura jerarquica dentro de la empresa. Puede ser una estructura de
los distintos departamentos, de los centros de compra, de las empresas asociadas a una
corporacion, etc.
Existe una funcion que, tomando como argumento la ID del objeto de negocio que
queremos como nodo raz de este arbol, y el tipo de objeto que es, procesa las tablas
en las que esta guardada la informacion necesaria y las devuelve en un formato mas
apropiado para reconstruir el arbol. Esta funcion se llama RH STRUC GET, y sobre ella
esta fundamentada la transaccion PPSM. De nuevo, contamos con el mismo problema

42
Figura 17: Ejemplo de arbol de estructura organizativa.

que antes, esta funcion es incapaz de transmitir datos mediante RFC. Y, de nuevo, la
solucion pasa por crear otra funcion wrapper que internamente la llame y que s tenga
RFC activado. Hemos llamado a esta funcion Z RFC HR READ ORG STRUCTURE, y
hemos hecho que resida en el mismo grupo de funciones que creamos para el apartado
anterior, Z RFC HR. El codigo fuente de esta funcion estara tambien detallado en el
anexo.

4.2.3. Vista general del modulo


Con esto hemos detallado la infraestructura necesaria para exponer nuestros datos a
nuestra cuenta en HCP. Como ya estuvimos adelantando anteriormente, este modulo es
extremadamente poco intrusivo para el ERP. Los unicos cambios que han hecho falta para
que funcione han sido la importacion del grupo de funciones /BODS/, y la creacion de 3
funciones Z adicionales. Todo lo demas ha sido externo. A partir de aqu, ya tenemos lo
necesario para empezar a operar con nuestros datos en la nube. El siguiente modulo del que
hablaremos, el modulo de integracion, se dedicara a hacer lo que en Business Intelligence
se denomina como ETL, de Extraccion, Transformacion, y Carga. Es decir, recogera los
datos que le lleguen en formato nativo, los transformara, y los cargara mediante queries
SQL generadas dinamicamente en la base de datos de HANA.

43
Figura 18: Diagrama de flujo de datos del modulo onPremise.

4.3. Modulo de integracion onCloud


En esta seccion pasaremos a detallar el modulo de integracion, que corre sobre la
maquina virtual de Java includa en HCP. Hablaremos sobre las tecnologas principales
empleadas para hacer posible el flujo de integracion y como interactuan entre s, dejando
la comparacion con otros posibles metodos de integracion para el siguiente apartado.
La funcion del modulo de integracion consiste en responder a las peticiones del modulo
de presentacion (es decir, la aplicacion web en s) de subir las tablas y estructuras de datos
especificadas desde nuestro ERP hacia nuestra cuenta en HCP, transformando los datos
de forma que sean compatibles con esta.
Como ya vimos en la figura 11, el modulo de integracion va sobre la maquina virtual de
Java en HCP, y se compone de un Servlet en el que va la logica de negocio y dos objetos de
acceso a datos: HanaDAO y AbapDAO. A medida que vayamos explicando los modulos
iremos comentando en que consisten estos conceptos. De momento como introduccion
breve, diremos que un servlet es un bloque de codigo Java que va sobre una aplicacion
servidor, y que se ejecuta cuando se intenta acceder a la URL asociada a el, mientras que
un objeto de acceso a datos, en este caso, es un objeto que implementa una interfaz entre
la logica de negocio ejecutada en Java y las bases de datos a las que estamos intentando

44
acceder, como HANA o el ERP.

4.3.1. ControllerServlet
Ya en la introduccion adelantabamos la primera pregunta que tenemos que hacernos:
que es un Servlet?
Un servlet es una clase Java utilizada para extender las capacidades de un servidor.
Aunque un servlet puede responder a varios tipos de peticiones, son frecuentemente uti-
lizados para extender aplicaciones en servidores web, con lo que se los puede considerar
como applets que corren en servidores, en vez de en los navegadores de internet. Estos
tipos de servlet existen como contrapartida a otras tecnologas de Web dinamicas como
PHP o ASP.NET.
La URL asociada al servlet depende de varios factores. La primera parte de la URL,
el dominio, depende de la direccion IP del servidor sobre el que nuestro servlet resida, o el
nombre que se le haya dado por los servidores DNS. La segunda parte, el path, depende
del programador del servlet. Un servlet generalmente se genera mediante un proyecto de
web dinamico, en Eclipse. Este proyecto consiste en una serie de directorios para guardar
codigo fuente Java, o Javascript, o el archivo war compilado, entre otros.
Entre esta serie de directorios se encuentra uno, al que se conoce como WebContent/WEB-
INF. Este directorio tiene la propiedad especial de que cualquier recurso guardado en el no
es directamente accesible desde el exterior, lo cual lo hace popular para guardar imagenes,
libreras especficas para el proyecto, archivos de base de datos, y cualquier otro documen-
to que no queramos que el usuario pueda descargar directamente. Pero mas alla de eso,
tambien tenemos un archivo generado automaticamente, conocido como web.xml. Este
archivo se encarga de mapear el path de la URL a la clase responsable de manejar las
peticiones que le lleguen a este path. Es decir, mediante este archivo podemos exponer
nuestro codigo al exterior. Ademas de eso podemos definir otra serie de cosas como filtros,
recursos, y demas.
Nuestra clase Servlet se compone de un metodo de inicializacion, en el que iniciali-
zamos los objetos hanaDAO y abapDAO, el contexto inicial, y la fuente de datos que
manipularemos (HANA, en este caso).
Y luego tenemos un metodo al que llamamos (al que tenemos que llamar) doPost. Este
metodo se ejecuta cuando llamamos a la URL definida en WEB-INF mediante el metodo
POST de HTTP. Dado que solo tenemos este metodo definido, esto quiere decir que otros
metodos de llamada, como GET o DELETE, no tendran ningun efecto. En los anexos se
podra encontrar el codigo fuente de WEB-INF.xml y ControllerServlet.java, en el que se
puede uno apoyar para ver los conceptos explicados en mas detalle.
Por que solo empleamos el metodo POST? Debido a la funcion de nuestro modulo.
El metodo POST sirve para pedir que un servidor web acepte los datos que esten en el
cuerpo del mensaje y los guarde [9], que es en esencia la tarea del modulo de integracion.

45
Sin embargo, tenemos que responder a varios tipos de peticiones distintas, en funcion
del tipo de datos que queramos subir. Como hacemos esto? Mediante la inclusion de
parametros en la URL, en la forma application/x-www-form-urlencoded. Recordemos
del apartado anterior que tenamos 3 tipos de datos que necesitabamos subir: tablas,
clusters, y estructuras organizacionales, para lo cual necesitabamos llamar a 4 funciones
distintas.
Para que el servlet sepa a que funcion tiene que llamar en que momento, hacemos que el
servlet capture los parametros mandados a el codificados en la URL, entre los cuales tiene
que estar uno conocido como functionname. Este parametro, como su propio nombre
indica, contiene el nombre de la funcion a la que el servlet tiene que llamar en el ERP.
Mediante este parametro, hacemos que el Servlet llame a abapDAO, que es el que se
encarga de interactuar con el ERP, saque los datos correspondientes, los transforme, y
los traspase a hanaDAO. AbapDAO se encarga de extraer los datos en formato nativo, y
HanaDAO se encarga de transformarlos en statements SQL que la base de datos pueda
entender. Hablaremos de esto en mas detalle en los siguientes apartados.
Una ultima cosa que comentar es la siguiente. El modulo de integracion y el modulo de
presentacion (la aplicacion web) estan en dominios distintos. Esto quiere decir que, debido
a la poltica de mismo origen, una poltica de seguridad implementada por todos los
navegadores modernos, no podemos servir la aplicacion web y los datos necesarios desde
dos dominios distintos, pues el navegador lo bloqueara. Vamos a tener que burlar esta
poltica de alguna forma. Hablaremos en mas detalle de esto cuando tratemos la aplicacion
web, por el momento solo diremos que hay varias formas de conseguir esto. Una de ellas,
y la recomendada por SAP, es la tecnica de proxy inverso, mediante la cual cambiamos
mediante un componente intermedio la URL del destino al que queramos conectarnos, de
tal forma que el navegador vea el mismo dominio en ambas partes de la comunicacion y no
aborte la operacion. Tenemos la ventaja, al utilizar este metodo, de que solo necesitamos
controlar el lado cliente para hacerlo funcionar, pero el inconveniente de que puede ser
complicado ponerse a programarlo. Es por ello que hemos utilizado CORS.
CORS (Cross-Origin Resource Sharing) es un protocolo desarrollado por la W3C me-
diante el cual el navegador es capaz de ignorar la poltica de mismo origen. Este metodo
tiene el inconveniente de que necesitamos controlar ambos puntos de la comunicacion, el
cliente y el servidor, pero tiene la ventaja de que es mucho mas sencillo de implementar.
Unicamente tenemos que anadir un header a las respuesteas de nuestro modulo, que es:
Access-Control-Allow-Origin: *. Tambien tenemos que anadir un parametro en las
llamadas al modulo de integracion desde la aplicacion web, pero eso lo veremos en la
seccion correspondiente.

4.3.2. AbapDAO
Antes de empezar a comentar el funcionamiento de este objeto, tenemos que saber
que es exactamente un Objeto de Acceso a Datos (Data Access Object en ingles). Se

46
define un DAO como un objeto que proporciona de una interfaz abstracta hacia algun
tipo de Base de Datos u otro mecanismo de persistencia de datos. Al mapear las llamadas
a los metodos de este objeto a distintas operaciones en la capa de aplicacion, el DAO
proporciona algunas operaciones de datos especficas sin exponer los detalles de la base
de datos a ControllerServlet. Este aislamiento implica que si necesitasemos cambiar de
base de datos, solo tendramos que modificar el DAO asociado a ella, dejando el resto
intacto. Mediante esta separacion de codigo, se obedece a una de las buenas practicas de
programacion orientada a objetos, el principio de responsabilidad unica, que establece
que cada objeto que compone un programa ha de cumplir una funcion especfica, y todos
los metodos y servicios asociados a el deberan estar relacionados con esa funcion.
AbapDAO, como ya hemos adelantado anteriormente, es una clase a la que se llama
desde ControllerServlet cuando se quieren extraer datos de nuestro ERP. Ahora bien,
ya vimos en el apartado anterior que nuestro ERP solo entiende ABAP, mientras que
nuestro programa esta codificado en Java. Es decir, tenemos que ejecutar codigo ABAP
desde Java. Como conseguimos hacer esto?
Existe una clase Java capaz de ejecutar codigo ABAP en una destinacion remota,
conocida como Java Connector, o JCo.
Para poder llegar al ERP, desde la maquina virtual de Java, recuerdese que en el
apartado anterior mencionamos que mapeamos el end-point HTTP real mediante el que
nos conectamos al ERP por uno virtual que exponemos a la nube, que en este caso hemos
llamado abapserver.hana.cloud:sapgw42. Ahora, tenemos que crear un destino JCo
que coja este mapping virtual y le de un nombre en el espacio nominal de destinos de
HCP, un protocolo de conexion (en este caso RFC) y una serie de parametros distintos
mediante los cuales HCP pueda conectarse al otro endpoint del tunel creado por Cloud
Connector. En este caso le damos el nombre JCoDemoSystem.
Con este destino creado, la variable JCoDemoSystem es inicializada automaticamente
en el espacio Java, con lo cual podemos acceder al destino y ejecutar statements JCo sobre
el, que el ERP entiende como llamadas a funcion mediante RFC. De esa forma, cuando le
llega la orden desde ControllerServlet de ejecutar una funcion ABAP en remoto, utiliza
JCo para importar la lista de parametros que hay que pasarle, transforma variables Java
a parametros JCo, ejecuta la funcion, y devuelve las tablas requeridas en una estructura
a la que hemos llamado AbapData, que consiste de:

Un array de strings donde se guarda el nombre de las columnas de la tabla

Un array de strings donde se guardan los tipos de datos de cada columna

Un array bidimensional donde se guardan las lneas de datos

Hay 4 metodos asociados a esta clase aparte de su constructor, que son:

getTable

47
Figura 19: Servicio de destinos de HANA Cloud Platform.

getPayrollHistory

getPayrollResult

getOrganizationalStructure

Como se puede observar, cada uno de estos metodos se corresponde con las funciones
en el ERP que describimos en la seccion anterior, de tal forma que ControllerServlet solo
tiene que llamar a estos metodos para recibir los datos, encargandose AbapDAO de los
detalles especficos de la conexion entre ambos.

4.3.3. HanaDAO
HanaDAO es un objeto de acceso a datos como AbapDAO, pero en vez de interactuar
con el ERP, interactua con HANA. Para ello, utiliza Java Database Connectivity, mas
conocido como JDBC.
JDBC es una tecnologa de conexion de datos desarrollada y soportada por Oracle.
Es una API para Java que define como un cliente puede acceder a una base de datos.
Provee metodos para escribir datos en una BBDD. Esta orientada hacia bases de datos
relacionales.

48
En nuestro caso, usamos JDBC para crear y poblar tablas cuya estructura y tipos de
datos son iguales (o, al menos, lo mas parecidos posible) a nuestras tablas y clusters en
el ERP. Para ello, como suceda con JCo, JDBC actua a modo de interfaz entre nuestro
espacio Java y la base de datos, que entiende SQL, como ya vinimos adelantando en el
captulo anterior.
Los metodos que posee HanaDAO (aparte del metodo constructor y otros de baja
relevancia para nuestros propositos) son los siguientes:

checkTable

createTable

existsTable

addRow

typeMapping

Por partes:

createTable se encarga de crear una tabla mediante el comando CREATE TABLE, to-
mando como parametros el nombre de la tabla (suplido por el propio ControllerSer-
vlet), el nombre de las columnas y sus tipos de datos (entregados por AbapDAO,
utilizando la estructura de datos descrita en el apartado anterior). Para transfor-
mar los tipos de datos propios de ABAP a los tipos de datos propios de HANA
empleamos el metodo typeMapping.

existsTable se encarga de comprobar si una tabla existe y devolver verdadero o


falso segun exista o no.

checkTable es un wrapper para los dos metodos anteriores. Su comportamiento es


el siguiente: si la tabla no existe, la crea. Si existe, no hace nada.

addRow se encarga de anadir una fila a nuestras tablas tomando como parametros
el nombre de la tabla, el nombre de las columnas y sus tipos de datos, y la fila que
queramos subir, en formato String[]. Para ello, usa la instruccion SQL INSERT INTO.
Dado que solo sube una fila (como su propio nombre indica) es utilizada muchas
veces dentro de un bucle for, para poder subir varias filas a la vez.

De nuevo, ControllerServlet solo ve los metodos servidos por HanaDAO, con lo que no
necesitamos crear statements SQL desde all. Si cambia la especificacion de HANA, por
tanto, solo tendramos que modificar HanaDAO.
JDBC es una solucion de bajo nivel, comparada con otras como EJB o Hibernate,
que abstraen la necesidad de usar SQL. Sin embargo, es necesario hacerlo con SQL, dado

49
que a diferencia de otras aplicaciones web donde conocemos las estructuras de las tablas
que queremos manipular (como por ejemplo una tabla en la que se guarde el email, la
contrasena y el nombre de usuario de todas las personas que se vayan registrando), aqu,
por la propia naturaleza del proyecto, hemos de operar contando con que es imposible
conocer de antemano las estructuras de todas las tablas que posee el ERP, lo cual hace
imposible trabajar con ORM (Object-Relational Mapping, que transforma objetos Java
en Entidades de Bases de datos directamente).

4.3.4. Vista general del Modulo


Con esto ya hemos dejado explicado como hemos sido capaces de transportar nuestros
datos del ERP a HANA. Un par de cosas mas que comentar antes de seguir con el modulo
de presentacion son las siguientes.
La primera tiene que ver con el mantenimiento del codigo. Hemos empleado git para
llevar a cabo el seguimiento de la historia del codigo, ya que HCP tiene disponibles varios
repositorios. Si bien estos repositorios estan limitados en su uso (no se pueden borrar, no
se pueden subir archivos por otra persona que no sea el titular de la cuenta de HCP), han
sido suficientes para nuestros propositos.
La segunda tiene que ver con el patron de diseno empleado. Como se puede observar,
hemos dejado separadas la parte del codigo encargada de ejecutar la logica de negocio
(ControllerServlet) y las partes encargadas de tratar con las bases de datos (AbapDAO,
HanaDAO). Estas partes se pueden interpretar como el Controlador del programa y los
Modelos (vease la seccion 2.2.1.4). No hay vision, dado que este modulo no esta pensado
para servir al usuario final. De eso se encargara el modulo de presentacion. Va a ser
interesante tener en cuenta a partir de ahora el patron de diseno MVC, pues, si bien
aqu lo hemos introducido de manera parcial, en el siguiente apartado, la estructura de la
aplicacion web disenada va a ser altamente compleja, pero siempre va a obedecer a este
mismo patron de diseno, lo cual nos puede servir para no perdernos demasiado.

50
Figura 20: Diagrama de proceso del modulo de integracion.

51
4.4. Modulo de presentacion onCloud: Aplicacion Web
Este modulo es, de todos ellos, el mas complejo, debido a varios motivos.
El primero y mas fundamental es la estructura de paquetes de HANA XS, en el que
reside nuestra aplicacion. Debido a la complejidad de la estructura de permisos y privi-
legios de HANA y a la cantidad de parametros que hay que definir para que el sistema
reconozca los archivos que hay que mandar y como hay que mandarlos, dedicaremos la
primera parte de este apartado a hablar de estos temas.
El segundo es debido a la complejidad de la pagina web desarrollada en s. Tenemos
4 pantallas de presentacion distintas (tablas, historial de nomina, resultados de nomina y
arboles de estructura organizativa) y un shell comun a todas ellas para poder navegar de
una a otra, lo cual hace 5 pares de Vista-Controlador, o 10 archivos de Javascript de lado
cliente. Ademas de eso esta el modelo, en el lado servidor, que se encarga de transformar
los datos de formato SQL a JSON y mandarlos al navegador.
Es decir, tenemos 11 archivos distintos de codigo fuente, sin contar con la parte an-
terior. Aun as, dado que estamos hablando del diseno de una pagina web, algo bastante
mas cercano al da a da que un ERP o un flujo de integracion de datos, la comprension
de lo que hace cada fichero en este apartado no debera ser demasiado complicada.

4.4.1. Archivos de proyecto HANA XS


HANA XS tiene una estructura de paquetes. Esto es, todos los proyectos que se quieran
hacer sobre el, necesitan ir dentro de un paquete. Un paquete en la estructura de HA-
NA XS es como un directorio en un ordenador corriente, puede contener tanto archivos
como otros paquetes. Ademas, la estructura de paquetes modifica la URL necesaria para
acceder a los archivos necesarios para cargar la pagina web. Por ejemplo, nuestro proyec-
to se halla en la ruta p1940528821trial/myhanaxs/jcodata, por tanto, para acceder a
nuestra pagina web, la URL necesaria es https://s3hanaxs.hanatrial.ondemand.com/
p1940528821trial/myhanaxs/jcodata/.
Dentro de jcodata (el paquete raz de nuestro proyecto) hemos de poner varios archivos
para que el sistema lo reconozca como una aplicacion web y podamos acceder a el.
El primero de ellos es un archivo de texto vaco llamado .xsapp, mediante el cual, el
sistema HANA XS reconoce jcodata como una aplicacion web. Siempre que este archivo
este en un paquete, HANA XS sabra que todos los archivos Javascript (.js) que esten
dentro del mismo paquete habran de ser servidos hacia el navegador web.
Tambien tenemos que declarar otro archivo de texto, al que llamaremos .xsaccess.
En este archivo, en formato JSON, declararemos dos propiedades necesarias para po-
der acceder desde fuera a nuestro paquete. La primera de ellas es exposed:true, sin
la cual no podramos acceder al paquete desde el exterior. La segunda de ellas es de-
fault file:index.html, que nos permite acceder al archivo html que necesitamos para
poder ver la pagina web sin tener que teclearlo en la URL. El texto completo de este

52
archivo se puede ver en el anexo.
El tercero de ellos es .xsprivileges. En el simplemente declaramos un perfil de privilegios
al que hemos denominado Basic. Sin embargo, si lo dejamos as es un perfil vaco, ya
que no lo hemos asignado a ningun usuario ni le hemos concedido ningun privilegio al
perfil.
Tal y como estan las cosas, si intentasemos escribir a la base de datos fallaramos.
Tenemos que contactar con la seccion de autenticacion de HANA, para lo cual tenemos
que emplear SQL.
El primer paso consiste en concederle privilegios al perfil Basic que tenemos declara-
do. Para ello, tenemos que crear otro archivo de texto, al que llamamos model access.hdbrole.
En este archivo, creamos un rol, al que hemos llamado model access. A este rol le asig-
namos el perfil de privilegios Basic. Y declaramos que el perfil Basic tiene potestad
para leer, actualizar, escribir, y borrar datos en el schema en el que estamos guardando
nuestras tablas, que esta asociado a nuestro modulo de integracion (el nombre del schema
es NEO 2YRCM3I3BM65X2EPAL42IELVB).
Sin embargo no hemos terminado. Hemos creado un perfil de privilegios, le hemos
asociado algunos privilegios y lo hemos asignado a un rol de usuario. Pero todava no
hemos asignado el rol de usuario a ningun usuario.
Para poder hacerlo, tenemos que hablar con el servicio de autenticacion de HANA,
para lo cual sera necesario utilizar SQL. Para asignarlo, nos tenemos que conectar desde
nuestro entorno de desarrollo a nuestra cuenta en HANA, tras lo cual tenemos que abrir
una consola SQL. Y, dentro de esta consola SQL, tenemos que asignar el rol creado a
nuestro usuario en HCP (cuyo nombre es P1940528821) mediante el comando:
CALL "HCP"."HCP_GRANT_ROLE_TO_USER"
(p1940528821trial.myhanaxs.jcodata::model_access,p1940528821)
Una vez hecho esto, ya s tenemos cumplidos nuestros objetivos. HANA XS reconoce
a este paquete como un proyecto Web SAPUI5, se puede acceder a este paquete desde el
exterior, y podemos editar nuestro schema, en el que guardamos nuestras tablas, a traves
de el.
Ahora lo que tenemos que ir haciendo es posicionar nuestro codigo fuente de tal for-
ma que se sirva una pagina web completamente funcional cuando nos conectemos a este
paquete. Empezaremos por el primer archivo que tenemos que darle al navegador, in-
dex.html.

4.4.2. Lado cliente: HTML, CSS


El primer archivo que tenemos que declarar en cualquier pagina web es el archivo
HTML. En el, por lo general, suele ir el texto de la pagina web, el camino a las imagenes
que se han de mostrar, los hipervnculos hacia las demas secciones de la pagina, etc.
En este caso sin embargo, nuestro archivo HTML unicamente sirve para incluir las
libreras Javascript que vamos a necesitar para poder servir nuestros pares VC hacia

53
el navegador, en los cuales esta toda la informacion necesaria para desplegar la pagina
web con la que el usuario va a interactuar. Apoyandonos en el codigo fuente incluido en
el anexo, vemos que las primeras lneas de la cabecera del documento se dedican a la
codificacion de los caracteres y al modo de compatibilidad de la web, los cuales no son
muy relevantes, y a la inclusion de un archivo CSS, del cual hablaremos mas adelante.
Despues de eso, nos dedicamos a incluir libreras Javascript includas en el propio
paquete, como jsPDF o FileSaver, que nos serviran mas adelante para generar un pdf con
la nomina de los trabajadores.
Por ultimo, inclumos SAPUI5. SAPUI5 es un framework de Javascript desarrollado
por SAP, que proporciona una serie de assets disenados especficamente para crear aplica-
ciones web de negocios. Al estar desarrollado por SAP, ofrece un gran nivel de integracion
con sus soluciones anteriores, lo cual nos facilita bastante la tarea a la hora de programar
esta aplicacion. Ademas de incluir la librera, especificamos un estilo de pagina (bluecrys-
tal), y una serie de libreras internas (como commons, mediante la cual se accede a
los assets de UI5 para equipos de escritorio, o viz, que nos permite mostrar graficas a
partir de nuestros datos).
El siguiente script nos sirve para definir los directorios en los que estan nuestros pares
VC, declara un modelo JSON y lo asocia al nucleo de la aplicacion web para darles a
las variables que hay dentro de el visibilidad global (aunque hemos de destacar que este
modelo no se corresponde con el modelo tpico en el patron de diseno MVC, ya que no
actua de interfaz con ninguna base de datos. Simplemente es un objeto JSON en el que
podemos guardar variables globales, lo cual nos sirve para transferir informacion entre
pares distintos de VC).
Por ultimo cogemos el shell que hemos codificado en mainView y lo ponemos en
content, que es, como se puede ver mas abajo, el cuerpo de nuestro HTML.
Con esto ya hemos descrito la funcionalidad principal de nuestro archivo HTML, con
la excepcion del archivo CSS. CSS son las siglas de Custom StyleSheet, y en este caso lo
estamos utilizando para personalizar algunos assets que SAP nos proporciona en SAPUI5.
Por poner un ejemplo, si queremos poner un fondo blanco sobre una lnea de texto,
SAPUI5 no nos deja hacerlo por defecto. Si queremos anadir margenes entre distintos
elementos en un horizontalLayout, UI5 no nos deja tampoco hacerlo por defecto. Lo que
s nos deja hacer UI5, es inyectar estilos CSS mediante el metodo .addStyleClass().
En el archivo CSS que hemos creado tenemos definidos dos estilos, myWhiteCellStyle y
myTextFieldMargin, cuyos efectos son los que hemos expuesto como ejemplos mas arriba:
dar un background blanco al texto y anadir margenes.
Con esto hemos explicado la funcion de los archivos HTML y CSS de nuestra aplicacion
web. En el siguiente apartado empezaremos a explicar el primero de los pares VC que
hemos anadido en el cuerpo de nuestra aplicacion: mainView.shell.

54
4.4.3. Lado cliente: Pares VC SAPUI5
Antes de meternos a comentar la funcionalidad y el funcionamiento de los pares VC,
queremos comentar un par de cosas.
La primera de ellas es que es altamente recomendable apoyarse en el SDK de SAPUI5
[17] mientras se va leyendo este texto, ya que posee bastante informacion sobre los distintos
controles de SAPUI5 y como funcionan. La segunda es comentar que, cada par VC se
corresponde con un grupo de elementos en pantalla, el shell, o las distintas pestanas y
funcionalidades de nuestra aplicacion. Solo hay un modelo, ya que esta aplicacion solo
interactua con una base de datos (HANA). Hay varias maneras en SAPUI5 de declarar
la vista, se recomienda utilizar XML (ya que, al ser un lenguaje de marcas, no se puede
implementar ninguna clase de logica mediante el, lo cual refuerza la division entre Vista
y Controlador), pero nosotros hemos preferido utiilizar Javascript, ya que as podemos
incluir una pequena parte de la logica de proceso del programa en la vista. Esto, a pesar
de no ser una buena practica, facilita y acorta bastante la programacion del codigo fuente.
Sin mas dilacion, empezamos:

4.4.3.1. Shell
Este es el par VC que hemos incluido en index.html en el cuerpo de la pagina web. Un
shell es un frame de aplicacion, que se muestra siempre en la parte superior de la pantalla,
y que sirve para navegar entre las distintas zonas de la pagina web.
Nuestro shell se compone de un ttulo, en el que aparece el nombre de la aplicacion
web, y 3 botones, que son:
Volcado de tablas
Ficha salarial de empleado
Arbol de estructura organizativa
La vista se encarga de declarar la variable shell, junto al ttulo, y a los 3 botones, aparte
de concretar cosas como el estilo del shell, eliminar elementos que vienen por defecto y
que son innecesarios, y demas. Pero si solo tuviesemos la vista, al pulsar cualquiera de
estos 3 botones no pasara nada. Reiteramos: la vista se encarga de definir que es lo que
sale por pantalla, y nada mas (aunque se relajara un poco esta distincion en los siguientes
apartados). Para poder darle funcionalidad a nuestros botones, necesitamos asignarles una
ID, mediante la cual podamos declarar desde el controlador variables que capturen los
elementos con estas IDs especficas, de tal forma que podamos manipular estos elementos
desde un archivo de codigo fuente distinto.
Una vez hecho esto, armamos en la vista el evento worksetItemSelected para que
le pase el control al controlador (valga la redundancia), el cual se encargara de cambiar
entre pares VC, y establecemos por defecto que aparezca el par VC asociado a la parte
de volcado de tablas.

55
4.4.3.2. Volcado de tablas
La parte de volcado de tablas de nuestra aplicacion es la primera que aparece, por defecto.
Los componentes a la vista que estan por debajo del Shell son: un control de tabla vaco
en cuyo toolbar aparece un textView que nos pide el nombre de una tabla para intentar
mostrarla o subirla, al lado de un textField en el que podemos escribir el nombre de la
tabla y un boton, que esta desactivado si el textField esta vaco. En el cuerpo de la tabla
aparece un texto pidiendo al usuario que introduzca una tabla en el textField.

Figura 21: Aplicacion web: Volcado de tablas

Cuando el usuario teclea el nombre de una tabla del ERP en el textField y le pulsa
al boton de busqueda, el controlador empieza a ejecutar la siguiente rutina, mediante
llamadas AJAX hacia el modelo de HANA y hacia el Servlet del modulo de integracion.

1. Mandamos una peticion hacia el modelo HANA, en la que indicamos que queremos
que se nos sirva la tabla cuyo nombre hemos introducido en el campo de texto en
formato JSON.

2. Si el modelo encuentra la tabla y nos la sirve, cogemos los datos y los metadatos
y, mediante factory functions, los asignamos al control de tabla vaco que nos
aparece en pantalla, atando los datos a el, de tal forma que nos lo muestra por
pantalla (o nos muestra una alerta indicando que la tabla estaba vaca, en caso de

56
que no tenga datos), terminando aqu la rutina. Si no la encuentra y recibimos una
respuesta de error, pasamos al siguiente apartado.
3. Mandamos una peticion hacia el modulo de integracion, diciendole que queremos
que nos suba la tabla hacia nuestra base de datos en la nube.
4. Si no funciona y recibimos una respuesta de error (por no tener Cloud Connector
encendido y funcionando, por ejemplo), mostramos una alerta diciendo que no hemos
sido capaces de subir la tabla a HANA y terminamos con la rutina. Si funciona,
volvemos al primer paso.

Figura 22: Aplicacion web: Historial salarial de un Empleado

Mediante esta rutina, nos aseguramos de que si tenemos la tabla en nuestra base
de datos, no volvemos a volcarla, si no que simplemente la mostramos por pantalla. Si
no la tenemos, la volcamos primero y luego la mostramos por pantalla. Con esta rutina
por tanto, conseguimos darle a nuestro usuario una interfaz para interactuar con toda
la maquinaria que hemos descrito en los apartados anteriores para poder subir tablas,
sin que este necesite conocer los detalles de la programacion en ABAP o en Java de los
modulos implementados. Esta misma rutina es la que se implementa en todos los otros
controladores de esta aplicacion web para obtener datos, con la unica diferencia de que el
tipo de datos que piden es distinto cada vez. Por tanto, no repetiremos el algoritmo que
siguen los controladores para obtener datos en los siguientes apartados.

57
Figura 23: Aplicacion web: Detalle salarial de un Empleado

4.4.3.3. Ficha de empleado: Historial Salarial


La vista inicial asociada a esta pestana es practicamente igual que la anterior: tenemos un
control de tabla, en cuyo toolbar hay un texto, un campo en el que introducir un numero
de empleado, y un boton de busqueda. En la tabla vaca, aparece de nuevo otro texto
pidiendonos que introduzcamos el numero de algun empleado.
El funcionamiento de esta pestana es muy similar al de la pestana anterior, con la
diferencia de que esta pestana sirve para subir los datos de cluster de un historial salarial
y la anterior serva para subir tablas. La rutina de subida de datos es exactamente la
misma, con la diferencia de que el modelo no nos va a devolver los datos del ERP en s, si
no un subconjunto de los datos del ERP unidos a ciertos datos contenidos en la tabla de
recursos humanos PA0001, los cuales vamos a necesitar para poder rellenar ciertos datos
en el siguiente apartado. Veremos con detalle la query reaizada cuando veamos el modelo
XSJS.
Una vez subidos los datos, nos aparece una tabla detallando los distintos salarios
mensuales que ha recibido el empleado. Al clicar en una de sus entradas entramos en la
siguiente vista de la ficha de empleado:

4.4.3.4. Ficha de empleado: Calculo de salario

58
Una vez entramos en esta vista, podemos observar varios detalles de la nomina del
empleado que hemos decidido observar, como su salario bruto, salario neto, su contribucion
al IRPF, la division de la compana en la que se encuentra, una proyeccion de su salario
en 5 anos (para lo cual utilizamos la librera sap.viz), etc.
Al fondo de la pagina aparecen dos botones. El primero sirve para generar y descargar
la nomina en formato PDF. Dado que la nomina es generada mediante la librera jsPDF,
no nos hace falta descargar ningun dato al generarla, el navegador simplemente lee los
datos de la pestana ya cargada y genera el PDF. El segundo sirve para volver a la pestana
anterior, en caso de que se quiera ver y generar la nomina de otro periodo o de otro
trabajador.
Cabe destacar que la query SQL generada por el controlador en este apartado no es
suficiente para poblar todos los datos de esta pagina, sino que tambien tenemos que pasarle
datos obtenidos en la vista anterior, para lo cual utilizabamos las variables declaradas en
el modelo global declarado en index.html. Para ello, recordemos que habamos asociado
este modelo con el nucleo de la aplicacion. Por tanto, solo tenemos que utilizar los metodos
encadenados sap.ui.getCore().getModel() para acceder a estas variables.

4.4.3.5. Estructura Organizativa


Por ultimo, hablaremos de la presentacion de la estructura organizativa. Esta parte funcio-
na igual que las anteriores, con la diferencia de que al tener que presentar una estructura
de arbol, el control con el que tenemos que trabajar, Tree, no posee una toolbar. Para
poder pasarle datos, tenemos que declarar un MatrixLayout, que es una manera de di-
vidir la pantalla en filas y columnas. Dentro de este MatrixLayout, definimos lo que se
conoce como LayoutCells, en las que vamos introduciendo nuestros controles. Definimos,
por tanto, dos filas dentro de este layout, una con 3 columnas (3 celulas, donde ponemos
el texto, el campo de introduccion de texto, y el boton), y otra con una columna, donde
introducimos el control Tree. Ademas, inyectamos CSS para anadir un margen entre las
columnas de la primera fila (para que el texto, el campo de introduccion de texto y el
boton no aparezcan demasiado juntos) y para hacer aque el background de la primera fila
sea blanco, de tal forma que se parezca lo mas posible a las otras dos pestanas.

4.4.4. Lado servidor: Modelo XS


Del lado servidor tenemos el modelo. Hay dos maneras de sacar datos desde HANA
XS, una de ellas es mediante un archivo conocido como .xsodata.
.xsodata es un archivo capaz de exponer las tablas contenidas en HANA en formato
OData, simplemente dandole el nombre y el schema en el que se encuentran, en tiempo
de desarrollo. Es, de lejos, la forma mas sencilla y practica de exponer nuestros datos al
exterior, pues no requiere programar y expone las tablas con las que queremos interactuar
en formato OData, un estandar de la industria, con toda la funcionalidad que eso conlleva.

59
Figura 24: Aplicacion web: Arbol Estructura organizativa

Sin embargo, el lector astuto ya habra podido averiguar por que no podemos usar esta
manera de exponer nuestras tablas en nuestra aplicacion.
El problema es el siguiente, .xsodata requiere que se le diga de antemano que tablas
hay que exponer, mediante la modificacion de su codigo fuente. El problema es que esta
aplicacion nos ha de servir para subir y exponer absolutamente todas las tablas que
tenemos en nuestro ERP, con lo cual nos es imposible dar por sabido de antemano que
tablas va a querer subir y exponer el cliente. Ademas, por motivos de seguridad, no
podemos hacer que .xsodata vaya monitorizando y exponiendo un schema entero, sino
que tenemos que especificarle que es lo que queremos exponer a nivel de tabla.
Esto hace completamente imposible trabajar con .xsodata para nuestra aplicacion, con
lo cual tendremos que trabajar de otra manera.
La solucion pasa por utilizar el interprete de XSJS que va incluido en HANA pa-
ra exponer nuestros datos. Como ya comentamos anteriormente, XSJS es derivado de
Scriptmonkey, el interprete Javascript de Firefox. Tanto Scriptmonkey como XSJS son
interpretes ligeros, con funcionalidad muy limitada, pero suficiente para nuestros proposi-
tos.
El modelo programado solo respondera ante el metodo GET, ya que solo queremos
leer datos mediante el, no escribirlos. El modelo recibe el nombre de la tabla con la que
se quiere trabajar, ejecuta (con una excepcion, que veremos mas adelante) un comando

60
SELECT * FROM <Schema>.<Tabla> y devuelve el resultado en formato JSON, que manda hacia
la aplicacion que genero la peticion.
La excepcion se encuentra en las peticiones generadas por la pestana de Historial
Salarial en nuestra aplicacion web. En este caso, dado que nuestro modulo de integracion
sube las tablas y los clusters de datos tal y como vienen en el ERP, y necesitamos los
datos de dos elementos distintos para generar dicha pagina (el cluster en s y los datos de
empleado contenidos en la tabla de RRHH PA0001), no hacemos un simple SELECT, sino
que ademas hacemos un INNER JOIN con PA0001, teniendo como campo de union entre
ambas tablas el numero de empleado.

4.4.5. Vista general del modulo


Con este ultimo elemento queda explicado el modulo de presentacion, y con el el
proyecto tecnico en s. Hemos podido ver las idiosincrasias y la estructura de archivos
necesaria para poder trabajar con HANA XS como servidor web, los pares VC que co-
monen la aplicacion web de lado cliente, y el modelo mediante el cual el navegador puede
interactuar con la base de datos.

Figura 25: Diagrama de flujo de datos del modulo de presentacion.

Como se puede observar, la estructura del codigo de este apartado es bastante compleja
comparada con las anteriores, en parte debido a seguir con bastante fidelidad el patron de

61
diseno MVC. Sin embargo, esta estructura nos da la ventaja de tener un diseno altamente
modular, en el que se pueden detectar y corregir rapidamente los fallos de programacion
que se hayan cometido, y que es altamente escalable. Si tuviesemos que implementar una
nueva pestana, solo tendramos que modificar el Shell y crear un nuievo par VC, en vez
de tener que encontrar el lugar adecuado para declarar nuestras variables en un codigo
fuente de 2000 lneas.
A continuacion escribiremos algunas conclusiones, incluyendo posibles lneas de inves-
tigacion futuras, comparaciones con otros metodos de integracion, etc.

62
5. Comparativa con otras soluciones en el mercado.
Hemos visto, en este proyecto, una manera de integrar datos entre soluciones onCloud
y onPremise. Pero no es, ni mucho menos, la unica. Veremos a continuacion otras maneras
de integrar, y las compararemos con esta, detallando sus pros y sus contras, con la idea de
que el lector pueda hacerse una idea de donde se situa este proyecto dentro del creciente
mercado de integracion de datos en Cloud Computing, y que alternativas existen.

5.1. Netweaver Gateway


Netweaver Gateway es, a da de hoy, el estandar de SAP a la hora de exponer datos
del ERP a la nube. No es, a diferencia de otros ejemplos aqu, un middleware dedicado al
proposito especfico de integracion de datos, sino mas bien una manera de exponer datos
usando el protocolo OData. Es compatible con la mayora de los productos (aunque no
todos) que vamos a listar a continuacion.
Mediante Netweaver Gateway, se pueden crear servicios OData utilizando la transac-
cion SEGW, que utiliza las BAPIs GET ENTITYSET y GET ENTITY para capturar los
metadatos y los datos (respectivamente) de las tablas que queramos incluir en el servicio
y las expone en un endpoint web.
Un landscape que contenga Netweaver Gateway generalmente tendra un front-end y
un back-end. El back-end por definicion ha de estar en el back-end, pero el front-end puede
estar tambien integrado en el ERP, o puede no estarlo. Si decidimos poner el front-end
en el mismo ERP, el principal inconveniente es que, al no estar el ERP originalmente
disenado para tales tareas, el rendimiento del servicio no sera el mismo que si el back-end
y el front-end estuvieran separados. Por el contrario, si el back-end y el front-end estan
en servidores distintos, el rendimiento del ERP no sufrira tanto, pero a costa de tener que
dedicar dos servidores en vez de uno.
HCP tiene un servicio conocido como HCI OData Provisioning, capaz de actuar de
servidor front-end en la nube, lo cual es una gran opcion para alguien que ya tenga HCP
en productivo.
La principal ventaja de utilizar Netweaver Gateway es que SAP esta claramente po-
tenciando su uso, y que ademas esta basado en estandares tan utilizados como OData,
con lo que se puede contratar a alguien que no tenga conocimientos funcionales del ERP
para tratar con el, siempre que tenga habilidades de desarrollo Web y de servicios REST.
El inconveniente es que necesita de una instalacion que puede seer mas o menos costosa en
caso de que no se tenga el ERP en su version actual, ya que se necesitan addons adiciona-
les en caso de que la version de Netweaver sobre la que este corriendo el ERP sea anterior
a la 7.4. Ademas, construir un servicio en SEGW es mas complicado que simplemente
pasarle el nombre de una tabla a una aplicacion (como hacemos nosotros para integrar).
Es recomendable usarlo siempre que se tenga una version igual o mas reciente que 7.4,

63
en caso de que sea menor, habra que estudiar el coste de instalar los addons necesarios
frente a utilizar otras opciones.

5.2. HANA Cloud Integration


HANA Cloud Integration es, actualmente, el middleware de integracion mas potencia-
do por SAP en estos momentos, y uno de sus productos mas estrategicos. Esta compuesto
por una VPN en la nube en la cual los servidores de SAP pueden realizar tareas de ETL
entre dos endpoints OData o SOAP, entre otros.
Si bien es verdad que se puede implementar un flujo de integracion interno entre
dos endpoints mediante llamadas AJAX sin demasiada dificultad, HCI presenta varias
ventajas. Entre ellas esta la seguridad que proporciona establecer una comunicacion en
una VPN, el hecho de que, al guardarse las transacciones en un log, se pueden recuperar
en caso de emergencia, y el hecho de que no hace falta saber AJAX ni ningun otro tipo de
tecnica de programacion web para poder establecer flujos de integracion, pues la interfaz
disponible con HCI abstrae ese tipo de aspectos tecnicos.
Esto hace que HCI sea un producto muy potente a la hora de realizar una integracion,
pero tambien tiene sus inconvenientes. Principalmente son dos, el primero es que no es un
producto gratuito (lleva un coste fijo al mes utilizarlo), y el segundo es que no esta hoy
en da demasiado implantado en el mercado. Esto hace que sea bastante difcil obtener
acceso a el. Sin embargo, hay documentos tecnicos recientes de SAP que fuerzan a utilizar
HCI (o PI, que veremos mas adelante) para integrar con ciertos servicios en la nube, lo
cual nos hace pensar que dentro de unos anos es posible que se convierta en un estandar.

5.3. Dell Boomi


Dell Boomi es otra VPN dedicada a tareas de ETL, al igual que HCI. Hay varias
diferencias sin embargo.
La primera y mas crucial es que, hoy en da, es el estandar de middlewares de inte-
gracion en la nube. Es uno de los productos mas utilizados en el mercado y, hoy por hoy,
de los que mas se utilizan a la hora de implementar flujos de integracion (suponiendo que
no se tenga algo como PI). Ademas, no requiere de un endpoint OData para integrar con
la nube, sino que utiliza conectores (parecidos a Cloud Connector) para comunicarse via
RFC o BAPI con las soluciones onPremise que queramos exponer. Es compatible con la
mayora de productos de SAP, ademas de otras soluciones de companas distintas.
La principal desventaja de Boomi es el coste. Actualmente, en su version mas barata,
cuesta 2000$ al mes utilizarlo. Ademas, es una opcion que apenas ha salido del mercado
americano.

64
5.4. SAP PI
SAP PI es, a diferencia de las soluciones anteriores, un middleware onPremise capaz
de exponer datos a la nube. Dado que PI exista antes de que SAP empezase a potenciar
el Cloud Computing como un nuevo estandar (estableciendo flujos de integracion entre
distintas soluciones onPremise), hoy en da es una de las soluciones mas implementadas
y utilizadas para integrar entre distintas soluciones.
Hoy en da es lo mas utilizado, hasta el punto de que SAP esta regalando licencias de
HCI a quien ya tenga licencias de PI con el objetivo de seguir fomentando la integracion
a la nube. Esto a nos esta explicando la principal ventaja de PI, que es el hecho de
que, al ser una solucion mas madura que las anteriores, la poseen la mayora de los
clientes. Sin embargo, operar con PI es mucho mas complejo que operar con HCI, haciendo
falta administradores de sistemas y programadores Java para mantener los servidores e
implementar flujos de integracion con ellos.

5.5. SAP Landscape Transformation


SAP Landscape Transformation es una de las soluciones mas novedosas para copiar
datos de un ERP a SAP HANA. Es el estandar de integracion en productos como HANA
One, y es completamente gratuito utilizarlo.
Depende de SAP LT Replication Server para funcionar, una aplicacion servidor inte-
grada en HANA Studio, lo cual lo hace actualmente una de las opciones mas interesantes
a la hora de replicar tablas a la nube. Sin embargo, solo funciona con HANA, MaxDB y
demas bases de datos, y no esta pensado para ser utilizado, por ejemplo, con Concur o
Ariba, con lo que su alcance es mas limitado que el de otras soluciones listadas aqu.

65
6. Conclusiones
Pasaremos a detallar aqu algunas de las conclusiones obtenidas con respecto a los
objetivos establecidos en el primer captulo, y como los hemos cubierto.
Recordemos que una parte de los objetivos contenidos en esta memoria consistan en
introducir el modelo de negocio de SAP a un lego en la materia y explicar el cambio
tan radical de estrategia que haban tenido en estos ultimos anos, debido a la natura-
leza disruptiva del Cloud Computing, sin meternos en tanto detalle como para que esta
parte ocupase la mayor parte de esta memoria. Si bien esto nos ha forzado a dejarnos
algunos detalles interesantes, consideramos que los dos primeros captulos son una buena
introduccion para, al menos, hacerse una idea general de la posicion actual en la que se
encuentra el ecosistema SAP. En ultima instancia, sin embargo, es el lector el que ha de
juzgar si este es, o no, el caso.
Con respecto al proyecto tecnico en s. Podemos observar que hace exactamente lo
que dice que hace, es decir, mueve tablas y estructuras de datos de un ERP a una cuenta
de HCP. No hay elemento subjetivo ah. Una posible crtica que se le podra hacer es en
terminos de escalabilidad, es decir, como reaccionara la aplicacion creada si en vez de
tener que subir tablas de pocos MB tuviesemos que subir tablas de varios GB?
Probablemente tendramos que hacer algunos cambios, sobre todo a la hora de instalar
Cloud Connector en una maquina distinta al ERP, pues si no, tendramos tablas de varios
GB dando saltos por nuestra red local. Tambien podra hacerse la crtica de que JCo
no es, ni mucho menos, un estandar, y que la manera en que se han hecho las cosas es
tan poco ortodoxa que sera difcil encontrar a alguien capaz de mantener y expandir la
funcionalidad de la aplicacion creada desde cero.
Todo esto son crticas validas. Sin embargo, hemos de darnos cuenta de que, en ultima
instancia, un ingeniero tiene que desarrollar, primero acorde con las circunstancias de la
situacion actual, y despues segun lo que podra suceder en un futuro. Hemos tenido que
operar con tres restricciones severas, que son:
La aplicacion realizada no puede depender de ninguna herramienta que sea de pago.
Es decir, el coste de la infraestructura ha de ser cero.
Los cambios que se le tengan que hacer al ERP han de ser lo menos invasivos posible,
pues, en este caso, es una herramienta esencial para muchos otros proyectos actual-
mente en curso. Una parada de varios das para realizar instalaciones complejas
podra ser catastrofica.
Nuestro ERP en concreto no esta del todo actualizado, con lo que muchas de las
opciones mas potentes que tenemos para poder integrar estan restringidas para
nosotros.
A partir de estas tres restricciones, se puede ver el por que de las decisiones de diseno
que hemos tomado. Teniendo en cuenta esto, y el hecho de que estamos desarrollando

66
para una empresa pequena, se puede ver por que hemos instalado, por ejemplo, Cloud
Connector fuera del servidor de nuestro ERP, o por que hemos decidido usar JCo + Cloud
Connector en vez de NetWeaver Gateway + Dell Boomi, por poner un ejemplo. Ninguna
solucion es perfecta, es cuestion de ver cual es la mas adecuada (o, en ciertos casos, cual
es la unica posible de implementar) para cada situacion. Para nuestras circunstancias,
la solucion implementada es perfectamente funcional, pero hemos de insistir en que no
existe una panacea (que es por lo que hemos intentado dar una comparativa de soluciones
distintas en el captulo anterior, y bajo que condiciones su uso sera favorable).
En el creciente mercado de las soluciones de integracion y migracion de datos en
particular, y en el mundo del desarrollo de software en general, hay que tener siempre
muy en cuenta las restricciones con las que hay que operar y los aspectos de desarrollo
que no sean tan crticos. Saber distinguir cuales son las herramientas mas adecuadas para
cada caso es una de las habilidades mas importantes que un ingeniero, especialmente si se
va a dedicar en un futuro a la gestion de proyectos, puede adquirir. Esta es, bajo el humilde
punto de vista del escritor de este texto, quiza la leccion mas importante aprendida a lo
largo de la elaboracion del proyecto aqu descrito.

67
Referencias
[1] APICS. SCM (supply chain management). http://www.apics.org/dictionary/
dictionary-information?ID=4202, 2013. [Online; visitado el 20 de octubre de
2014].

[2] Cimdata. About PLM. http://www.cimdata.com/plm.html, 2012. [Online; visitado


el 20 de octubre de 2014].

[3] CIO Leadership. Maximising the Value of Supplier Relationships. http:


//www.vantagepartners.com/researchandpublications/viewPublications.
aspx?id=3150, 2009. [Online, visitado el 20 de octubre de 2014].

[4] Reuven Cohen. The Art Of Reinvention: SAP Puts Its Weight
Behind A Better, Faster And Stronger Cloud Computing Stra-
tegy. http://www.forbes.com/sites/reuvencohen/2013/05/09/
the-art-of-reinvention-sap-puts-its-weight-behind-a-better-faster-and-stronger-cl
2013. [Online; visitado el 27 de octubre de 2014].

[5] Michael Doane. The New SAP Blue Book. Performance Monitor Press, 2006.

[6] Bob Evans. SAP HANA and the Death of the Lunch Break. http://www.forbes.
com/sites/sap/2011/08/17/sap-hana-and-the-death-of-the-lunch-break/,
2011. [Online; visitado el 22 de octubre de 2014].

[7] Bob Evans. SAP HANA and the Game-Changing Po-


wer of Speed. http://www.forbes.com/sites/sap/2011/11/21/
sap-hana-and-the-game-changing-power-of-speed/, 2011. [Online; visita-
do el 22 de octubre de 2014].

[8] Gartner, Inc. CRM Sales Magic Quadrant. http://www.gartner.com/document/


2798021, 2014. [Online, visitado el 20 de octubre de 2014].

[9] Internet Engineering Task Force. RFC 7231. http://tools.ietf.org/html/


rfc7231#section-4.3.3, 2014. [Online; visitado el 3 de noviembre de 2014].

[10] SAP SE. A 42-year history of innovation. http://www.sap.com/corporate-en/


about/our-company/history/index.html, 2014. [Online; visitado el 20 de octubre
de 2014].

[11] SAP SE. SAP HANA Cloud Connector. https://help.hana.ondemand.com/help/


frameset.htm?e6c7616abb5710148cfcf3e75d96d596.html, 2014. [Online; visitado
el 27 de octubre de 2014].

68
[12] SAP SE. SAP HANA One Petabyte Performance White Pa-
per. http://www.saphana.com/community/blogs/blog/2012/11/12/
the-sap-hana-one-petabyte-test, 2014. [Online; visitado el 24 de octubre
de 2014].

[13] SAP SE. SAP HANA Predictive Analysis Library (PAL) Reference. http://help.
sap.com/hana/SAP_HANA_Predictive_Analysis_Library_PAL_en.pdf, 2014. [On-
line; visitado el 23 de octubre de 2014].

[14] SAP SE. SAP HANA R Integration Guide. http://help.sap.com/hana/sap_hana_


r_integration_guide_en.pdf, 2014. [Online; visitado el 23 de octubre de 2014].

[15] SAP SE. SAP HANA SQL and System Views Reference. http://help.sap.com/
hana/sap_hana_sql_and_system_views_reference_en.pdf, 2014. [Online; visita-
do el 22 de octubre de 2014].

[16] SAP SE. SAP HANA SQLScript Reference. http://help.sap.com/hana/sap_


hana_sql_script_reference_en.pdf, 2014. [Online; visitado el 23 de octubre de
2014].

[17] SAP SE. SAPUI5 SDK. https://sapui5.netweaver.ondemand.com/sdk/, 2014.


[Online; visitado el 6 de noviembre de 2014].

[18] SAP SE. SAP Corporate Fact Sheet. http://www.sap.com/corporate-en/


factsheet, 2014. [Online; visitado el 19 de octubre de 2014].

[19] Robert Shaw. Computer Aided Marketing and Selling. Butterworth Heinemann,
1991.

[20] Kay Somers. SAP HANA Core Architecture. http://en.community.dell.com/


techcenter/b/techcenter/archive/2012/09/28/sap-hana-core-architecture,
2012. [Online; visitado el 22 de octubre de 2014].

[21] TechTarget. ERP (enterprise resource planning). http://searchsap.techtarget.


com/definition/ERP, 2014. [Online; visitado el 20 de octubre de 2014].

[22] Verdi Ogewell. Great ERP, worse PLM What SAP PLM needs to sharpen
its competitive edge. http://www.engineering.com/PLMERP/ArticleID/8529/
Great-ERP-worse-PLM-What-SAP-PLM-needs-to-sharpen-its-competitive-edge.
aspx, 2014. [Online; visitado el 20 de octubre de 2014].

[23] Jeffrey Word. SAP HANA Essentials. Epistemy Press LLC, 2013.

69
Apendices
A. Codigo fuente del modulo onPremise: ABAP.
Detallamos aqu el codigo fuente de las funciones Z desarrolladas en el ERP.

A.1. Z RFC HR READ PAYROLL ENTRIES


1 FUNCTION Z _ R F C _ H R _ R E A D _ P A Y R O L L _ E N T R I E S .
2 *"--------------------------------------------------------------------
3 * "*" Interfase local
4 * " IMPORTING
5 *" VALUE ( INT_PERNR ) TYPE PERSNO
6 * " EXPORTING
7 *" VALUE ( INT_RELID ) TYPE RELID
8 * " TABLES
9 *" IT_RGDIR STRUCTURE PC261
10 *"--------------------------------------------------------------------
11 DATA : " it_rgdir TYPE STANDARD TABLE OF pc261 INITIAL SIZE 0 ,
12 int_molga TYPE molga .
13
14 CALL FUNCTION P Y X X _ G E T _ R E L I D _ F R O M _ P E R N R
15 EXPORTING
16 employee = int_pernr
17 IMPORTING
18 relid = int_relid
19 molga = int_molga .
20
21 CALL FUNCTION CU_READ_RGDIR
22 EXPORTING
23 persnr = int_pernr
24 IMPORTING
25 molga = int_molga
26 TABLES
27 in_rgdir = it_rgdir .
28
29 ENDFUNCTION .

70
A.2. Z RFC HR READ PAYROLL RESULTS
1 FUNCTION Z _ R F C _ H R _ R E A D _ P A Y R O L L _ R E S U L T S .
2 *"--------------------------------------------------------------------
3 * "*" Interfase local
4 * " IMPORTING
5 *" VALUE ( INT_RELID ) TYPE RELID
6 *" VALUE ( INT_SEQNR ) TYPE PC261 - SEQNR
7 *" VALUE ( INT_PERNR ) TYPE PC200 - PERNR
8 * " EXPORTING
9 *" VALUE ( ST_RESULT ) TYPE PAYES_RESULT
10 *"--------------------------------------------------------------------
11
12 CALL FUNCTION P Y X X _ R E A D _ P A Y R O L L _ R E S U L T
13 EXPORTING
14 clusterid = int_relid
15 employeenumber = int_pernr
16 sequencenumber = int_seqnr
17 CHANGING
18 payroll_result = st_result
19 EXCEPTIONS
20 I L L E G A L _ I S O C O D E _ O R _ C L U S T E R I D = 1 " Cluster ID Or ISO Code Not in
Table T500L
21 E R R O R _ G E N E R A T I N G _ I M P O R T = 2 " Error When Generating the Import
Routine
22 IMPORT_MISMATCH_ERROR = 3 " Error On Importing : Different
Structures
23 SUBPOOL_DIR_FULL = 4 " More Than 36 Temp . Routines Were
Generated
24 NO_R EAD_A UTHORI TY = 5 " No Reading Authorization for Cluster
25 NO_RECORD_FOUND = 6 " No Payroll Result Found
26 VERSIONS_DO_NOT_MATCH = 7 " Version Indicators Do Not Correspond
27 ERROR_READING_ARCHIVE = 8 " Error When Reading Archives
28 E RR O R_R E A DI N G _ RE L I D = 9. " Error When Reading Cluster ID
29
30 ENDFUNCTION .

71
A.3. Z RFC HR READ ORG STRUCTURE
1 FUNCTION z _ r f c _ h r _ r e a d _ o r g _ s t r u c t u r e .
2 *"----------------------------------------------------------------------
3 * "*" Interfase local
4 * " IMPORTING
5 *" VALUE ( INT_OTYPE ) TYPE OBJEC - OTYPE
6 *" VALUE ( INT_OBJID ) TYPE OBJEKTID
7 *" VALUE ( INT_WEGID ) TYPE GDSTR - WEGID
8 * " EXPORTING
9 *" VALUE ( INT_PLVAR ) TYPE OBJEC - PLVAR
10 * " TABLES
11 *" INT_TAB STRUCTURE SWHACTOR
12 *" INT_OBJEC STRUCTURE OBJEC
13 *" INT_STRUC STRUCTURE STRUC
14 *"----------------------------------------------------------------------
15 CALL FUNCTION RH_STRUC_GET
16 EXPORTING
17 act_otype = int_otype
18 act_objid = int_objid
19 act_wegid = int_wegid
20 IMPORTING
21 act_plvar = int_plvar
22 TABLES
23 result_tab = int_tab
24 result_objec = int_objec
25 result_struc = int_struc
26 EXCEPTIONS
27 no_plvar_found = 1
28 no_entry_found = 2
29 OTHERS = 3.
30 .
31 IF sy - subrc <> 0.
32 WRITE : Error , sy - subrc .
33 * WITH SY - MSGV1 SY - MSGV2 SY - MSGV3 SY - MSGV4 .
34 ENDIF .
35
36
37 ENDFUNCTION .

72
B. Codigo fuente del modulo de integracion: Java.
Pasamos a listar el codigo fuente del modulo de integracion:

B.1. ControllerServlet.java
1 package com . sap . transfer . jco ;
2
3 import java . io . IOException ;
4 import java . io . PrintWriter ;
5 import java . sql . SQLException ;
6 import java . util . Arrays ;
7
8 import javax . naming . InitialContext ;
9 import javax . naming . NamingException ;
10 import javax . servlet . ServletException ;
11 import javax . servlet . http . HttpServlet ;
12 import javax . servlet . http . Htt pS er vl et Re qu es t ;
13 import javax . servlet . http . H tt p S er v l et R e sp o n se ;
14 import javax . sql . DataSource ;
15
16 import com . sap . conn . jco . AbapException ;
17 import com . sap . conn . jco . JCoException ;
18
19 /* *
20 * Application which makes use of / BODS / RFC_READ_TABLE2 to upload a
table whose name is passed by x - www - form - urlencoded
21 * from an SAP ERP back - end to a HANA cloud account , preserving the
structure .
22 *
23 * Note : The JCo APIs are available under < code > com . sap . conn . jco </ code >.
24 */
25 public class Co ntroll erServ let extends HttpServlet
26 {
27 private static final long serialVersionUID = 1 L ;
28 private String destinationName = " JCoDemoSystem " ;
29 // private String functionName = "/ BODS / RFC_READ_TABLE2 ";
30 private boolean tableNotExist ;
31 private TransferView transferView ;
32 private HanaDAO hanaDAO ;
33 private AbapDAO abapDAO ;
34
35 /* * { @inheritDoc } */
36 @Override
37 public void init () throws ServletException {
38 try {
39 InitialContext ctx = new InitialContext () ;

73
40 DataSource ds = ( DataSource ) ctx . lookup ( " java : comp / env / jdbc /
DefaultDB " ) ;
41
42 hanaDAO = new HanaDAO ( ds ) ;
43 abapDAO = new AbapDAO ( destinationName ) ;
44 } catch ( SQLException e ) {
45 throw new ServletException ( e ) ;
46 } catch ( NamingException e ) {
47 throw new ServletException ( e ) ;
48 } catch ( JCoException e ) {
49 throw new ServletException ( e ) ;
50 }
51 }
52
53
54 protected void doPost ( Ht tp Se rv le tR eq ue st request ,
55 H ttp S er v l et R e sp o n s e response ) throws ServletException , IOException
{
56 PrintWriter responseWriter = response . getWriter () ;
57 transferView = new TransferView ( responseWriter ) ;
58 String functionName = request . getParameter ( " functionname " ) ;
59 try {
60 if ( functionName . equals ( " / BODS / RFC_READ_TABLE2 " ) ) {
61 // TABLE UPLOADER CODE
62 AbapData tableData ;
63 String tableName = request . getParameter ( " tablename " ) ;
64
65 response . setStatus ( H t t pS e r vl e t Re s p on s e . SC_CREATED ) ;
66 tableData = abapDAO . getTable ( tableName ) ;
67
68 tableNotExist = hanaDAO . checkTable ( tableName , tableData .
fieldType , tableData . fieldName ) ;
69
70 if ( tableNotExist )
71 for ( int i = 0; i < tableData . tableEntries . length ; i ++)
72 hanaDAO . addRow ( tableName , tableData . tableEntries [ i ] ,
tableData . fieldType , tableData . fieldName ) ;
73
74 } else if ( functionName . equals ( " Z _ R F C _ H R _ R E A D _ P A Y R O L L _ E N T R I E S " ) ) {
75 // PAYROLL HISTORY UPLOADER CODE
76 String employeeNumber = request . getParameter ( " employeenumber " ) ;
77 String tableName = " PH " + employeeNumber ;
78
79 AbapData payrollHistory = abapDAO . getP ayroll Entrie s ( Integer .
parseInt ( employeeNumber ) ) ;
80
81 // fieldName = payrollHistory [0];
82 // fieldType = payrollHistory [1];

74
83 // tableEntries = Arrays . copyOfRange ( payrollHistory , 2 ,
payrollHistory . length ) ;
84
85 // Anadimos PERNR para poder hacer un join
86 payrollHistory . fieldName = Arrays . copyOf ( payrollHistory .
fieldName , payrollHistory . fieldName . length +1) ;
87 payrollHistory . fieldType = Arrays . copyOf ( payrollHistory .
fieldType , payrollHistory . fieldType . length +1) ;
88
89
90 payrollHistory . fieldName [ payrollHistory . fieldName . length - 1] =
" PERNR " ;
91 payrollHistory . fieldType [ payrollHistory . fieldType . length - 1] =
"I";
92 for ( int j = 0; j < payrollHistory . tableEntries . length ; j ++) {
93 payrollHistory . tableEntries [ j ] = Arrays . copyOf ( payrollHistory .
tableEntries [ j ] , payrollHistory . tableEntries [ j ]. length + 1)
;
94 payrollHistory . tableEntries [ j ][ payrollHistory . tableEntries [ j ].
length - 1] = employeeNumber ;
95 }
96
97 tableNotExist = hanaDAO . checkTable ( tableName , payrollHistory .
fieldType , payrollHistory . fieldName ) ;
98
99 if ( tableNotExist )
100 for ( int i = 0; i < payrollHistory . tableEntries . length ; i ++)
101 hanaDAO . addRow ( tableName , payrollHistory . tableEntries [ i ] ,
payrollHistory . fieldType , payrollHistory . fieldName ) ;
102
103 response . setStatus ( H t t pS e r vl e t Re s p on s e . SC_CREATED ) ;
104
105 } else if ( functionName . equals ( " Z _ R F C _ H R _ R E A D _ P A Y R O L L _ R E S U L T S " ) ) {
106 // PAYROLL RESULTS UPLOADER CODE
107 String employeeNumber = request . getParameter ( " employeenumber " ) ;
108 String sequenceNumber = request . getParameter ( " sequencenumber " ) ;
109 String relationId = request . getParameter ( " relationid " ) ;
110
111 String tableName = " PH " + employeeNumber + " _PE " +
sequenceNumber ;
112
113 AbapData payrollResults = abapDAO . getP ayroll Result s (
employeeNumber , sequenceNumber , relationId ) ;
114 // for ( int i = 0; i < payrollResults . length ; i ++)
115 // responseWriter . println (" <p >" + payrollResults [ i ] + " </p >") ;
116
117 tableNotExist = hanaDAO . checkTable ( tableName , payrollResults .
fieldType , payrollResults . fieldName ) ;
118

75
119 if ( tableNotExist )
120 for ( int i = 0; i < payrollResults . tableEntries . length ; i ++)
121 hanaDAO . addRow ( tableName , payrollResults . tableEntries [ i ] ,
payrollResults . fieldType , payrollResults . fieldName ) ;
122
123 response . setStatus ( H t t pS e r vl e t Re s p on s e . SC_CREATED ) ;
124
125 } else if ( functionName . equals ( " Z _ R F C _ H R _ R E A D _ O R G _ S T R U C T U R E " ) ) {
126 // STRUCTURE UPLOADER CODE
127 String objectType = request . getParameter ( " objecttype " ) ;
128 String objectId = request . getParameter ( " objectid " ) ;
129 String evaluationPath = request . getParameter ( " evaluationpath " ) ;
130
131 String tableName = " ST " + objectType + " _ID " + objectId ;
132 AbapData orgTable = abapDAO . g e t O r g a n i z a t i o n a l S t r u c t u r e (
objectType , objectId , evaluationPath ) ;
133
134 tableNotExist = hanaDAO . checkTable ( tableName , orgTable . fieldType
, orgTable . fieldName ) ;
135
136 if ( tableNotExist )
137 for ( int i = 0; i < orgTable . tableEntries . length ; i ++)
138 hanaDAO . addRow ( tableName , orgTable . tableEntries [ i ] , orgTable
. fieldType , orgTable . fieldName ) ;
139
140 response . setStatus ( H t t pS e r vl e t Re s p on s e . SC_CREATED ) ;
141 }
142
143 } catch ( AbapException e ) {
144 transferView . exceptionWriter (e , destinationName ) ;
145 response . setStatus ( H t t pS e r vl e t Re s p on s e . S C _ I N T E R N A L _ S E R V E R _ E R R O R ) ;
146 } catch ( JCoException e ) {
147 transferView . exceptionWriter (e , destinationName ) ;
148 response . setStatus ( H t t pS e r vl e t Re s p on s e . S C _ I N T E R N A L _ S E R V E R _ E R R O R ) ;
149 } catch ( SQLException e ) {
150 transferView . exceptionWriter (e , destinationName ) ;
151 response . setStatus ( H t t pS e r vl e t Re s p on s e . S C _ I N T E R N A L _ S E R V E R _ E R R O R ) ;
152 }

76
B.2. AbapDAO.java
1 package com . sap . transfer . jco ;
2
3 import java . sql . SQLException ;
4 import java . util . ArrayList ;
5 import java . util . Arrays ;
6 import java . util . Collections ;
7 import java . util . List ;
8
9 import com . sap . conn . jco . AbapException ;
10 import com . sap . conn . jco . JCoDestination ;
11 import com . sap . conn . jco . J C o D e s t i n a t i o n M a n a g e r ;
12 import com . sap . conn . jco . JCoException ;
13 import com . sap . conn . jco . JCoFunction ;
14 import com . sap . conn . jco . JCoParameterList ;
15 import com . sap . conn . jco . JCoRepository ;
16 import com . sap . conn . jco . JCoStructure ;
17 import com . sap . conn . jco . JCoTable ;
18
19 /* *
20 * @author Juan Jose Perez
21 *
22 * The following class contains some methods which help abstract away
the intricacies
23 * of the ETL process to our main Servlet
24 *
25 */
26
27 public class AbapDAO {
28
29 String wa ;
30 final String destinationName ;
31 final JCoDestination destination ;
32 final JCoRepository repo ;
33
34 public AbapDAO ( String destName ) throws JCoException {
35 destinationName = destName ;
36
37 // access the RFC Destination " JCoDemoSystem "
38 destination = J C o D e s t i n a t i o n M a n a g e r . getDestination (
destinationName ) ;
39 repo = destination . getRepository () ;
40 }
41
42 /* *
43 * A method which uses / BODS / RFC_READ_TABLE2 and JCo to upload an
ABAP table in native format
44 * @param tableName

77
45 * @return AbapData
46 * @throws AbapException
47 * @throws SQLException
48 * @throws JCoException
49 */
50
51 public AbapData getTable ( String tableName )
52 throws AbapException , SQLException , JCoException {
53
54 AbapData tableData = new AbapData () ;
55 // make an invocation of R F C _ G E T _ T A B L E _ E N T R I E S in the
backend ;
56
57 JCoFunction rfcReadTable = repo . getFunction ( " / BODS /
RFC_READ_TABLE2 " ) ;
58
59 // Execute the local function and retrieve the data
60 JCoParameterList imports = rfcReadTable .
g e t I m p o r t P a r a m e t e r L i s t () ;
61 JCoParameterList exports = rfcReadTable .
g e t E x p o r t P a r a m e t e r L i s t () ;
62 JCoParameterList tables = rfcReadTable . g e t T a b l e P a r a m e t e r L i s t
() ; // Se corresponde con TABLES en la llamada a funcion
ABAP .
63 imports . setValue ( " QUERY_TABLE " , tableName ) ;
64 imports . setValue ( " DELIMITER " ," ; " ) ;
65
66
67 rfcReadTable . execute ( destination ) ;
68 JCoTable fields = tables . getTable ( " FIELDS " ) ;
69 JCoTable data = tables . getTable (( String ) exports . getValue ( "
OUT_TABLE " ) ) ;
70 tableData . fieldType = new String [ fields . getNumRows () ];
71 tableData . fieldName = new String [ fields . getNumRows () ];
72 tableData . tableEntries = new String [ data . getNumRows () ][];
73
74 for ( int i = 0; i < data . getNumRows () ; i ++) {
75 data . setRow ( i ) ;
76 tableData . tableEntries [ i ] = data . getString ( " WA " ) . split ( " ; "
);
77 }
78
79
80
81 for ( int i = 0; i < fields . getNumRows () ; i ++) {
82 fields . setRow ( i ) ;
83 // responseWriter . print (( i == 0 ? "" : ";") + fields .
getString (" FIELDNAME ") ) ;

78
84 tableData . fieldName [ i ] = " \" " + fields . getString ( "
FIELDNAME " ) + " \" " ;
85
86 }
87
88
89 for ( int i = 0; i < fields . getNumRows () ; i ++) {
90 fields . setRow ( i ) ;
91 // responseWriter . print (( i == 0 ? "" : ";") + fields .
getString (" TYPE ") ) ;
92 tableData . fieldType [ i ] = fields . getString ( " TYPE " ) ;
93 }
94
95 return tableData ;
96 }
97
98 /* *
99 * A method which uses Z _ R F C _ H R _ R E A D _ P A Y R O L L _ E N T R I E S and JCo to
upload the Payroll History of an employee
100 * @param employeeNumber
101 * @return AbapData
102 * @throws AbapException
103 * @throws SQLException
104 * @throws JCoException
105 */
106
107
108 public AbapData ge tPayro llEntr ies ( int employeeNumber )
109 throws AbapException , SQLException , JCoException {
110 JCoTable p a y ro l l En t r ie s T ab l e ;
111 AbapData payrollEntries = new AbapData () ;
112 String relationalId ;
113
114 JCoFunction r f c R e a d P a y r o l l E n t r i e s = repo . getFunction ( "
Z_RFC_HR_READ_PAYROLL_ENTRIES ");
115 JCoParameterList imports = r f c R e a d P a y r o l l E n t r i e s .
g e t I m p o r t P a r a m e t e r L i s t () ;
116 JCoParameterList exports = r f c R e a d P a y r o l l E n t r i e s .
g e t E x p o r t P a r a m e t e r L i s t () ;
117 JCoParameterList tables = r f c R e a d P a y r o l l E n t r i e s .
g e t T a b l e P a r a m e t e r L i s t () ;
118 imports . setValue ( " INT_PERNR " , employeeNumber ) ;
119 r f c R e a d P a y r o l l E n t r i e s . execute ( destination ) ;
120 p ay r o ll E n tr i e sT a b le = tables . getTable ( " IT_RGDIR " ) ;
121 relationalId = exports . getString ( " INT_RELID " ) ;
122 // payrollEntries = new String [2 + p ay r o ll E n tr i e sT a b le .
getNumRows () ][ p a y ro l l En t r ie s T ab l e . getNumColumns () + 1];
123 // Arrays . fill ( payrollEntries , "") ;
124

79
125 payrollEntries . fieldType = new String [ p ay r o ll E n tr i e sT a b le .
getNumColumns () + 1];
126 payrollEntries . fieldName = new String [ p ay r o ll E n tr i e sT a b le .
getNumColumns () + 1];
127 payrollEntries . tableEntries = new String [ p ay r o ll E n tr i e sT a b le .
getNumRows () ][ p a y ro l l En t r ie s T ab l e . getNumColumns () + 1];
128
129 for ( int i = 0; i < p a yr o l lE n t ri e s Ta b l e . getNumColumns () ; i ++) {
130 if (! Arrays . asList ( payrollEntries . fieldName ) . contains (
p ay r o ll E n tr i e s Ta b l e . g etReco rdMeta Data () . g etReco rdTyp eName ( i
))) {
131 payrollEntries . fieldName [ i ] = p ay r o ll E n tr i e sT a b le .
getR ecordM etaDa ta () . get Record TypeNa me ( i ) ;
132 payrollEntries . fieldType [ i ] = p ay r o ll E n tr i e sT a b le .
getR ecordM etaDa ta () . getTypeAsString ( i ) ;
133 }
134 }
135
136 payrollEntries . fieldName [ p ay r o ll E n tr i e sT a b le . getNumColumns () ] =
" RELID " ;
137 payrollEntries . fieldType [ p ay r o ll E n tr i e sT a b le . getNumColumns () ] =
" CHAR " ;
138
139 for ( int j = 0; j < p a yr o l lE n t ri e s Ta b l e . getNumRows () ; j ++) {
140 p a yr o l lE n t ri e s Ta b l e . setRow ( j ) ;
141 for ( int i = 0; i < p a yr o l lE n t ri e s Ta b l e . getNumColumns () ; i ++)
{
142 if ( payrollEntries . fieldName [ i ] != null )
143 payrollEntries . tableEntries [ j ][ i ] = p ay r o ll E n tr i e sT a b le .
getString ( i ) ;
144 }
145 payrollEntries . tableEntries [ j ][ p ay r o ll E n tr i e sT a b le .
getNumColumns () ] = relationalId ;
146 }
147
148 List < String > list = new ArrayList < String >( Arrays . asList (
payrollEntries . fieldName ) ) ;
149 list . removeAll ( Collections . singleton ( null ) ) ;
150 payrollEntries . fieldName = list . toArray ( new String [ list . size () ]) ;
151
152 list = new ArrayList < String >( Arrays . asList ( payrollEntries .
fieldType ) ) ;
153 list . removeAll ( Collections . singleton ( null ) ) ;
154 payrollEntries . fieldType = list . toArray ( new String [ list . size () ]) ;
155
156 for ( int j = 0; j < payrollEntries . tableEntries . length ; j ++) {
157 list = new ArrayList < String >( Arrays . asList ( payrollEntries .
tableEntries [ j ]) ) ;
158 list . removeAll ( Collections . singleton ( null ) ) ;

80
159 payrollEntries . tableEntries [ j ] = list . toArray ( new String [ list .
size () ]) ;
160 }
161
162 return payrollEntries ;
163 }
164
165 /* *
166 * A method which uses Z _ R F C _ H R _ R E A D _ P A Y R O L L _ R E S U L T S and JCo to
upload the results of the calculation of the Payroll of an
employee
167 * @param employeeNumber
168 * @param sequenceNumber
169 * @param relationId
170 * @return AbapData
171 * @throws JCoException
172 */
173
174 public AbapData ge tPayro llResu lts ( String employeeNumber , String
sequenceNumber , String relationId ) throws JCoException {
175 JCoStructure p a y r o l l R e s u l t s S t r u c t u r e ;
176 AbapData payrollResults = new AbapData () ;
177
178 JCoFunction r f c R e a d P a y r o l l R e s u l t s = repo . getFunction ( "
Z_RFC_HR_READ_PAYROLL_RESULTS ");
179 JCoParameterList imports = r f c R e a d P a y r o l l R e s u l t s .
g e t I m p o r t P a r a m e t e r L i s t () ;
180 JCoParameterList exports = r f c R e a d P a y r o l l R e s u l t s .
g e t E x p o r t P a r a m e t e r L i s t () ;
181
182 imports . setValue ( " INT_PERNR " , employeeNumber ) ;
183 imports . setValue ( " INT_SEQNR " , sequenceNumber ) ;
184 imports . setValue ( " INT_RELID " , relationId ) ;
185
186 r f c R e a d P a y r o l l R e s u l t s . execute ( destination ) ;
187
188 p a y r o l l R e s u l t s S t r u c t u r e = exports . getStructure ( " ST_RESULT " ) ;
189
190 JCoTable payrollCRT = p a y r o l l R e s u l t s S t r u c t u r e . getStructure ( " INTER " ) .
getTable ( " CRT " ) ;
191 payrollCRT . setRow (0) ;
192 String grossSalary ;
193 String wageType ;
194
195 payrollResults . fieldType = new String []{ " NUM " , " CHAR " };
196 payrollResults . fieldName = new String []{ " BETRG " , " LGART " };
197 payrollResults . tableEntries = new String [ payrollCRT . getNumRows ()
][2];
198

81
199 for ( int i = 0; i < payrollCRT . getNumRows () ; i ++) {
200 payrollCRT . setRow ( i ) ;
201 grossSalary = payrollCRT . getString ( " BETRG " ) ;
202 wageType = payrollCRT . getString ( " LGART " ) ;
203 payrollResults . tableEntries [ i ] = new String []{ grossSalary ,
wageType };
204 }
205
206 return payrollResults ;
207 }
208
209 /* *
210 * A method which uses Z _ R F C _ H R _ R E A D _ O R G _ S T R U C T U R E and JCo to upload
the organizational structure of a node
211 * @param objectType
212 * @param objectId
213 * @param evaluationPath
214 * @return AbapData
215 * @throws JCoException
216 */
217
218 public AbapData g e t O r g a n i z a t i o n a l S t r u c t u r e ( String objectType , String
objectId , String evaluationPath ) throws JCoException {
219 JCoTable orgStructure ;
220 JCoTable orgObjects ;
221
222 AbapData orgTable = new AbapData () ;
223
224 JCoFunction rf c R ea d O rg S t ru c t ur e = repo . getFunction ( "
Z_RFC_HR_READ_ORG_STRUCTURE ");
225
226 JCoParameterList imports = r fc R e ad O r gS t r uc t u re .
g e t I m p o r t P a r a m e t e r L i s t () ;
227 JCoParameterList tables = r fc R e ad O r gS t r uc t u re . g e t T a b l e P a r a m e t e r L i s t
() ;
228
229 imports . setValue ( " INT_OTYPE " , objectType ) ;
230 imports . setValue ( " INT_OBJID " , objectId ) ;
231 imports . setValue ( " INT_WEGID " , evaluationPath ) ;
232
233 r fc R e ad O r gS t r u ct u r e . execute ( destination ) ;
234
235 orgStructure = tables . getTable ( " INT_STRUC " ) ;
236 orgObjects = tables . getTable ( " INT_OBJEC " ) ;
237
238 orgTable . fieldType = new String []{ " C " , " I " , " I " , " C " , " C " , " I " , " C " ,
" I " , " I " , " I " , " I " };
239 orgTable . fieldName = new String []{ " STEXT " , " SEQNR " , " LEVEL " , " OTYPE "
, " OBJID " , " PDOWN " , " DFLAG " , " VCOUNT " , " PNEXT " , " PUP " , " PPREV " };

82
240 orgTable . tableEntries = new String [ orgStructure . getNumRows () ][
orgTable . fieldName . length ];
241
242 for ( int i = 0; i < orgStructure . getNumRows () ; i ++) {
243 orgObjects . setRow ( i ) ;
244 orgStructure . setRow ( i ) ;
245 orgTable . tableEntries [ i ][0] = orgObjects . getString ( orgTable .
fieldName [0]) ;
246 for ( int j = 1; j < orgTable . fieldName . length ; j ++)
247 orgTable . tableEntries [ i ][ j ] = orgStructure . getString ( orgTable .
fieldName [ j ]) ;
248 }
249
250 return orgTable ;
251 }
252 }

83
B.3. HanaDAO.java
1 package com . sap . transfer . jco ;
2
3 import java . sql . Connection ;
4 import java . sql . DatabaseMetaData ;
5 import java . sql . Pr epared Statem ent ;
6 import java . sql . ResultSet ;
7 import java . sql . SQLException ;
8
9 import javax . sql . DataSource ;
10
11 /* *
12 * @author Juan Jose Perez
13 * Data access object encapsulating some necessary JDBC operations for
any possible data row .
14 */
15 public class HanaDAO {
16
17 private enum AbapTypes
18 {
19 B ,S ,L ,C ,N ,P ,D ,T ,F ,X ,g ,y , I
20 }
21
22 private DataSource dataSource ;
23
24 /* *
25 * Create new data access object with data source .
26 */
27 public HanaDAO ( DataSource newDataSource ) throws SQLException {
28 setDataSource ( newDataSource ) ;
29 }
30
31 /* *
32 * Get data source which is used for the database operations .
33 */
34 public DataSource getDataSource () {
35 return dataSource ;
36 }
37
38 /* *
39 * Set data source to be used for the database operations .
40 */
41 public void setDataSource ( DataSource newDataSource ) throws
SQLException {
42 this . dataSource = newDataSource ;
43 }
44
45 /* *

84
46 * Add a row to the table .
47 */
48 public void addRow ( String tableName , String [] entries , String []
dataTypes , String [] dataColumns ) throws SQLException {
49 Connection connection = dataSource . getConnection () ;
50
51 try {
52 String statement ;
53 String typeCheck = " VARCHAR \\(\\ d +\\) | VARBINARY \\(\\ d +\\) | TIME
| DATE " ;
54
55 statement = " INSERT INTO " + tableName + " ( " ;
56
57
58 for ( int i = 0; i < entries . length ; i ++) {
59 if (! entries [ i ]. trim () . isEmpty () ) {
60 statement = statement + dataColumns [ i ] + " , " ;
61 }
62 }
63
64 statement = statement . substring (0 , statement . length () -2) + " )
VALUES ( " ; // Eliminamos la ultima coma y seguimos
escribiendo .
65
66 for ( int i = 0; i < entries . length ; i ++) {
67 if (! entries [ i ]. trim () . isEmpty () ) {
68 // Si es un String , quitamos espacios en blanco al
principio y al final y anadimos comillas . Si no lo es ,
eliminamos todos los caracteres que no sean numericos o
puntos y no anadimos comillas .
69 statement = statement +
70 ( typeMapping ( dataTypes [ i ]) . matches ( typeCheck ) ? " " : " " ) + ( typeMapping
( dataTypes [ i ]) . matches ( typeCheck ) ? entries [ i ]. trim () : entries [ i ].
trim () . replaceAll ( " [^\\ d .] " , " " ) ) +
71 ( typeMapping ( dataTypes [ i ]) . matches ( typeCheck ) ? " " : " " ) + " , " ;
72 }
73 }
74
75 statement = statement . substring (0 , statement . length () -2) + " ) "
;
76
77 Prep aredSt atemen t pstmt = connection
78 . prepareStatement ( statement ) ;
79 pstmt . executeUpdate () ;
80 } finally {
81 if ( connection != null ) {
82 connection . close () ;
83 }
84 }

85
85 }
86
87 /* *
88 * Get all persons from the table .
89 */
90 // public List < Person > selectAllPersons () throws SQLException {
91 // Connection connection = dataSource . getConnection () ;
92 // try {
93 // Pre paredS tateme nt pstmt = connection
94 // . prepareStatement (" SELECT ID , FIRSTNAME , LASTNAME
FROM T_PERSONS ") ;
95 // ResultSet rs = pstmt . executeQuery () ;
96 // ArrayList < Person > list = new ArrayList < Person >() ;
97 // while ( rs . next () ) {
98 // Person p = new Person () ;
99 // p . setId ( rs . getString (1) ) ;
100 // p . setFirstName ( rs . getString (2) ) ;
101 // p . setLastName ( rs . getString (3) ) ;
102 // list . add ( p ) ;
103 // }
104 // return list ;
105 // } finally {
106 // if ( connection != null ) {
107 // connection . close () ;
108 // }
109 // }
110 // }
111
112 /* *
113 * Check if the person table already exists and create it if not .
114 */
115 public boolean checkTable ( String tableName , String [] dataTypes ,
String [] dataColumns ) throws SQLException {
116 Connection connection = null ;
117 boolean result = false ;
118
119 try {
120 connection = dataSource . getConnection () ;
121 if (! existsTable ( connection , tableName ) ) {
122 createTable ( connection , tableName , dataTypes ,
dataColumns ) ;
123 result = true ;
124 }
125 } finally {
126 if ( connection != null ) {
127 connection . close () ;
128 }
129 }
130

86
131 return result ;
132 }
133
134 /* *
135 * Check if the person table already exists .
136 */
137 private boolean existsTable ( Connection conn , String tableName )
throws SQLException {
138 DatabaseMetaData meta = conn . getMetaData () ;
139 ResultSet rs = meta . getTables ( null , null , tableName , null ) ;
140 while ( rs . next () ) {
141 String name = rs . getString ( " TABLE_NAME " ) ;
142 if ( name . equals ( tableName ) ) {
143 return true ;
144 }
145 }
146 return false ;
147 }
148
149 /* *
150 * Create the person table .
151 */
152 private void createTable ( Connection connection , String tableName ,
String [] dataTypes , String [] dataColumns ) throws SQLException {
153 String statement = " CREATE TABLE " + tableName + " ( " ; //
Generamos el Statement SQL en funcion de los datos que
obtenemos .
154
155 for ( int i = 0; i < dataTypes . length ; i ++)
156 {
157 statement = statement + " " + dataColumns [ i ] + " " + typeMapping
( dataTypes [ i ]) + (( i == dataTypes . length - 1) ? " " : " ," ) ;
// TODO : Encontrar la manera de averiguar cuales son los
campos clave y ponerlo
158 }
159
160 statement = statement + " ) " ;
161
162
163 Prep aredSt ateme nt pstmt = connection
164 . prepareStatement ( statement ) ;
165 pstmt . executeUpdate () ;
166 }
167
168 /* *
169 * Map ABAP types to SQL types
170 */
171
172 private String typeMapping ( String type )

87
173 {
174 String result ;
175 type = type . substring (0 , 1) ;
176
177 switch ( AbapTypes . valueOf ( type ) )
178 {
179 case B :
180 result = " VARBINARY (1) " ;
181 break ;
182 case S :
183 result = " VARBINARY (2) " ;
184 break ;
185 case L :
186 result = " VARBINARY (4) " ;
187 break ;
188 case C :
189 result = " VARCHAR (255) " ;
190 break ;
191 case N :
192 result = " VARCHAR (255) " ;
193 break ;
194 case P :
195 result = " DECIMAL " ;
196 break ;
197 case D :
198 result = " DATE " ;
199 break ;
200 case T :
201 result = " TIME " ;
202 break ;
203 case F :
204 result = " DOUBLE PRECISION " ;
205 break ;
206 case X :
207 result = " VARBINARY (255) " ;
208 break ;
209 case I :
210 result = " INTEGER " ;
211 break ;
212 default :
213 result = null ;
214 }
215 return result ;
216 }
217 }

88
B.4. AbapData.java
1 package com . sap . transfer . jco ;
2
3 public class AbapData {
4 String [] fieldType ;
5
6 String [] fieldName ;
7
8 String [][] tableEntries ;
9 }

89
C. Archivos nativos de HANA XS.
A continuacion listamos los archivos necesarios para crear una aplicacion web, expo-
nerla, y hacer que pueda manipular los datos de HANA.

C.1. .xsapp

90
C.2. .xsaccess
1 {
2 " exposed " : true ,
3 " default_file " : " index . html " ,
4 " cors " : [{ " enabled " : true }] ,
5 " a n o n y mo u s _ c o n n e c t i o n " : " p1940528821trial . myhanaxs . jcodata :: AC "
6 }

91
C.3. .xsprivileges
1 { " privileges " :
2 [ { " name " : " Basic " , " description " : " Basic usage privilege " } ]
}

92
C.4. model access.hdbrole
1 role p1940528821trial . myhanaxs . jcodata :: model_access {
2 application privilege : p1940528821trial . myhanaxs . jcodata :: Basic ;
3 catalog schema " N E O _ 2 Y R C M 3 I 3 B M 6 5 X 2 E P A L 4 2 I E L V B " : SELECT , UPDATE , INSERT ,
DELETE ;
4 package p1940528821trial . myhanaxs . jcodata : REPO .
ACTIVATE_IMPORTED_OBJECTS ;
5 }

93
D. Codigo fuente del modulo de presentacion: Javas-
cript, CSS3, HTML5.
A continuacion pasamos a listar el codigo fuente de la aplicacion web:

D.1. index.html
1 <! DOCTYPE html >
2 < html >
3 < head >
4 < meta http - equiv = X - UA - Compatible content = IE = edge , chrome =1 >
5 < meta http - equiv = Content - Type content = text / html ; charset = UTF -8 / >
6 < link href = " customStyles . css " type = " text / css " rel = " stylesheet " / >
7
8 < script id = " jspdf - bootstrap " type = " text / javascript " src = " jspdf . js " > <
/ script >
9 < script id = " jspdf - standard - fonts - metrics - bootstrap " type = " text /
javascript "
10 src = " jspdf . plugin . s t a n d a r d _ f o n t s _ m e t r i c s . js " > </ script >
11 < script id = " filesaver - min - bootstrap " type = " text / javascript " src = "
FileSaver . min . js " > </ script >
12
13 < script id = sap - ui - bootstrap " type = " text / javascript
14 src = https :// sapui5 . hana . ondemand . com / resources / sap - ui - core . js

15 data - sap - ui - theme = " sap_bluecrystal "
16 data - sap - ui - libs = sap . ui . commons , sap . ui . ux3 , sap . viz , sap . ui .
table , sap . ui . core
17 data - sap - ui - xx - bindingSyntax = " complex " >
18 </ script >
19
20 < script >
21 sap . ui . localResources ( " mainView " ) ;
22 sap . ui . localResources ( " dataTableView " ) ;
23 sap . ui . localResources ( " dataPayrollView " ) ;
24 sap . ui . localResources ( " orgStructureView " ) ;
25
26 var transferModel = new sap . ui . model . json . JSONModel () ;
27 transferModel . setData ({
28 FPBEG : [] ,
29 FPEND : [] ,
30 RELID : [] ,
31 BUKRS : [] ,
32 WERKS : [] ,
33 ENAME : []
34 }) ;
35

94
36 sap . ui . getCore () . setModel ( transferModel ) ;
37
38 var view = sap . ui . view ({ viewName : " mainView . shell " ,
39 type : sap . ui . core . mvc . ViewType . JS }) ;
40 view . placeAt ( " content " ) ;
41 </ script >
42 </ head >
43
44 < body class = " sapUiBody " role = " application " >
45 < div id = " content " > </ div >
46 </ body >
47 </ html >

95
D.2. customStyles.css
1 @CHARSET " ISO -8859 -1 " ;
2
3
4 /* Custom Styles */
5
6 . myWhiteCellStyle {
7 background - color : # FFFFFF ;
8 }
9
10 . myTex tField Margin {
11 margin - left : 6 px ;
12 margin - right : 6 px ;
13 }

96
D.3. shell.controller.js
1 sap . ui . controller ( " mainView . shell " , {
2 w or k set I t em s C o nt r o l : function ( oEvent , key ) {
3 oShell = this . getView () . byId ( " shell " ) ;
4 if ( key == " table_button " ) {
5 var viewTable = sap . ui . view ({ viewName : " dataTableView . table " ,
6 type : sap . ui . core . mvc . ViewType . JS }) ;
7 oShell . setContent ( viewTable , true ) ;
8 } else if ( key == " cluster_button " ) {
9 var viewCluster = sap . ui . view ({ viewName : " dataPayrollView .
cluster " ,
10 type : sap . ui . core . mvc . ViewType . JS }) ;
11 oShell . setContent ( viewCluster , true ) ;
12 } else if ( key == " structure_button " ) {
13 var viewStructure = sap . ui . view ({ viewName : " orgStructureView . tree " ,
14 type : sap . ui . core . mvc . ViewType . JS }) ;
15 oShell . setContent ( viewStructure , true ) ;
16 }
17
18 }
19 }) ;

97
D.4. shell.view.js
1 sap . ui . jsview ( " mainView . shell " , {
2 get Contro llerNa me : function () {
3 return " mainView . shell " ;
4 },
5
6 createContent : function ( oController ) {
7
8 // Shell
9 var oShell = new sap . ui . ux3 . Shell ( this . createId ( " shell " ) , {
10 showPane : false ,
11 showSearchTool : false ,
12 showFeederTool : false ,
13 showLogoutButton : false ,
14 designType : sap . ui . ux3 . ShellDesignType . Crystal ,
15
16 worksetItems : [
17 new sap . ui . ux3 . NavigationItem ({ key : " table_button " ,
text : " Volcado de tablas " }) ,
18 new sap . ui . ux3 . NavigationItem ({ key : " cluster_button " ,
text : " Ficha Salarial Empleado " }) ,
19 new sap . ui . ux3 . NavigationItem ({ key : " structure_button " ,
text : " Arbol estructura organizativa " })
20 ],
21
22 w ork s et I t em S e le c t e d : function ( oEvent ) {
23 var key = oEvent . getParameter ( " item " ) . getKey () ;
24 oController . wo r k se t I te m s Co n t ro l ( oEvent , key ) ;
25 }
26 }) ;
27
28 var viewTable = sap . ui . view ({ viewName : " dataTableView . table " ,
29 type : sap . ui . core . mvc . ViewType . JS }) ;
30 oShell . setContent ( viewTable , true ) ;
31
32 return oShell ;
33 }
34
35 }) ;

98
D.5. table.controller.js
1 sap . ui . controller ( " dataTableView . table " , {
2
3 setTableModel : function ( tableName , layout , table ) {
4 layout . setBusy ( true ) ;
5 this . tableModel = new sap . ui . model . json . JSONModel () ;
6 table . setModel ( this . tableModel ) ;
7
8 jQuery . ajax ({
9 url : " / p1940528821trial / myhanaxs / jcodata / json . model . xsjs / " ,
10 type : GET ,
11 contentType : application / json ,
12 dataType : json ,
13 data : {
14 tablename : tableName
15 },
16 success : jQuery . proxy ( function ( result ) {
17 // Asociamos el modelo a la tabla
18 this . tableModel . setData ( result ) ;
19 var rowcount = this . tableModel . oData . data . length ;
20 this . showData ( rowcount , table ) ;
21 } , this ) ,
22 error : jQuery . proxy ( function () {
23 this . uploadTable ( tableName ) ;
24 } , this ) ,
25 complete : jQuery . proxy ( function () {
26 layout . setBusy ( false ) ;
27 } , this )
28 }) ;
29 },
30
31
32 showData : function ( rowcount , table ) {
33 if ( rowcount === 0) {
34 sap . ui . commons . MessageBox . alert ( " Table was empty . " ) ;
35 rowcount = 2;
36 table . setNoData ( new sap . ui . commons . TextView ({
37 text : " "
38 }) ) ;
39 }
40
41 // Y atamos las filas y columnas al modelo
42 table . bindColumns ( " / metadata " , function ( sID , oContext ) {
43 var sProperty = oContext . getObject () ;
44 return new sap . ui . table . Column ({
45 width : 150 px ,
46 wrapping : true ,
47 id : sProperty ,

99
48 label : sProperty ,
49 template : sProperty ,
50 sortProperty : sProperty ,
51 filterProperty : sProperty
52 }) ;
53 }) ;
54
55 table . bindRows ( " / data " ) ;
56
57 table . bindProperty ( " visibleRowCount " , " / rowcount " , function () {
58 return Math . min (15 , rowcount ) ;
59 }) ;
60 },
61
62
63 uploadTable : function ( tableName ) {
64 jQuery . ajax ({
65 url : " https :// j c o t f p 1 9 4 0 5 2 8 8 2 1 t r i a l . hanatrial . ondemand . com /
jcotf / Contro llerSe rvlet " ,
66 type : POST ,
67 crossDomain : true ,
68 data : {
69 functionname : " / BODS / RFC_READ_TABLE2 " ,
70 tablename : tableName } ,
71 success : jQuery . proxy ( function ( result ) {
72 sap . ui . commons . MessageBox . alert ( " Tabla subida con exito !
");
73 layout = this . getView () . byId ( " layout " ) ;
74 table = this . getView () . byId ( " table " ) ;
75 this . setTableModel ( tableName , layout , table ) ;
76 } , this ) ,
77 error : function () { sap . ui . commons . MessageBox . alert ( " Error
subiendo tabla . " ) ; }
78 }) ;
79 }
80 }) ;

100
D.6. table.view.js
1 sap . ui . jsview ( " dataTableView . table " , {
2 get Contro llerNa me : function () {
3 return " dataTableView . table " ;
4 },
5
6 createContent : function ( oController ) {
7
8 // Layout
9 var oLayout = new sap . ui . commons . layout . MatrixLayout ( this . createId ( "
layout " ) , {
10 widths : 150 px ,
11 allowWrapping : true
12 }) ;
13
14 // Toolbar Controls
15 var oTableNameLabel = new sap . ui . commons . Label ({
16 text : " Introduzca el nombre de la tabla que desee ver ( e . g .:
PA0001 ) : "
17 }) ;
18
19 var oTableNameValue = new sap . ui . commons . TextField ( this . createId ( "
oTableNameValue " ) , {
20 value : ,
21 width : 20 em ,
22 press1 : [ oController . onLiveChange , oController ] ,
23 liveChange : function ( oEvent ) {
24 var searchBtn = oController . getView () . byId ( " oSearchButton " ) ;
25 if ( this . getLiveValue () === " " ) {
26 searchBtn . setEnabled ( false ) ;
27 } else {
28 searchBtn . setEnabled ( true ) ;
29 }
30 }
31 }) ;
32
33 var oSearchButton = new sap . ui . commons . Button ( this . createId ( "
oSearchButton " ) , {
34 text : " Search " ,
35 press1 : [ oController . onSearch , oController ] ,
36 enabled : false ,
37 press : function ( event ) {
38 var tableName = oController . getView () . byId ( " oTableNameValue " ) ;
39 var layout = oController . getView () . byId ( " layout " ) ;
40 var table = oController . getView () . byId ( " table " ) ;
41
42 oController . setTableModel ( tableName . getDomRef () . value , layout ,
table ) ;

101
43 }
44 }) ;
45
46
47
48 // Stock Values Table
49 var oTable = new sap . ui . table . Table ( this . createId ( " table " ) , {
50 visibleRowCount : 2 ,
51 toolbar : new sap . ui . commons . Toolbar ({
52 items : [ oTableNameLabel , oTableNameValue , oSearchButton ]
53 }) ,
54 noData : new sap . ui . commons . TextView ({
55 text : " Introduzca el nombre de la tabla que quiera ver o subir .
"
56 }) ,
57 }) ;
58
59
60
61 oLayout . createRow ( new sap . ui . commons . layout . MatrixLayoutCell () .
addContent ( oTable ) ) ;
62
63 // Add chart here
64
65 return oLayout ;
66 }
67
68 }) ;

102
D.7. cluster.controller.js
1 sap . ui . controller ( " dataPayrollView . cluster " , {
2
3 setHistoryModel : function ( employeeNumber , layout , table ) {
4 layout . setBusy ( true ) ;
5 this . tableModel = new sap . ui . model . json . JSONModel () ;
6 table . setModel ( this . tableModel ) ;
7
8 jQuery . ajax ({
9 url : " / p1940528821trial / myhanaxs / jcodata / json . model . xsjs / " ,
10 type : GET ,
11 contentType : application / json ,
12 dataType : json ,
13 data : {
14 tablename : " PH " + employeeNumber
15 },
16 success : jQuery . proxy ( function ( result ) {
17 // Asociamos el modelo a la tabla
18 this . tableModel . setData ( result ) ;
19 var rowcount = this . tableModel . oData . data . length ;
20 this . showData ( rowcount , table ) ;
21 } , this ) ,
22 error : jQuery . proxy ( function () {
23 this . uploadHistory ( employeeNumber ) ;
24 } , this ) ,
25 complete : jQuery . proxy ( function () {
26 layout . setBusy ( false ) ;
27 } , this )
28 }) ;
29 },
30
31
32 showData : function ( rowcount , table ) {
33 if ( rowcount === 0) {
34 sap . ui . commons . MessageBox . alert ( " Payroll History was empty . "
);
35 rowcount = 2;
36 table . setNoData ( new sap . ui . commons . TextView ({
37 text : " "
38 }) ) ;
39 }
40
41 // Y atamos las filas y columnas al modelo
42 table . bindColumns ( " / metadata " , function ( sID , oContext ) {
43 var sProperty = oContext . getObject () ;
44 return new sap . ui . table . Column ({
45 width : 150 px ,
46 wrapping : true ,

103
47 id : sProperty ,
48 label : sProperty ,
49 template : sProperty ,
50 sortProperty : sProperty ,
51 filterProperty : sProperty
52 }) ;
53 }) ;
54
55 table . bindRows ( " / data " ) ;
56
57 table . bindProperty ( " visibleRowCount " , " / rowcount " , function () {
58 return Math . min (15 , rowcount ) ;
59 }) ;
60 },
61
62
63 uploadHistory : function ( employeeNumber ) {
64 jQuery . ajax ({
65 url : " https :// j c o t f p 1 9 4 0 5 2 8 8 2 1 t r i a l . hanatrial . ondemand . com /
jcotf / Contro llerSe rvlet " ,
66 type : POST ,
67 crossDomain : true ,
68 data : {
69 functionname : " Z _ R F C _ H R _ R E A D _ P A Y R O L L _ E N T R I E S " ,
70 employeenumber : employeeNumber } ,
71 success : jQuery . proxy ( function ( result ) {
72 sap . ui . commons . MessageBox . alert ( " Historial Salarial
subido con exito ! " ) ;
73 layout = this . getView () . byId ( " layout " ) ;
74 table = this . getView () . byId ( " table " ) ;
75 this . setHistoryModel ( employeeNumber , layout , table ) ;
76 } , this ) ,
77 error : function () { sap . ui . commons . MessageBox . alert ( " Error
subiendo el Historial Salarial . " ) ; }
78 }) ;
79 },
80
81 passVariables : function ( oEvent ) {
82 var payrollModel = sap . ui . getCore () . getModel () ;
83 var indexSelectedRow = oEvent . getParameters () . rowIndex ;
84 payrollModel . setData ({
85 FPBEG : this . getView () . byId ( " table " ) . getModel () . oData . data [
indexSelectedRow ]. FPBEG ,
86 FPEND : this . getView () . byId ( " table " ) . getModel () . oData . data [
indexSelectedRow ]. FPEND ,
87 RELID : this . getView () . byId ( " table " ) . getModel () . oData . data [
indexSelectedRow ]. RELID ,
88 CDSEQ : this . getView () . byId ( " table " ) . getModel () . oData . data [
indexSelectedRow ]. CDSEQ ,

104
89 BUKRS : this . getView () . byId ( " table " ) . getModel () . oData . data [
indexSelectedRow ]. BUKRS ,
90 WERKS : this . getView () . byId ( " table " ) . getModel () . oData . data [
indexSelectedRow ]. WERKS ,
91 ENAME : this . getView () . byId ( " table " ) . getModel () . oData . data [
indexSelectedRow ]. ENAME ,
92 PERNR : this . getView () . byId ( " o E m p l o y e e N u m b e r V a l u e " ) . getDomRef () .
value
93 }) ;
94 }
95 }) ;

105
D.8. cluster.view.js
1 sap . ui . jsview ( " dataPayrollView . cluster " , {
2 get Contro llerNa me : function () {
3 return " dataPayrollView . cluster " ;
4 },
5
6 createContent : function ( oController ) {
7
8 // Layout
9 var oLayout = new sap . ui . commons . layout . MatrixLayout ( this . createId ( "
layout " ) , {
10 widths : 150 px ,
11 allowWrapping : true
12 }) ;
13
14 // Toolbar Controls
15 var oTableNameLabel = new sap . ui . commons . Label ({
16 text : " Introduzca el numero de un empleado para ver su historial
salarial : "
17 }) ;
18
19 var o E m p l o y e e N u m b e r V a l u e = new sap . ui . commons . TextField ( this .
createId ( " o E m p l o y e e N u m b e r V a l u e " ) , {
20 value : ,
21 width : 20 em ,
22 press1 : [ oController . onLiveChange , oController ] ,
23 liveChange : function ( oEvent ) {
24 var searchBtn = oController . getView () . byId ( " oSearchButton " ) ;
25 if ( this . getLiveValue () === " " ) {
26 searchBtn . setEnabled ( false ) ;
27 } else {
28 searchBtn . setEnabled ( true ) ;
29 }
30 }
31 }) ;
32
33 var oSearchButton = new sap . ui . commons . Button ( this . createId ( "
oSearchButton " ) , {
34 text : " Search " ,
35 press1 : [ oController . onSearch , oController ] ,
36 enabled : false ,
37 press : function ( event ) {
38 var tableName = oController . getView () . byId ( " o E m p l o y e e N u m b e r V a l u e
");
39 var layout = oController . getView () . byId ( " layout " ) ;
40 var table = oController . getView () . byId ( " table " ) ;
41

106
42 oController . setHistoryModel ( tableName . getDomRef () . value , layout ,
table ) ;
43 }
44 }) ;
45
46
47 var oTable = new sap . ui . table . Table ( this . createId ( " table " ) , {
48 visibleRowCount : 2 ,
49 toolbar : new sap . ui . commons . Toolbar ({
50 items : [ oTableNameLabel , oEmployeeNumberValue , oSearchButton ]
51 }) ,
52 noData : new sap . ui . commons . TextView ({
53 text : " Introduzca el numero de un empleado . "
54 }) ,
55 ro wS el ec ti on Ch an ge : function ( oEvent ) {
56 oController . passVariables ( oEvent ) ;
57 var viewResult = sap . ui . view ({ viewName : " dataPayrollView . result " ,
58 type : sap . ui . core . mvc . ViewType . JS }) ;
59 oShell . setContent ( viewResult , true ) ;
60 }
61 }) ;
62
63
64
65 oLayout . createRow ( new sap . ui . commons . layout . MatrixLayoutCell () .
addContent ( oTable ) ) ;
66
67 // Add chart here
68
69 return oLayout ;
70 }
71
72 }) ;

107
D.9. result.controller.js
1 sap . ui . controller ( " dataPayrollView . result " , {
2
3 getPayrollData : function () {
4
5 transferModel = sap . ui . getCore () . getModel () ;
6 var sequenceNumber = transferModel . oData . CDSEQ ;
7 var employeeNumber = transferModel . oData . PERNR ;
8 var payrollData ;
9
10 // Eliminamos los ceros a la izquierda del n de secuencia
11 sequenceNumber = sequenceNumber . replace (/^[0]+/ g , " " ) ;
12
13 jQuery . ajax ({
14 // Necesitamos que sea sincrono , si no payrollData estaria
indefinido
15 async : false ,
16 url : " / p1940528821trial / myhanaxs / jcodata / json . model . xsjs / " ,
17 type : GET ,
18 contentType : application / json ,
19 dataType : json ,
20 data : {
21 tablename : " PH " + employeeNumber + " _PE " +
sequenceNumber
22 },
23 success : jQuery . proxy ( function ( result ) {
24 payrollData = result ;
25 } , this ) ,
26
27 error : jQuery . proxy ( function () {
28 this . upl oa dP ay ro ll En tr y ( employeeNumber , sequenceNumber ,
relationId ) ;
29 // else
30 // sap . ui . commons . MessageBox . alert (" Esta funcion depende
de la tabla PA0001 para funcionar . " +
31 // " Si despues de subirla el error persiste , contacte
con el desarrollador de la aplicacion .")
32 } , this )
33 }) ;
34
35 return payrollData ;
36 },
37
38
39 up lo ad Pa yr ol lE nt ry : function ( employeeNumber , sequenceNumber ,
relationId ) {
40 jQuery . ajax ({
41 async : false ,

108
42 url : " https :// j c o t f p 1 9 4 0 5 2 8 8 2 1 t r i a l . hanatrial . ondemand . com /
jcotf / Contro llerSe rvlet " ,
43 type : POST ,
44 crossDomain : true ,
45 data : {
46 functionname : " Z _ R F C _ H R _ R E A D _ P A Y R O L L _ R E S U L T S " ,
47 employeenumber : employeeNumber ,
48 sequencenumber : sequenceNumber ,
49 relationid : relationId } ,
50 success : jQuery . proxy ( function ( result ) {
51 sap . ui . commons . MessageBox . alert ( " Entrada salarial subida
con exito ! " ) ;
52 this . getPayrollEntry () ;
53 } , this ) ,
54 error : function () { sap . ui . commons . MessageBox . alert ( " Error
subiendo entrada salarial . " ) ; }
55 }) ;
56
57 jQuery . ajax ({
58 async : false ,
59 url : " https :// j c o t f p 1 9 4 0 5 2 8 8 2 1 t r i a l . hanatrial . ondemand . com /
jcotf / Contro llerSe rvlet " ,
60 type : POST ,
61 crossDomain : true ,
62 data : {
63 functionname : " / BODS / RFC_READ_TABLE2 " ,
64 tablename : " PA0001 "
65 },
66 success : jQuery . proxy ( function ( result ) {
67 sap . ui . commons . MessageBox . alert ( " PA0001 subida con exito
!");
68 this . getPayrollEntry () ;
69 } , this ) ,
70 error : function () { sap . ui . commons . MessageBox . alert ( " Error
subiendo PA0001 . " ) ; }
71 }) ;
72 },
73
74 getChartModel : function ( year , grossPay ) {
75 var chartModel = new sap . ui . model . json . JSONModel ({
76 predictedSalary : [
77 { year_M : year , grossPay_M : grossPay }
78 ]
79 }) ;
80
81 for ( var i = 0; i < 4; i ++) {
82 dataObj = { year_M : ++ year , grossPay_M : ( grossPay *= 1.02) .
toFixed (2) }
83 chartModel . oData . predictedSalary . push ( dataObj ) ;

109
84 }
85
86 return chartModel ;
87 },
88
89 pdfPayroll : function ( organization , division , employeeName ,
payrollBeginning , payrollEnd ,
90 grossPay , taxableBase , baseOfContribution , baseOfContributionPPC
, IRPF , netPay ) {
91
92 var pdf = new jsPDF ( l , cm , a5 ) ;
93 pdf . setLineWidth (0.05) ;
94 pdf . setFontSize (10) ;
95
96 // 1.2 cm de margen horizontal entre rectangulos y borde de
pagina
97 // 0.2 cm de margen entre borde izquierdo o derecho de
rectangulo y linea
98 // 0.5 cm de margen vertical entre lineas de texto
99
100 // Rectangulo superior izquierdo : Datos de la Empresa
101 pdf . rect (1.2 , 0.5 , 9 , 3) ;
102 pdf . text (1.4 , 1 , " Empresa : " + organization ) ;
103 pdf . text (1.4 , 1.5 , " Division : " + division ) ;
104 pdf . text (1.4 , 2 , " Direccion de la empresa : Por subir " ) ;
105 pdf . text (1.4 , 2.5 , " CIF de la empresa : Por subir " ) ;
106 pdf . text (1.4 , 3 , " Codigo postal de la empresa : Por subir " ) ;
107
108
109 // Rectangulo superior derecho : Datos del empleado
110 pdf . rect (10.8 , 0.5 , 9 , 3) ;
111 pdf . text (11 , 1 , " Empleado : " + employeeName ) ;
112 pdf . text (11 , 1.5 , " Antiguedad : Por subir " ) ;
113 pdf . text (11 , 2 , " DNI del empleado : Por subir " ) ;
114 pdf . text (11 , 2.5 , " Numero Seguridad Social : Por subir " ) ;
115 pdf . text (11 , 3 , " Periodo de Nomina : " + payrollBeginning + " /
" + payrollEnd ) ;
116
117 // Tabla Central : Conceptos de nomina
118 // Header izquierdo
119 pdf . rect (1.2 , 4 , 15 , 1) ;
120 pdf . text (1.4 , 4.5 , " Concepto " ) ;
121 // Header derecho
122 pdf . rect (16.2 , 4 , 3.6 , 1) ;
123 pdf . text (16.4 , 4.5 , " Cantidad " ) ;
124 // Conceptos
125 pdf . rect (1.2 , 5 , 15 , 3.5) ;
126 pdf . text (1.4 , 5.5 , " Salario Bruto " ) ;
127 pdf . text (1.4 , 6 , " Base Imponible " ) ;

110
128 pdf . text (1.4 , 6.5 , " Base de Cotizacion " ) ;
129 pdf . text (1.4 , 7 , " Base de Cotizacion por contingencias
profesionales " ) ;
130 pdf . text (1.4 , 7.5 , " IRPF " ) ;
131 pdf . text (1.4 , 8 , " Salario Neto " ) ;
132 // Cantidades
133 pdf . rect (16.2 , 5 , 3.6 , 3.5) ;
134 pdf . text (16.4 , 5.5 , grossPay ) ;
135 pdf . text (16.4 , 6 , taxableBase ) ;
136 pdf . text (16.4 , 6.5 , ba se Of Co nt rib ut io n ) ;
137 pdf . text (16.4 , 7 , b a s e O f C o n t r i b u t i o n P P C ) ;
138 pdf . text (16.4 , 7.5 , " -" + IRPF ) ;
139 pdf . text (16.4 , 8 , netPay ) ;
140
141 // Rubrica
142 pdf . text (1.4 , 9.5 , " Sevilla , a " + this . spanishFullDate () ) ;
143
144 pdf . output ( save , nomina . pdf ) ;
145 },
146
147 spanishFullDate : function () {
148 today = new Date () ;
149
150 // Mes para la rubrica
151 switch ( today . getMonth () + 1) {
152 case 1:
153 var month = " enero " ;
154 break ;
155 case 2:
156 var month = " febrero " ;
157 break ;
158 case 3:
159 var month = " marzo " ;
160 break ;
161 case 4:
162 var month = " abril " ;
163 break ;
164 case 5:
165 var month = " mayo " ;
166 break ;
167 case 6:
168 var month = " junio " ;
169 break ;
170 case 7:
171 var month = " julio " ;
172 break ;
173 case 8:
174 var month = " agosto " ;
175 break ;

111
176 case 9:
177 var month = " septiembre " ;
178 break ;
179 case 10:
180 var month = " octubre " ;
181 break ;
182 case 11:
183 var month = " noviembre " ;
184 break ;
185 case 12:
186 var month = " diciembre " ;
187 }
188
189 return today . getDate () + " de " + month + " de " + today . getFullYear
() ;
190 }
191 }) ;

112
D.10. result.view.js
1 sap . ui . jsview ( " dataPayrollView . result " , {
2 get Contro llerNa me : function () {
3 return " dataPayrollView . result " ;
4 },
5
6 createContent : function ( oController ) {
7
8 transferModel = sap . ui . getCore () . getModel () ;
9
10
11 var employeeName = transferModel . oData . ENAME ;
12 var organization = transferModel . oData . BUKRS ;
13 var division = transferModel . oData . WERKS ;
14 var payrollBeginning = transferModel . oData . FPBEG ;
15 var payrollEnd = transferModel . oData . FPEND ;
16 var relationId = transferModel . oData . RELID ;
17
18 // Layout
19 var oLayout = new sap . ui . commons . layout . MatrixLayout ( this . createId ( "
layout " ) , {
20 widths : 150 px ,
21 width : " 100 % " ,
22 allowWrapping : true
23 }) . addStyleClass ( " myWhiteCellStyle " ) ;
24
25 var payrollData = oController . getPayrollData () ;
26
27 // Datos salariales del empleado
28
29 oLayout . createRow ( " Ficha de Empleado : " ) ;
30 oLayout . createRow ( " Nombre : " + employeeName ) ;
31 oLayout . createRow ( " Organizacion : " + organization + " Division :
" + division ) ;
32 oLayout . createRow ( " Periodo de nomina : " + payrollBeginning + " - " +
payrollEnd ) ;
33
34 for ( var i = 0; i < payrollData . data . length ; i ++) {
35 switch ( payrollData . data [ i ]. LGART ) {
36 case " /101 " :
37 var printWageType = true ;
38 var prefix = " Salario Bruto : " ;
39 var grossPay = parseFloat ( payrollData . data [ i ]. BETRG ) ;
40 break ;
41 case " /106 " :
42 var printWageType = true ;
43 var prefix = " Base Imponible : " ;
44 var taxableBase = parseFloat ( payrollData . data [ i ]. BETRG ) ;

113
45 break ;
46 case " /342 " :
47 var printWageType = true ;
48 var prefix = " Base de Cotizacion : " ;
49 var ba se Of Co nt rib ut io n = parseFloat ( payrollData . data [ i ]. BETRG ) ;
50 break ;
51 case " /343 " :
52 var printWageType = true ;
53 var prefix = " Base de Cotizacion por contingencias profesionales
: ";
54 var b a s e O f C o n t r i b u t i o n P P C = parseFloat ( payrollData . data [ i ]. BETRG
);
55 break ;
56 case " /398 " :
57 var printWageType = false ;
58 var contribution = parseFloat ( payrollData . data [ i ]. BETRG ) ;
59 break ;
60 case " /401 " :
61 var printWageType = true ;
62 var prefix = " IRPF : " ;
63 var IRPF = parseFloat ( payrollData . data [ i ]. BETRG ) ;
64 break ;
65 default :
66 var printWageType = false ;
67 }
68
69 if ( printWageType )
70 oLayout . createRow ( prefix + payrollData . data [ i ]. BETRG ) ;
71 };
72
73 var netPay = ( grossPay - contribution - IRPF ) . toFixed (2) ;
74
75 oLayout . createRow ( " Salario neto : " + netPay . toString () ) ;
76
77 // Prediccion salarial a 5 anios
78
79 chartModel = oController . getChartModel ( parseInt ( payrollEnd . substring
(0 , 4) ) , grossPay ) ;
80
81 var chartDataset = new sap . viz . ui5 . data . FlattenedDataset ({
82 dimensions : [
83 {
84 axis : 1 , // must be one for the x - axis , 2 for y - axis
85 name : Anio ,
86 value : " { year_M } "
87 }
88 ],
89
90 measures : [

114
91 // measure 1
92 {
93 name : Salario Bruto , // name is used as label in
the Legend
94 value : { grossPay_M } // value defines the binding
for the displayed value
95 },
96 ],
97
98 data : {
99 path : " / predictedSalary "
100 }
101
102 }) ;
103
104 var oBarChart = new sap . viz . ui5 . Bar ({
105 width : " 50 % " ,
106 height : " 400 px " ,
107 plotArea : {
108 // colorPalette : d3 . scale . category20 () . range ()
109 },
110 title : {
111 visible : true ,
112 text : Prediccion de Salario Bruto a 5 anios .
113 },
114 dataset : chartDataset
115 }) ;
116
117 oBarChart . setModel ( chartModel ) ;
118
119 oLayout . createRow ( oBarChart ) ;
120
121 // Botones para generar nomina y volver
122
123 var oButton1 = new sap . ui . commons . Button ( this . createId ( " button1 " ) , {
124 text : " Generar nomina " ,
125 press : function () {
126 oController . pdfPayroll ( organization , division , employeeName ,
payrollBeginning , payrollEnd ,
127 grossPay , taxableBase , baseOfContribution ,
baseOfContributionPPC , IRPF , netPay ) ;
128 }
129 }) . addStyleClass ( " my TextFi eldMar gin " ) ;
130
131 var oButton2 = new sap . ui . commons . Button ( this . createId ( " button2 " ) , {
132 text : " Volver " ,
133 press : function () {
134 var viewCluster = sap . ui . view ({ viewName : " dataPayrollView . cluster
",

115
135 type : sap . ui . core . mvc . ViewType . JS }) ;
136 oShell . setContent ( viewCluster , true ) ;
137 }
138 }) ;
139
140 oLayout . createRow (
141 new sap . ui . commons . layout . MatrixLayoutCell ({
142 content : [ oButton1 , oButton2 ]
143 })
144 );
145
146 return oLayout ;
147 }
148 }) ;

116
D.11. tree.controller.js
1 sap . ui . controller ( " orgStructureView . tree " , {
2 createTree : function ( oTree , sIdValue , oLayout ) {
3 oLayout . setBusy ( true ) ;
4 this . org Struc tureMo del = new sap . ui . model . json . JSONModel () ;
5
6 jQuery . ajax ({
7 url : " / p1940528821trial / myhanaxs / jcodata / json . model . xsjs / " ,
8 type : GET ,
9 contentType : application / json ,
10 dataType : json ,
11 data : {
12 tablename : " STO_ID " + sIdValue
13 },
14 success : jQuery . proxy ( function ( oModel ) {
15 // Asociamos el modelo a la tabla
16 this . drawTree ( oTree , oModel ) ;
17 } , this ) ,
18 error : jQuery . proxy ( function () {
19 this . uploadTree ( oTree , sIdValue , oLayout ) ;
20 } , this ) ,
21 complete : jQuery . proxy ( function () {
22 oLayout . setBusy ( false ) ;
23 } , this )
24 }) ;
25 },
26
27 drawTree : function ( oTree , oModel ) {
28 var addNode ;
29 nodeList = [];
30 for ( var i = 0; i < oModel . data . length ; i ++) {
31 addNode = true ;
32
33 // We check for repeated nodes . If a node is already in the tree ,
we don t add it .
34 for ( var j = 0; j < nodeList . length ; j ++)
35 if ( parseInt ( oModel . data [ i ]. SEQNR ) === nodeList [ j ])
36 addNode = false ;
37
38 if ( addNode ) {
39 nodeList [ nodeList . length ] = parseInt ( oModel . data [ i ]. SEQNR ) ;
40
41 switch ( oModel . data [ i ]. OTYPE ) {
42 case " O " :
43 var prefix = " Es superior directo de : " ;
44 break ;
45 case " S " :
46 var prefix = " Comprende : " ;

117
47 break ;
48 case " P " :
49 var prefix = " Titular : " ;
50 break ;
51 default :
52 var prefix = " " ;
53 }
54
55
56 var oNode = new sap . ui . commons . TreeNode ( this . createId ( " Node_ " +
oModel . data [ i ]. SEQNR ) , {
57 text : ( oModel . data [ i ]. LEVEL !== " 1 " ? prefix : " " ) + oModel .
data [ i ]. STEXT ,
58 expanded : false
59 }) ;
60
61 if ( oModel . data [ i ]. LEVEL !== " 1 " ) {
62 var oParent = this . getView () . byId ( " Node_ " + oModel . data [ i ]. PUP
);
63 oParent . addNode ( oNode ) ;
64 }
65
66 else
67 oTree . addNode ( oNode ) ;
68 }
69
70 }
71
72 },
73
74 uploadTree : function ( oTree , sIdValue , oLayout ) {
75 jQuery . ajax ({
76 url : " https :// j c o t f p 1 9 4 0 5 2 8 8 2 1 t r i a l . hanatrial . ondemand . com /
jcotf / Contro llerSe rvlet " ,
77 type : POST ,
78 crossDomain : true ,
79 data : {
80 functionname : " Z _ R F C _ H R _ R E A D _ O R G _ S T R U C T U R E " ,
81 objecttype : " O " ,
82 objectid : sIdValue ,
83 evaluationpath : "O -S - P "
84 },
85 success : jQuery . proxy ( function ( result ) {
86 sap . ui . commons . MessageBox . alert ( " Tabla subida con exito !
");
87 this . createTree ( oTree , sIdValue , oLayout ) ;
88 } , this ) ,
89 error : function () { sap . ui . commons . MessageBox . alert ( " Error
subiendo tabla . " ) ; }

118
90 }) ;
91 }
92 }) ;

119
D.12. tree.view.js
1 sap . ui . jsview ( " orgStructureView . tree " , {
2 get Contro llerNa me : function () {
3 return " orgStructureView . tree " ;
4 },
5
6 createContent : function ( oController ) {
7 var oLayout = new sap . ui . commons . layout . MatrixLayout ( this . createId ( "
layout " ) , {
8 layoutFixed : false ,
9 columns : 2 ,
10 width : " 100 % "
11 }) ;
12
13 var oIdLabel = new sap . ui . commons . Label ({
14 text : " Introduzca la ID del nodo raiz que desee ver ( tipo O ) : "
15 }) ;
16
17 var oIdValue = new sap . ui . commons . TextField ( this . createId ( " textfield
" ) ,{
18 value : ,
19 width : 20 em ,
20
21 press1 : [ oController . onLiveChange , oController ] ,
22 liveChange : function ( oEvent ) {
23 var searchBtn = oController . getView () . byId ( " searchbutton " ) ;
24 if ( this . getLiveValue () === " " ) {
25 searchBtn . setEnabled ( false ) ;
26 } else {
27 searchBtn . setEnabled ( true ) ;
28 }
29 }
30 }) ;
31
32 var oIdButton = new sap . ui . commons . Button ( this . createId ( "
searchbutton " ) ,{
33 text : " Search " ,
34 enabled : false ,
35 press : function ( event ) {
36 var oIdValue = oController . getView () . byId ( " textfield " ) ;
37 var sIdValue = oIdValue . getDomRef () . value ;
38 var oTree = oController . getView () . byId ( " tree " ) ;
39 var oLayout = oController . getView () . byId ( " layout " ) ;
40
41 oController . createTree ( oTree , sIdValue , oLayout ) ;
42 }
43 }) ;
44

120
45 oRow = new sap . ui . commons . layout . MatrixLayoutRow ({
46 cells : [
47 new sap . ui . commons . layout . MatrixLayoutCell ({
48 // backgroundDesign : sap . ui . commons . layout .
BackgroundDesign . Plain ,
49 content : new sap . ui . commons . layout . HorizontalLayout ( "
treeViewHorizontalLayout ", {
50 content : [ oIdLabel , oIdValue . addStyleClass ( "
myT extFie ldMarg in " ) , oIdButton ]
51 })
52 }) . addStyleClass ( " myWhiteCellStyle " ) ,
53 new sap . ui . commons . layout . MatrixLayoutCell ({
54 // backgroundDesign : sap . ui . commons . layout .
BackgroundDesign . Plain
55 }) . addStyleClass ( " myWhiteCellStyle " ) ]
56 }) ;
57
58 oLayout . addRow ( oRow ) ;
59
60 var oTree = new sap . ui . commons . Tree ( this . createId ( " tree " ) ) ;
61
62 oLayout . createRow ( new sap . ui . commons . layout . MatrixLayoutCell ({
63 content : oTree ,
64 colSpan : 2
65 }) ) ;
66
67 return oLayout ;
68 }
69
70 }) ;

121
D.13. model.json.xsjs
1 // **** Example for basic REQUEST RESPONSE handling
2 var paramName ; var paramValue ; var headerName ; var headerValue ; var
contentType ;
3
4 // Implementation of GET call
5 function handleGet () {
6 paramName = $ . request . parameters [0]. name ;
7 paramValue = $ . request . parameters [0]. value ;
8
9 if ( paramName === tablename ) {
10 var data = [];
11 var metadata = [];
12 var i ;
13 var result ;
14 var stmt ;
15 if ( paramValue . match (/\ bPH \ d +\ b / g ) !== null ) {
16 stmt = " SELECT FPBEG , FPEND , BUKRS , WERKS , ENAME , RELID , CDSEQ FROM \"
N E O _ 2 Y R C M 3 I 3 B M 6 5 X 2 E P A L 4 2 I E L V B \".\" " + paramValue + " \" " ;
17 stmt = stmt + " INNER JOIN \" N E O _ 2 Y R C M 3 I 3 B M 6 5 X 2 E P A L 4 2 I E L V B \".\"
PA0001 \" " ;
18 stmt = stmt + " ON \" " + paramValue + " \". PERNR = \" PA0001 \". PERNR
";
19 } else {
20 stmt = " SELECT * FROM \" N E O _ 2 Y R C M 3 I 3 B M 6 5 X 2 E P A L 4 2 I E L V B \".\" " +
paramValue + " \" " ;
21 }
22 var conn = $ . db . getConnection () ;
23 var pstmt = conn . prepareStatement ( stmt ) ;
24 var rs = pstmt . executeQuery () ;
25 var rsmd = pstmt . getMetaData () ;
26 var numColumns = rsmd . getColumnCount () ;
27 // Save metadata
28
29 var row ;
30 for ( i = 1; i <= numColumns ; i ++) {
31 metadata . push ( rsmd . getColumnLabel ( i ) ) ;
32 }
33 // Save data
34
35 while ( rs . next () ) {
36 row = {};
37 for ( i = 1; i <= numColumns ; i ++) {
38 row [ metadata [i -1]] = rs . getString ( i ) ;
39 }
40 data . push ( row ) ;
41 }
42 $ . response . contentType = " application / json " ;

122
43 // $ . response . status = $ . net . http . CREATED ;
44 rs . close () ;
45 pstmt . close () ;
46 conn . close () ;
47 result = {
48 " metadata " : metadata ,
49 " data " : data
50 };
51 return result ;
52 }
53 return { " myResult " : " Wrong parameters " };
54 }
55 // Check Content type headers and parameters
56 function validateInput () {
57 var i ; var j ;
58 // Check content - type is application / json
59 contentType = $ . request . contentType ;
60 if ( contentType === null || contentType . startsWith ( " application / json "
) === false ) {
61 $ . response . status = $ . net . http . I N T E R N A L _ S E R V E R _ E R R O R ;
62 $ . response . setBody ( " Wrong content type request use application / json "
);
63 return false ;
64 }
65 // Extract parameters and process them
66 for ( i = 0; i < $ . request . parameters . length ; ++ i ) {
67 paramName = $ . request . parameters [ i ]. name ;
68 paramValue = $ . request . parameters [ i ]. value ;
69 if ( paramName !== tablename ) {
70 return false ;
71 }
72 }
73 // Extract headers and process them
74 for ( j = 0; j < $ . request . headers . length ; ++ j ) {
75 headerName = $ . request . headers [ j ]. name ;
76 headerValue = $ . request . headers [ j ]. value ;
77 // Add logic
78 }
79 return true ;
80 }
81 // Request process
82 function processRequest () {
83 if ( validateInput () ) {
84 try {
85 switch ( $ . request . method ) {
86 // Handle your POST calls here
87 case $ . net . http . GET :
88 $ . response . setBody ( JSON . stringify ( handleGet () ) ) ;
89 break ;

123
90 // Handle your other methods : PUT , DELETE
91 default :
92 $ . response . status = $ . net . http . ME TH OD _N OT _A LL OW ED ;
93 $ . response . setBody ( " Wrong request method " ) ;
94 break ;
95 }
96 $ . response . contentType = " application / json " ;
97 } catch ( e ) {
98 $ . response . status = $ . net . http . I N T E R N A L _ S E R V E R _ E R R O R ;
99 $ . response . setBody ( " Failed to execute action : " + e . toString () ) ;
100 }
101 }
102 }
103 // Call request processing
104 processRequest () ;

124

You might also like