GeneXus for SAP Systems Car Parts: SAP ERP Connector

Unofficial Content

SAP ERP Connector

SAP ERP Introducción

SAP ERP es un Enterprise Resource Planner creado por SAP.

Alguno de los módulos más conocidos del SAP ERP son, el módulo financiero contable, control de gastos, gestión de materiales, ventas y distribución, gestión de planta, entre otros.

Cada módulo de SAP ERP contiene entidades que representan los objetos de negocios o Business Objects (BO) del ERP, definiendo su estructura y las operaciones que se pueden realizar con ellas, respetando las reglas de negocios de cada una, estas se conocen con el nombre de BAPIs (o Business Object API).

SAP cuenta con un repositorio para estos objetos de negocio que recibe el nombre de Business Object Repository (BOR)

Un ejemplo de un Business Object podría ser Material en el módulo de Gestión de Materiales o Sales Order en el módulo de ventas y distribución.

Un método de la BAPI Material, sería GetDetail() que permite obtener la información detallada de un material en particular.

SAP ERP Connector de GeneXus for SAP Systems permite explorar el Business Object Repository, accediendo a todas las BAPIs publicadas y así poder importar los métodos para que sean llamados desde GeneXus permitiendo una integración entre GeneXus y SAP ERP

TIP: En SAP se pueden crear objetos y funciones personalizadas. Por convención estos elementos se nombran prefijandolos con una Z, para diferenciarlos de los elementos estándar. En el caso de las BAPIs, los administradores de un sistema SAP, pueden haber creado ZBAPIs para implementar una personalización en particular.

Las ZBAPIs también pueden ser incluidas en el Business Object Repository y por ende ser accesibles por GeneXus for SAP System ERP Connector.

 

Importando una BAPI

Para Explorar/Importar las BAPIs disponibles en un sistema SAP, diríjase a Tools -> Application Integration-> SAP BAPI IMPORT.

image_2019517211512_1_png

Esta opción presenta un diálogo, donde se pueden ingresar los datos de conexión del sistema  SAP ERP a explorar.

image_2019517211529_1_png

Los valores a ser ingresados en este diálogo se encuentran en la configuración de la cuenta de la interfaz del ERP, llamada SAP GUI.

Se puede probar si la conexión está disponible con el botón ‘Test Connection’. Notificará si la conexión fue exitosa o no.

Para comenzar a explorar el BOR oprima el botón OK.

TIP: Tener más de una cierta cantidad de intentos fallidos de ingreso a SAP, bloqueará la cuenta. Cada sistema puede tener una cantidad diferente de máximos de intentos fallidos.

La prueba de conexión también realiza un intento de ingreso.

A continuación se detallan los campos a completar.

Connection Name

 Nombre que recibirá la conexión

Application Server 

 Dirección del servidor SAP ERP al que desea conectarse

Instance Number

 Número de instancia del servidor

System ID

 Identificador del Sistema SAP a conectarse.

Client Number

 Número de mandante (Entorno de ejecución)

Router String

 Se especifican las estaciones de una conexión entre dos anfitriones

Use SAPGUI

 Se inicia SAPGUI (interfaz de SAP) para mostrar el resultado de la conexión

Language

 Lenguaje utilizado en la conexión.

User Name

 Nombre de usuario de la cuenta SAP

Password

 Contraseña de la cuenta SAP

Una vez ingresados los datos de conexión al ERP SAP, seleccione OK.

Podrá visualizar en el formulario que se ha cargado, el Business Object Repository en el panel izquierdo, mostrando todos los Business Objects dentro de un árbol jerárquico.

image_2019517211757_1_png

Cada Business Object se puede expandir para ver los métodos correspondientes.

image_2019517211822_1_png

Si se selecciona una BAPI, en el panel derecho podrá ver información sobre la misma, junto con los parámetros requeridos y documentación de cómo realizar la llamada al método.

image_2019517211846_1_png

Seleccionando el Tab Info (Method) verá la opción de hacer un Test en Runtime, que permitirá probar el funcionamiento del método en tiempo de ejecución, para esto se debe completar los parámetros solicitados.

image_201951721193_1_png

Para esta demo diríjase a la categoría Sales and Distribution->Sales Organization y seleccione dentro del objeto Sales Order el método CreateFromDat2.

Este método permitirá crear una Sales Order, cumpliendo con el requerimiento solicitado de que el sistema cree órdenes de venta en SAP.

Una vez seleccionada la BAPI haga click en import para incorporarla en su KB.

Se puede importar más de una BAPI a la vez.

image_2019517211921_1_png

Una vez seleccionadas las BAPIs a importar, se muestra un listado de las mismas y se puede configurar un prefijo para nombrar los objetos.

Las BAPIs serán importadas al módulo especificado.

Haga click en Confirm para completar el proceso de importación.

image_2019517211941_1_png

Una vez importadas las BAPIs cierre el ERP Connector y diríjase al KB Explorer.

image_201951721208_1_png

Se puede ver que GeneXus ha generado de forma automática objetos, una vez importada la BAPI.

Para cada parámetro del método se ha generado un Structured Data Type (SDT)

Se generan 2 objetos externos:

  • GXEnterpriseSessionManager (gestiona la conexión al ERP)
  • sapCustomerOrder (representa al Business Object Sales Order que fue importado)

image_2019517212033_1_png

El objeto GXEnterpriseSessionManager define los datos de sesión para establecer la conexión con el ERP SAP.

Se puede observar que los atributos son los mismos que se deben ingresar cuando se explora el árbol de BAPIs.

 REF: DemoCarPartsXpz4.xpz 
 Contenido: Objetos importados con ERP Connector

Para manejar los datos de sesión de SAP adecuadamente, se puede definir un Structured Data Type que permita almacenar, en una variable, los datos de sesión para la conexión con el ERP.

Para crear el Structured Data Type cree un folder llamado ‘SAPIntegration’ haciendo click derecho en el módulo Root y seleccione New->Folder.

Una vez que haya creado la carpeta, haga click derecho y seleccione New->Object y cree el Structured Data Type llamado ‘SAPSessionData’

image_2019517212129_1_png

Defina los siguientes atributos:

Nombre Atributo

Tipo

SAPSessionAppServer

Character (512)

SAPSessionInstanceNumber

Character (10)

SAPSessionRouterString

Character (512)

SAPSessionClientNumber

Character (10)

SAPSessionSystemId

Character (10)

SAPSessionSessionName

Character (40)

SAPSessionSAPGUI

Character (10)

SAPSessionLanguage

Character (3)

SAPSessionUserName

Character (40)

SAPSessionPassword

Character (40)

El Structured Data Type debe quedar definido de la siguiente manera:

image_2019517212220_1_png

De esta manera queda definida la estructura permitiendo almacenar información sobre las sesiones SAP que se manejan desde la aplicación.

En la siguiente demo se considera que se va a utilizar una sola sesión de SAP. En este caso, las conexiones con el sistema SAP se realizan al mismo servidor y con un único juego de credenciales.

TIP: En un escenario real, los datos del Server SAP ERP es recomendable que se encuentren en alguna tabla para facilitar el mantenimiento y permitir varios servers a los cuales conectar la aplicación. Del mismo modo, cada usuario tendrá sus credenciales para acceder a SAP ERP.

En este caso, por ser una POC,  se cargará la sesión a través de un Objeto Data Provider.

Haga click derecho en la carpeta ‘SAPIntegration’ y seleccione New->Object.

Seleccione Data Provider en la categoría Common y nombre al objeto ‘SAPSessionDataDP’

image_201951721233_1_png

Una vez creado, en KB Explorer arrastre el icono del SDT ‘SAPSessionData’ y suelte el elemento en el código del Data Provider ‘SAPSessionDataDP’.

image_2019517212319_1_png

De esta forma, automáticamente quedan especificados los atributos del SDT dentro del Data Provider.

Ingrese la configuración sistema SAP incluida las credenciales para almacenar la información a utilizar en futuras conexiones.

image_2019517212338_1_png

Haga click en el Icono del Diskette image_2019517212412_1_png o presione Ctrl+S para salvar el progreso realizado.

TIP: Es altamente recomendable implementar medidas de seguridad para el manejo de credenciales, así como también toda la información que considere sensible. Esto lo puede hacer encriptando y desencriptando la misma, donde corresponda, utilizando las funciones GeneXus encrypt64() y decrypt64().

Hasta el momento se ha definido un Structured Data Type donde la sesión de SAP es almacenada mediante la carga de datos realizada por un Data Provider.

Otro punto importante, es resolver cómo gestionar los datos de la conexión al Sistema SAP a lo largo de la aplicación de forma que sea accesible por todos los objetos GeneXus que requieran utilizarlos.

Hay varios enfoques para resolver el punto anterior, una de estas opciones es utilizar una variable de tipo WebSession, que permite almacenar información en el servidor. De esta forma se puede guardar la información de la conexión con el sistema SAP. Esta información estará disponible para todos los objetos GeneXus que necesiten hacer uso de la misma.

Para esta demo se utilizara este enfoque de almacenar la información en WebSession.

La información de la conexión con SAP se debe almacenar en la WebSession al inicio de las aplicaciones, tanto la aplicación web como la aplicación para Smart Devices.

El procedimiento es similar para ambos casos.

Empiece por la aplicación para Smart Devices.

Seleccione el objeto de inicio de la aplicación Smart Devices llamado ‘DemoCarPartsSD’ y especifique el siguiente código en el evento ‘Refresh’.

image_2019517212438_1_png

 &SapSessionData = SapSessionDataDP()

Define automáticamente una variable del tipo SAPSessionData (inferido por el nombre) y carga en la variable la información especificada en el Data Provider SAPSessionDataDP.

 &WebSession.Set('sapserver', &SapSessionData.ToJson())

Se define automáticamente una variable del tipo WebSession (inferido por el nombre) y se ejecuta el método Set, donde se almacena bajo el identificador 'sapserver',  la información de la variable, serializada a través del método ToJson().

De manera análoga se debe cargar la información en la sesión para el inicio de la aplicación web.

En el evento ‘Start’ del objeto ‘FioriLaunchpad’ especifique el código anterior.

image_2019517212628_1_png

Haga click en el Icono del Diskette image_2019517212648_1_png o presione Ctrl+S para salvar el progreso realizado.


En el siguiente paso verá cómo generar un objeto Procedure que establezca la conexión con el sistema SAP, utilizando la información de la cuenta SAP almacenada en la WebSession.

De esta forma cada vez que necesite conectarse al sistema SAP para llamar a una BAPI, podrá llamar al Procedure definido a continuación.

Dentro de la carpeta ‘SAPIntegration’ cree un objeto Procedure y llámelo ‘SAPConnect’.

Puede crear el objeto haciendo click derecho en la carpeta y seleccionando New->Create.

image_2019517212711_1_png

En el selector de Rules defina el siguiente parámetro de salida.

 parm(out:&GXEnterpriseSessionManager);

image_2019517212739_1_png

Este parámetro es una variable del tipo del objeto externo generado (GxEnterpriseSessionManager) al importar la BAPI, la variable se definirá de forma automática de este tipo de datos.

Haga click en el Icono del Diskette image_2019517212811_1_png o presione Ctrl+S para salvar el progreso realizado.

En la pestaña de Source escriba el siguiente código.

 &SAPSessionData.FromJson(&WebSession.Get('sapserver'))

 &GXEnterpriseSessionManager.AppServer      = &SAPSessionData.SAPSessionAppServer
 &GXEnterpriseSessionManager.ClientNumber   = &SAPSessionData.SAPSessionClientNumber
 &GXEnterpriseSessionManager.RouterString   = &SAPSessionData.SAPSessionRouterString
 &GXEnterpriseSessionManager.SystemId       = &SAPSessionData.SAPSessionSystemId
 &GXEnterpriseSessionManager.InstanceNumber =  &SAPSessionData.SAPSessionInstanceNumber
 &GXEnterpriseSessionManager.SessionName    = &SAPSessionData.SAPSessionSessionName
 &GXEnterpriseSessionManager.UserName       = &SAPSessionData.SAPSessionUserName
 &GXEnterpriseSessionManager.Password       = &SAPSessionData.SAPSessionPassword

 &GXEnterpriseSessionManager.Connect()

Utilizando una variable del tipo WebSession se obtiene la sesión que se guardó al inicio de la aplicación, llamada ‘sapserver’.

En esta sesión se carga una variable del tipo SAPSessionData utilizando el método fromJson.

Luego asignamos a una variable del tipo GXEnterpriseSessionManager los datos obtenidos de la sesión.

Se invoca al método Connect para iniciar la conexión con el Sistema SAP

image_2019517212957_1_png

Haga click en el Icono del Diskette image_2019517213026_1_png o presione Ctrl+S para salvar el progreso realizado.

Automáticamente se definen las variables SAPSessionData y WebSession inferidas por GeneXus.

Ha definido un procedimiento que permite conectarse al Sistema SAP utilizando credenciales que se guardan en la sesión del servidor y puede ser referenciado por cualquier objeto definido.

Ya ha definido las bases para poder manejar la conexión con el ERP:

  • Importación de la BAPI a utilizar, generando el objeto externo que establece la conexión.
  • Definición de la estructura para la gestión de credenciales para la conexión.
  • Gestión de las credenciales para establecer una sesión de conexión con el ERP.

Ahora verá los pasos necesarios para ingresar el registro de Sales Order en la aplicación.

 REF: DemoCarPartsXpz5.xpz
 Contenido: EO: GXEnterpriseSessionManager y carpeta SAPIntegration con objetos para     conectarse a SAP ERP)

 

 REF: DemoCarPartsXpz5All.xpz  
 Contenido: Toda la KB hasta aquí

Creación de Sales Order en SAP ERP desde GeneXus

En esta sección se cubrirá como crear una Sales Order en SAP ERP haciendo uso de la BAPI importada en el paso anterior.

La estrategia elegida para implementar este requerimiento es utilizar una tabla auxiliar (o de redundancia controlada) a efectos de registrar los datos de la Sales Order ingresada en esta tabla, creada desde el sistema desarrollado en GeneXus. Luego de esto el pedido es enviado a SAP ERP y se registra en la tabla de redundancia, el número de pedido devuelto por SAP.

Esto significa que si un usuario de la aplicación ingresa una Sales Order y por algún motivo falla el envío de la misma al sistema SAP ERP (fallo en la conexión, downtime del servidor, etc) el registro de la Sales Order queda almacenado en las tablas del sistema desarrollado con GeneXus para que no se pierda la información.

Procedimiento de creación de la Sales Order en SAP ERP

Comenzaremos definiendo el objeto que ingresa el registro en SAP ERP.

Cree un nuevo Objeto de tipo procedimiento (dentro del folder SAPIntegration ) y llámelo ‘CreateSOSAP’.

image_201951721331_1_png

En la pestaña de Rules defina los siguientes parámetros de entrada y salida

  parm(in:&SalesOrderId, out:&SalesOrderSAPNumber, out:&BAPIRET2);

El parámetro SalesOrderId contiene el identificador de la SalesOrder grabada en la tabla de redundancia (del sistema y la DB creada por GeneXus) a ser ingresada en SAP ERP.

El parámetro SAPSONumber retorna el número asignado por SAP ERP a la SalesOrder creada en el ERP.

La estructura BAPIRET2 contiene el retorno de la operación en SAP, indicando si se realizó exitosamente o si tuvo errores.

image_2019517213349_1_png

Los tipos de las variables SalesOrderId, SalesOrderSAPNumber y BAPIRET2 se infieren automáticamente por GeneXus.

Marque el tipo BAPIRET2 como una colección, ya que SAP ERP puede retornar más de un mensaje al intentar crear el pedido.

image_201951810429_1_png

En la pestaña Source comience a especificar el código con la siguiente línea

  SAPConnect(&GxEnterpriseSessionManager)

image_2019518104236_1_png

Este es el procedimiento definido anteriormente que se encarga de gestionar a través de la sesión, la conexión con el sistema SAP utilizando un juego de credenciales.

Como resultado retorna la sesión y si se pudo establecer la conexión con el ERP.

Una vez llamado el método SAPConnect se trabajará con la variable resultado (GxEnterpriseSessionManager) a lo largo del procedure.

La variable GxEnterpriseSessionManager se define de forma automática.

Los atributos ErrorCode y ErrorMessage almacenados dentro de la variable GxEnterpriseSessionManager contienen la información resultante de intentar establecer una conexión. Si la conexión fue exitosa el atributo ErrorCode se retorna vacío.

ErrorMessage es una descripción del error.

De esta manera cada vez que se realiza una llamada externa a una BAPI se debe establecer una conexión al sistema utilizando el método Connect.

A continuación especifique el siguiente bloque condicional

  if &GXEnterpriseSessionManager.ErrorCode.IsEmpty()

  endif

Dentro de este bloque definirá las operaciones a realizar en el caso de una conexión exitosa, para el caso contrario posteriormente se incluirá una clausula else

Comenzará a especificar código haciendo uso de las estructuras generadas en la importación de la BAPI.

Por más información sobre las estructuras de la BAPI utilizada puede referirse a la documentación presente en el explorador de BAPIs de SAP ERP Connector.

Escriba el siguiente código dentro del bloque if/endif.

  for each
  where SalesOrderId = &SalesOrderId
       //     * Sales Order Header
       &BAPISDHD1.DOC_TYPE   = 'TA'
       &BAPISDHD1.SALES_ORG  = 'UY01'
       &BAPISDHD1.SALES_DIST = ''
       &BAPISDHD1.DIVISION   = '01'
       &BAPISDHD1.DISTR_CHAN = '01'
       &BAPISDHD1.DOC_DATE   = SalesOrderDate 

       //     * Sales Order Partner
       &BAPIPARNRRow.PARTN_ROLE = 'AG'
       &BAPIPARNRRow.PARTN_NUMB = CustomerId
       &BAPIPARNR.Add(&BAPIPARNRRow)

       //     * Sales Order Item
       &BAPISDITMRow.ITM_NUMBER = 10
       &BAPISDITMRow.MATERIAL   = ProductId
       &BAPISDITMRow.TARGET_QTY = SalesOrderProductQty
       &BAPISDITMRow.TARGET_VAL = '0'
       &BAPISDITM.Add(&BAPISDITMRow)      

       &BAPISCHDLRow.ITM_NUMBER = 10
       &BAPISCHDLRow.REQ_QTY    = SalesOrderProductQty
       &BAPISCHDL.Add(&BAPISCHDLRow)

  Endfor


A continuación se detalla cada sección para dar una introducción sobre el manejo de la BAPI para la creación de SalesOrder y algunos conceptos GeneXus.

  for each
  where SalesOrderId = &SalesOrderId
    . . . . .

  Endfor

El bloque for each /Endfor es la estructura utilizada en GeneXus para obtener un conjunto de datos de la Base de Datos. El developer solamente menciona los atributos que desea utilizar y GeneXus automáticamente infiere las tablas y/o Joins necesarios para obtenerlos.

Como se puede apreciar, el where se utiliza para indicar que los datos de la orden que se desea utilizar, son los de la que tenga el SalesOrderId recibido como parámetro.

TIP: Si en lugar de haber recibido dicho valor en una variable se hiciera directamente sobre el atributo, podría haberse omitido el where y GeneXus infiere la condición de filtro de forma automática.

  parm(in:SalesOrderId, ...

  for each
    . . . . .

  Endfor

Cabezal de la Sales Order

  //     * Sales Order Header
  &BAPISDHD1.DOC_TYPE   = 'TA'
  &BAPISDHD1.SALES_ORG  = 'UY01'
  &BAPISDHD1.SALES_DIST = ''
  &BAPISDHD1.DIVISION   = '01'
  &BAPISDHD1.DISTR_CHAN = '01'&APISDHD1.DOC_DATE   = SalesOrderDate

La estructura BAPISDHD1 maneja la información referente al cabezal de una Sales Order.  Si bien se puede asignar valor a muchos atributos para el cabezal de una Sales Order, en este caso se asignan los mínimos necesarios para poder crear un pedido. Para este caso se asignan datos estáticos en ciertos atributos, esto depende de la parametrización del Sistema SAP ERP implantado.

La variable &BAPISDHD1 de tipo BAPISDHD1 se define de forma automática.

Para la fecha del documento en SAP, se asigna la fecha que GeneXus obtendrá de la tabla redundante, donde ya se grabó el pedido.

Interlocutor comercial de la Sales Order

  //     * Sales Order Partner
  &BAPIPARNRRow.PARTN_ROLE = 'AG'
  &BAPIPARNRRow.PARTN_NUMB = CustomerId
  &BAPIPARNR.Add(&BAPIPARNRRow)

Aquí se carga la estructura de partners, notar que la estructura (BAPIPARNR) es una colección por lo que debe definir una variable  &BAPIPARNR de tipo BAPIPARNR e indicar que es una colección en la pestaña de variables.

Al ser una colección se pueden cargar varios registros (cada registro tendrá un rol, esto es destinatario de mercancía, responsable de pago, etc., en este caso solo se cargará un registro PARTN_ROLE = 'AG'), para esto se define la variable &BAPIPARNRRow de tipo BAPIPARNR sin indicar que el mismo es una colección.

En el número de partner se asocia el número de cliente que GeneXus obtendrá de la tabla redundante, donde ya se grabó el pedido.

Líneas de la Sales Order

  //     * Sales Order Item
  &BAPISDITMRow.ITM_NUMBER = 10
  &BAPISDITMRow.MATERIAL   = ProductId
  &BAPISDITMRow.TARGET_QTY = SalesOrderProductQty
  &BAPISDITMRow.TARGET_VAL = '0'
  &BAPISDITM.Add(&BAPISDITMRow)

  &BAPISCHDLRow.ITM_NUMBER = 10
  &BAPISCHDLRow.REQ_QTY    = SalesOrderProductQty
  &BAPISCHDL.Add(&BAPISCHDLRow)

En esta sección se realiza la carga de los productos o ítems a ingresar en la SalesOrder (en ese caso de la POC, solamente un producto).

Cada ítem tiene un número de línea o posición en SAP ERP, como en este caso se ingresará solamente una línea, se numera de forma fija con el valor 10.

En esta sección se deben cargar datos en dos colecciones:
- &BAPISDITM como una colección del tipo BAPISDITM
- &BAPISCHDL como una colección del tipo BAPISCHDL

Defina una variable &BAPISDITMRow de tipo BAPISDITM para ingresar en la colección &BAPISDITM.

Aquí se almacena el identificador de producto y la cantidad requerida, que GeneXus obtendrá de la tabla redundante, donde ya se grabó el pedido.

Defina una variable &BAPISCHDLRow de tipo BAPISDITM para ingresar en la colección &BAPISCHDL.

Esta colección se utiliza en SAP ERP para poder indicar diferentes valores, relacionado a la entrega, a cantidades parciales de la cantidad total pedida (P.e. si se pidieron 12 unidades de un producto, se puede indicar una fecha de entrega para 6 de ellos y otra fecha para las restantes 6 unidades). Por lo tanto cada línea de esta estructura debe estar relacionada a una línea del pedido (de la estructura &BAPISDITM) a través de los campos &BAPISDITMRow.ITM_NUMBER y &BAPISCHDLRow.ITM_NUMBER (en este caso, donde utilizamos solo una linea por Sales Order, a ambos campos se les asigna el mismo numero de linea = 10 y también se crea solamente una linea de entrega por la cantidad total pedida)

Creación de la Sales Order

Una vez especificado el código anterior, se tiene completa la carga de datos mínima necesaria en las estructuras, para dar de alta una SalesOrder en SAP ERP.

Es momento de realizar la llamada a la BAPI importada para crear el registro de SalesOrder en SAP ERP.

Antes de ejecutar la llamada a la BAPI se debe ejecutar el método TransactionBegin para comunicarle al Sistema SAP del inicio de la transacción.  A su vez, luego de realizar la llamada a la BAPI  pasando los parámetros necesarios, se debe ejecutar el método TransactionCommit para notificar al sistema SAP que aplique los cambios realizados. Ambos son métodos del External Object GXEnterpriseSessionManager.

Escriba el siguiente código luego del bloque for each/endfor del procedimiento.

  &GXEnterpriseSessionManager.TransactionBegin()      

  &sapCustomerOrder.CREATEFROMDAT2(&SALESDOCUMENTIN, &BAPISDHD1, &BAPISDHD1X,&BAPI_SENDER,                                 
                                                                       &BINARY_RELATIONSHIPTYPE, &INT_NUMBER_ASSIGNMENT,
                                                                       &BEHAVE_WHEN_ERROR, &BAPISDLS, &TESTRUN, &CONVERT, &BAPIRET2, &BAPISDITM,
                                                                       &BAPISDITMX, &BAPIPARNR, &BAPISCHDL, &BAPISCHDLX, &BAPICOND, &BAPICONDX,
                                                                       &BAPICUCFG, &BAPICUINS, &BAPICUPRT, &BAPICUVAL, &BAPICUBLB, &BAPICUVK,
                                                                       &BAPICUREF, &BAPICCARD, &BAPISDTEXT, &BAPISDKEY, &BAPIPAREX, &BAPIADDR1)

  &GXEnterpriseSessionManager.TransactionCommit()

&sapCustomerOrder.CREATEFROMDAT2(...) es la llamada a BAPI de SAP ERP.

La variable &sapCustomerOrder, de tipo sapCustomerOrder (nombre del objeto externo generado al importar la BAPI) se define de forma automática inferido por el nombre.

A continuación se detallan las variables pasadas por parámetro y sus tipos, las cuales deberá definir.

Nombre Variable

Tipo Variable

Colección

SALESDOCUMENTIN

Character(20)

 

BAPISDHD1

BAPISDHD1

 

BAPISDHD1X

BAPISDHD1X

 

BAPI_SENDER

BAPI_SENDER

 

BINARY_RELATIONSHIPTYPE

Character(8)

 

INT_NUMBER_ASSIGNMENT

Character(2)

 

BEHAVE_WHEN_ERROR

Character(2)

 

BAPISDLS

BAPISDLS

 

TESTRUN

Character(2)

 

CONVERT

Character(2)

 

BAPIRET2

BAPIRET2

X

BAPISDITM

BAPISDITM

X

BAPISDITMX

BAPISDITMX

X

BAPIPARNR

BAPIPARNR

X

BAPISCHDL

BAPISCHDL

X

BAPISCHDLX

BAPISCHDLX

X

BAPICOND

BAPICOND

X

BAPICONDX

BAPICONDX

X

BAPICUCFG

BAPICUCFG

X

BAPICUINS

BAPICUINS

X

BAPICUPRT

BAPICUPRT

X

BAPICUVAL

BAPICUVAL

X

BAPICUBLB

BAPICUBLB

X

BAPICUVK

BAPICUVK

X

BAPICUREF

BAPICUREF

X

BAPICCARD

BAPICCARD

X

BAPISDTEXT

BAPISDTEXT

X

BAPISDKEY

BAPISDKEY

X

BAPIPAREX

BAPIPAREX

X

BAPIADDR1

BAPIADDR1

X

TIP: una forma fácil de definir todas estas variables, es seleccionarlas todas desde el módulo Enterprise en el KBExplorer y arrastrarlas sobre la solapa Variables del procedure CreateSOSAP (click sobre la primera y Ctrl+click sobre la última, arrastrar y soltar), luego de esto, solo resta marcar como Collection las que se indican en la lista anterior.

Resultado del llamado a la BAPI

Una vez realizada la llamada a la BAPI y luego de llamar al método TransactionCommit se debe analizar el resultado de la llamada a la BAPI, para esto se consulta los datos retornados en la variable &BAPIRET2 (que almacena el resultado de la ejecución de un método y su descripción)

  // Analyze the BAPI answer to obtain &SAPSONumber
  &SalesOrderSAPNumber.SetEmpty()

  for &BAPIRET2Row in &BAPIRET2
       if &BAPIRET2Row.TYPE <> 'E' and &BAPIRET2Row.TYPE <> 'A'
             // Save the document number created
             &SalesOrderSAPNumber = &BAPIRET2Row.MESSAGE_V2
       endif
  endfor

Se inicializa la &SalesOrderSAPNumber (Empty) para fijar o no un valor, dependiendo del resultado de la llamada.

Defina una variable &BAPIRET2Row de tipo BAPIRET2 para leer de la colección &BAPIRET2.

Al evaluar si el atributo TYPE es distinto de ‘E’ y de ‘A’ comprobamos que el resultado no haya sido de error (TYPE puede tener los valores S para Success, I para Info, W para Warning, A para Abort y E para Error)

Si el resultado no es erróneo, se asigna el atributo MESSAGE_V2 que contiene el número asignado por SAP para la orden creada.

En caso contrario, la variable &SalesOrderSAPNumber permanece vacía, indicando que no se pudo dar de alta la SalesOrder en SAP (y queda pendiente de poder ser reprocesada desde las tablas redundantes del sistema GeneXus).

Registrar el resultado de la llamada a la BAPI

Para concluir con el código del procedimiento, en caso que la creación haya sido exitosa, se asigna el número de Sales Order devuelto por SAP ERP, al pedido en la tabla redundante creada por GeneXus y se describe la cláusula  else del condicional

if &GXEnterpriseSessionManager.ErrorCode.IsEmpty() para definir las acciones a ejecutar en caso de que no se haya podido establecer la conexión con el sistema SAP.

  if &GXEnterpriseSessionManager.ErrorCode.IsEmpty()
       . . . . .

       if not &SalesOrderSAPNumber.IsEmpty()
             for each
             where SalesOrderId = &SalesOrderId
                    SalesOrderSAPNumber = &SalesOrderSAPNumber
             endfor
       endif
  else
       &BAPIRET2Row.NUMBER  = &GXEnterpriseSessionManager.ErrorCode
       &BAPIRET2Row.MESSAGE = &GXEnterpriseSessionManager.ErrorMessage
       &BAPIRET2.Add(&BAPIRET2Row)
  endif

Y aquí finaliza la especificación a realizar para crear una Sales Order en SAP utilizando la BAPI.

Haga click en el Icono del Diskette image_201951813750_1_png o presione Ctrl+S para salvar el progreso realizado.

A continuación se muestra como debería quedar especificado el código.

  SAPConnect(&GxEnterpriseSessionManager)

  if &GxEnterpriseSessionManager.ErrorCode.IsEmpty()
        for each
        where SalesOrderId = &SalesOrderId
                //      * Sales Order Header
                &BAPISDHD1.DOC_TYPE   = 'TA'
                &BAPISDHD1.SALES_ORG  = 'UY01'
                &BAPISDHD1.SALES_DIST = ''
                &BAPISDHD1.DIVISION   = '01'
                &BAPISDHD1.DISTR_CHAN = '01'
                &BAPISDHD1.DOC_DATE   = SalesOrderDate

                //      * Sales Order Partner
                &BAPIPARNRRow.PARTN_ROLE = 'AG'
                &BAPIPARNRRow.PARTN_NUMB = CustomerId
                &BAPIPARNR.Add(&BAPIPARNRRow)

                //      * Sales Order Item
                &BAPISDITMRow.ITM_NUMBER = 10
                &BAPISDITMRow.MATERIAL   = ProductId
                &BAPISDITMRow.TARGET_QTY = SalesOrderProductQty
                &BAPISDITMRow.TARGET_VAL = '0'
                &BAPISDITM.Add(&BAPISDITMRow)

                &BAPISCHDLRow.ITM_NUMBER = 10
                &BAPISCHDLRow.REQ_QTY    = SalesOrderProductQty
                &BAPISCHDL.Add(&BAPISCHDLRow)
        endfor

        &GXEnterpriseSessionManager.TransactionBegin()

        &sapCustomerOrder.CREATEFROMDAT2(&SALESDOCUMENTIN, &BAPISDHD1, &BAPISDHD1X, &BAPI_SENDER, &BINARY_RELATIONSHIPTYPE, &INT_NUMBER_ASSIGNMENT, &BEHAVE_WHEN_ERROR, &BAPISDLS, &TESTRUN, &CONVERT, &BAPIRET2, &BAPISDITM, &BAPISDITMX, &BAPIPARNR, &BAPISCHDL, &BAPISCHDLX, &BAPICOND, &BAPICONDX, &BAPICUCFG, &BAPICUINS, &BAPICUPRT, &BAPICUVAL, &BAPICUBLB, &BAPICUVK, &BAPICUREF, &BAPICCARD, &BAPISDTEXT, &BAPISDKEY, &BAPIPAREX, &BAPIADDR1)

        &GXEnterpriseSessionManager.TransactionCommit()

        // Analyze the BAPI answer to obtain &SAPSONumber
        &SalesOrderSAPNumber.SetEmpty()
        for &BAPIRET2Row in &BAPIRET2
                if &BAPIRET2Row.TYPE <> 'E' and &BAPIRET2Row.TYPE <> 'A'
                        // Save the document number created
                        &SalesOrderSAPNumber = &BAPIRET2Row.MESSAGE_V2
                endif
        endfor
        if not &SalesOrderSAPNumber.IsEmpty()
                for each
                where SalesOrderId = &SalesOrderId
                        SalesOrderSAPNumber = &SalesOrderSAPNumber
                endfor
        endif
  else
        &BAPIRET2Row.NUMBER  = &GXEnterpriseSessionManager.ErrorCode
        &BAPIRET2Row.MESSAGE = &GXEnterpriseSessionManager.ErrorMessage
        &BAPIRET2.Add(&BAPIRET2Row)
  endif

 

 REF: DemoCarPartsXpz6.xpz
 Contenido: Contiene el procedimiento CreateSOSAP 

 

Llamado al procedimiento de creación de Sales Order en SAP ERP

Una vez que contamos con el procedimiento que crea la Sales Order en SAP ERP a partir de los datos ingresados en la tabla de redundancia, resta solamente incorporar el llamado en los puntos necesarios y mostrar el resultado devuelto por SAP ERP.

Com pretendemos que esta redundancia sea totalmente controlada, este procedimiento lo invocaremos, de forma automática, inmediatamente luego de confirmar la grabación del pedido en la tabla de redundancia creada por GeneXus, desde un panel (Web Panel para Web o SD Panel para Mobile) que adicionalmente nos mostrará el resultado de la creación del pedido en SAP ERP.

Escribamos entonces primero estos dos paneles.

Web Panel de creación de Sales Order en SAP ERP

Cree un panel de nombre SOSAPSaveResultWeb en el folder SAPIntegration (botón derecho sobre el folder SAPIntegration y New/Object/Web/Web Panel).

Agregue una variable de tipo Image (Toolbox + Drag and drop del control Attribute/Variable, New Variable y nombrela ResultImage, esto influirá para ella, el tipo de datos Image. Al dar OK se agrega al webform).

Modifíquele las siguientes Properties a la variable:
Label Position: None
Horizontal Alignment: Center

image_2019528211522_1_png

Este panel recibirá como parámetro, el Id del pedido creado en la tabla de redundancia, por lo tanto agregue la regla (en la solapa Rules) parm(in:&SalesOrderId);

image_2019528211534_1_png

Finalmente, en la solapa Events, agregue el Evento Start con el siguiente código, con lo cual se llamará al procedimiento de creación de la Sales Order en SAP ERP y según el resultado muestra el número de pedido devuelto por SAP o el mensaje de error.
Según el resultado sea erróneo o no, se mostrará en la variable definida de tipo Image, un Icono que indique el tipo de resultado (en esta caso Image:OKSAP image_2019528211555_1_pngo Image:ErrorSAPimage_2019528211614_1_png)

 Event Start
       CreateSOSAP(&SalesOrderId, &SalesOrderSAPNumber, &BAPIRET2)
       if &SalesOrderSAPNumber.IsEmpty()
             &ResultImage.FromImage(Image:ErrorSAP)
             msg(format('SAP SO Error: %1', &BAPIRET2.Item(1).MESSAGE))
       else
             &ResultImage.FromImage(Image:OKSAP)
             msg(format('SAP SO nbr.created: %1', &SalesOrderSAPNumber))
       endif
 Endevent


Las variable &SalesOrderId, &SalesOrderSAPNumber y &BAPIRET2 se definen automáticamente, solamente hay que indicarle a &BAPIRET2 que es una Collection.

Panel for Smart Device de creación de Sales Order en SAP ERP

Cree un panel para Smart Devices de nombre SOSAPSaveResultSD en el folder SAPIntegration (botón derecho sobre el folder SAPIntegration y New/Object/Smart Devices/Panel for Smart Devices).

Al oprimir el botón Create, GeneXus automaticamente propone el selector de Templates para Fiori Mobile, en este caso no usaremos ninguno, ya que usaremos un panel muy sencillo, por lo tanto oprimir Skip.

Desde la toolbox, arrastramos sobre el form del panel un control de tipo Image y seleccionamos el que usemos como indicador de Sales Order creada correctamente (p.e. image_2019528211720_1_png). Hacemos lo mismo insertando debajo de este otro control de tipo Image con la imagen elegida para indicar algún error en la creación de la Sales Order (p.e. image_2019528211737_1_png).

A ambos controles (Imagenes) seteamos las siguientes propiedades:
Control Name = ImageOK / ImageError (según corresponda)
Visible = false
Invisible Mode = Collapse Space

Finalmente cree y arrastre a la parte superior del form, desde la toolbox, una variable de nombre ResultSAP, definida como VarChar de 100 y con las siguientes propiedades:
Readonly = True
Label Position = None

 

image_2019528211753_1_png

image_201952821182_1_png image_2019528211812_1_pngimage_2019528211822_1_png

En la solapa Events, elija desde el selector de Eventos el evento Refresh para insertarlo.

 

image_2019528211831_1_png

De forma análoga a como hizo en el Web Panel SOSAPSaveResultWeb, escriba el código para llamar al procedimiento de creación de la Sales Order en SAP y de acuerdo al resultado muestre el nro. de pedido creado en SAP o el error recibido.

 Event Refresh
   &SalesOrderId.FromString(&WebSession.Get('salesorderid'))
   CreateSOSAP(&SalesOrderId, &SalesOrderSAPNumber, &BAPIRET2)
   if &SalesOrderSAPNumber.IsEmpty()
      ImageOK.Visible = false
      ImageError.Visible = true
      &ResultSAP = format('SAP SO Error: %1', &BAPIRET2.Item(1).MESSAGE)
   else
      ImageError.Visible = false
      ImageOK.Visible = true
      &ResultSAP = format('SAP SO nbr.created: %1', &SalesOrderSAPNumber)
   endif
 Endevent

Las variable &SalesOrderId, &SalesOrderSAPNumber y &BAPIRET2 se definen automáticamente, solamente hay que indicarle a &BAPIRET2 que es una Collection.

En este caso, por la forma en que se invocara a este panel, el parámetro se pasará a través de una Web Session, por lo tanto, en esta línea es justamente donde se recupera ese valor.
&SalesOrderId.FromString(&WebSession.Get('salesorderid'))

 

 TIP: La variable WebSession se define de forma automática del tipo de datos del mismo nombre  y cuenta con los métodos Save y Get para guardar y recuperar valores en la session web, esto es posible utilizarlo aquí, ya que el evento Start de un panel SD OnLine en GeneXus, se ejecuta del lado del Server. La función FromString, se utiliza ya que los valores en la web session siempre se almacena en formato de texto y la variable &SalesOrderId es numérica, esta función convierte de Texto a Numérico.


Invocación de los paneles de creación de Sales Order en SAP ERP

Finalmente, resta agregar la llamada a los paneles de creación de la Sales Order en SAP ERP, recién detallados.

Para el caso web, simplemente agregamos una regla en la transacción SalesOrder, de tal forma que esta se dispare en caso de Insert de un nuevo registro (una nueva Sales Order) y luego que esta haya sido grabada en las tablas de redundancias (adicionalmente condicionamos que esta regla se dispare solamente si la transacción se ejecuta en un ambiente Web. Por lo tanto, en la solapa Rules de la transacción SalesOrder agregamos la siguiente regla:

 web
 {
       SOSAPSaveResultWeb(SalesOrderId) if Insert On AfterComplete;
 }


Para el caso Mobile, la llamada la realizaremos desde el objeto WorkWithDevicesSalesOrder creado por el pattern, inmediatamente luego de realizar el Insert.

image_2019528212027_1_png

Por lo tanto en el evento ‘Insert’, en la solapa Events del List dentro del Panel for Smart Devices WorkWithDevicesSalesOrder (que se encuentra relacionado a la transaccion SalesOrder), se agrega al código, la llamada al panel creado anteriormente.

Código del evento ‘Insert’ creado por el pattern:

 Event 'Insert'
   // Generated by FioriMobile Start - Template: FioriMobileListSubtitle
   WorkWithDevicesSalesOrder.SalesOrder.Detail.Insert()
   // Generated by FioriMobile End - Template: FioriMobileListSubtitle
 EndEvent

 

Se modifica, dejándolo como sigue:

 Event 'Insert'
   Composite
   // Generated by FioriMobile Start - Template: FioriMobileListSubtitle
   WorkWithDevicesSalesOrder.SalesOrder.Detail.Insert()
   // Generated by FioriMobile End - Template: FioriMobileListSubtitle
   SOSAPSaveResultSD.Call()
   endcomposite
 EndEvent

 

TIP: El bloque de código Composite-endcomposite agrupa código GeneXus, de tal forma que ese código se ejecuta secuencialmente hasta la última línea, excepto cuando ocurre algún error en alguna de las líneas. En cuyo caso la secuencia se ve interrumpida.


Como comentamos al momento de definir el panel que invoca la creación de la orden en SAP ERP, la forma de pasar el Id de la Sales Order creada en la tabla de redundancia, la cual tiene los datos a pasar a SAP ERP, es a través de la WebSession, ya que en el panel WorkWithDevicesSalesOrder, al momento de llamar a SOSAPSaveResultSD, ya no se cuenta con la Id de la orden recién creada.

Por lo tanto, agregamos en la transacción SalesOrder, una regla que Grabe el la sesión, el Id de la orden creada, en caso que se esté Insertando y luego que los datos hayan sido grabados. En este caso este código lo condicionamos para que se ejecute solamente cuando se invoque en caso de usar la transacción en modalidad Business Component, que es como se invoca de forma automática desde la aplicación mobile al insertar un registro.

Para esto, en la solapa Rules de la transacción SalesOrder agregamos la siguiente regla:

 BC
 {
       &WebSession.Set('salesorderid', SalesOrderId.ToString())
             if Insert
             On AfterComplete;
 }

Haga click en el Icono del Diskette image_2019528212148_1_png o presione Ctrl+S para salvar el progreso realizado.

Si ejecuta la Aplicación para Smart Devices (DemoCarPartsSD) podrá ingresar una SalesOrder y verificar que desde la aplicación web (FioriLaunchpad) quedó guardado el registro en las tablas del sistema desarrollado en GeneXus y en SAP ERP.

TIP: Al momento de ejecutar, la aplicación utiliza SAP Net Connector, por tal motivo debe copiar al directorio de la aplicación (\bin) las siguientes dlls: sapnco.dll y sapnco_utils.dll

 

 image_2019528212225_1_png

Ejecutando la aplicación web podrá ver en el Tile de Sales Order el registro en las tablas del sistema desarrollado con GeneXus.

image_2019528212230_1_png

image_2019528212243_1_png

Finalmente comprobamos desde el sistema SAP ERP el ingreso de la SalesOrder.

image_2019528212255_1_png

 

TIP: IMPORTANTE, la integración con SAP ERP en esta demo es parcial, ya que está orientada solo a presentar las funcionalidades de GeneXus for SAP Systems.

En este caso los materiales fueron ingresados de forma manual en las tablas desarrolladas con GeneXUs y los códigos de los mismos coinciden deliberadamente con los códigos en SAP ERP.

Al realizar la aplicación con integración completa con SAP ERP, se deben obtener los materiales desde SAP utilizando las BAPIs asociadas al BO Material y lo mismo con los códigos de Clientes.

 

 REF: DemoCarPartsXpz7.xpz
 Contenido: Contiene paneles: SOSAPSaveResultSD y SOSAPSaveResultWeb, trn: Sales order   con la llamada a ambos

 

 REF: DemoCarPartsXpz7All.xpz
 Contenido: Contiene el procedimiento CreateSOSAP