Conectividad de SAP / VB Fecha: 30/Nov/2004 (20/11/2004) Primera Parte:
Introducción (18/Nov/04)
|
En una publicación anterior ( Conectividad de SAP / Visual Basic - Introducción ) intento dar una explicacion sencilla de cómo conectar Visual Basic con SAP mediante DCOM Connector.
Para dar un paso más y continuando con DCOM, esta nota pretende dar una idea acerca de la obtención de una tabla desde SAP para cargar una grilla y una orientacion para poder cargar un combo con una lista obtenida de una tabla especifica de SAP.
Deberías leer la publicación anterior para conocer acerca de los parámetros que se usan así sobre como hacer la conexión con SAP, como crear la librería DLL y como hacer la referencia desde Visual Basic.
Función en ABAP.
Desde una función RFC (Remote Function Call: funciones que pueden invocarse desde sistemas externos a SAP enviando y recibiendo valores), SAP devuelve una tabla con tres campos de la tabla clientes (KNA1 es el nombre de la tabla de clientes de SAP) y esta se lee desde Visual Basic. La función recibe dos parametros: desde y hasta que cliente quiero ver.FUNCTION ylu_dcom_ej01. *"---------------------------------------------------------------------- *" Interfase local *" IMPORTING *" VALUE(I_KUNNR_DESDE) TYPE KUNNR *" VALUE(I_KUNNR_HASTA) TYPE KUNNR *" TABLES *" T_KNA1 STRUCTURE YLU_DCOM_KNA1 *"---------------------------------------------------------------------- TABLES: kna1. SELECT kunnr name1 name2 FROM kna1 INTO CORRESPONDING FIELDS OF TABLE t_kna1 WHERE kunnr GE i_kunnr_desde AND kunnr LE i_kunnr_hasta. ENDFUNCTION.La tabla que devuelve la función se llama T_KNA1 y se basa en la estructura YLU_DCOM_KNA1 que se creó especialmente para el caso con tres campos de la tabla de clientes. En SAP, para guardar en una tabla auxiliar datos provenientes de una tabla del sistema necesito respetar la estructura que estoy leyendo y se hace necesario crear un objeto (que se llama estructura) basado en los campos de la tabla que será leida, en este caso KNA1.
Puede verse que se indica como tipo de dato de los parámetros “KUNNR”, en realidad, es el nombre del elemento de datos que le corresponde al campo de la tabla KNA1 que vamos a filtrar (qué es un “elemento de datos” en SAP es para otra nota). Esta indicación no solo hace referencia al tipo de dato, sino a todas las características y propiedades que tiene el campo y automáticamente me permite buscar en una lista.Form y código Visual Basic.
La grilla que usé para el ejemplo es la “Formula One”, no es la más común pero es muy fácil de usar y el código muy intuitivo, de manera que creo que es muy fácil adaptar el ejemplo a cualquier otra grilla. De todas formas cada línea de código de la grilla está comentada.
Notarán que el ingreso en las cajas de texto no puede ser menor a 10 caracteres. Y esto es por el tipo de dato que se usó para los parámetros de la función en SAP. “KUNNR” es char de 10 y de recibir una longitud menor no funcionará correctamente.Option Explicit Private Sesion As DESej01Lib.SessionComponent Private Funcion As DESej01Lib.Ylu Private Sub Form_Load() On Error GoTo errSAP Set Sesion = New DESej01Lib.SessionComponent Sesion.Destination = "DES" ' servidor Sesion.UserID = "usuario" Sesion.Password = "password" Sesion.Client = "100" ' mandante Exit Sub errSAP: MsgBox Err.Number & " " & Err.Description End Sub Private Sub Command1_Click() Dim rs As ADODB.Recordset Dim iLinea As Integer On Error GoTo errCarga ' el "lenght" de los campos parámetros debe ser 10. If Len(Text1(0).Text) < 10 Then Text1(0).Text = String(10 - Len(Text1(0).Text), "0") & Text1(0).Text End If If Len(Text1(1).Text) < 10 Then Text1(1).Text = String(10 - Len(Text1(1).Text), "0") & Text1(1).Text End If With Grilla .DeleteRange -1, -1, -1, -1, F1ShiftVertical .MaxRow = 1 End With Set Funcion = Sesion.CreateInstance("DES.YLU") ' este DimAs "dimensiona" (es decir que le da formato) ' al recordset rs como la tabla de retorno KNA_1 Funcion.DimAs "YLU_DCOM_EJ01", "T_KNA1", rs ' esta es la llamada Funcion.Ylu_Dcom_Ej01 Text1(0).Text, Text1(1).Text, rs ' cargo mi grilla con los datos que obtuve. ' si tuviera otro tipo de grilla, incluso una dbgrid, ' podría conectarla al recordset. With rs Do While Not .EOF iLinea = iLinea + 1 Grilla.MaxRow = iLinea Grilla.EntryRC(iLinea, 1) = .Fields!kunnr Grilla.EntryRC(iLinea, 2) = .Fields!name1 Grilla.EntryRC(iLinea, 3) = .Fields!name2 .MoveNext Loop .Close End With Set rs = Nothing Exit Sub errCarga: MsgBox Err.Number, Err.Description Set rs = Nothing End SubCon las versiones de DCOM Connector anteriores a la 6.20 hay un problema al intentar crear un proyecto C++ de este tipo (con una tabla como parámetro de retorno de la función). DCOM Connector devuelve un error y no genera el proyecto de C++ correctamente sino que define mal la estructura de la tabla de retorno. Esto solo puede verse abriendo con C++ el proyecto generado.
Para todo lo que implique lectura o escritura de tablas o ejecución de RFC's o BAPI´s (Business Programming Application Interfaces: funciones provistas por SAP para emular una transacción por medio de una llamada con parámetros) que usan como parámetro tablas, es necesario el uso de ADO. Si hay que enviar información solo declaro un recordset y la función correspondiente de SAP le da la estructura.Carga de combos.
Cargar un combo con datos que provienen de SAP se usará, generalmente, para completar una interfaz mediante la que se enviarán datos a SAP.
La única manera de insertar datos en SAP es usando transacciones (así se llaman ciertos métodos definidos por SAP para completar una determinada tarea, por ejemplo, para insertar un artículo hay que correr la transacción "MM01").
La BAPI HelpValues de SAP es un objeto por el podemos invocar la función BAPI_HELPVALUES_GET que expone el método GetList. Éste método devuelve una lista de códigos y valores, permitidos por SAP, de una tabla determinada como para cargar un combo.
Debo indicarle varios parametros, y me devolverá varias tablas, una tiene la lista para el combo, pero en una string, es decir, código y descripción concatenados; otra tabla tiene la estructura de esta string (es decir, cuantos campos, cuantos caracteres para el código y cuantos para la descripción); otra tabla es una lista solo con los códigos y en otra tabla, que es de entrada y no de salida, puedo indicar criterios de selección para filtrar registros.El uso de esta BAPI para la carga de un combo es específico del entorno en que me encuentro. Es decir que si estoy cargando, por ejemplo, un pedido de ventas y necesito cargar un combo con países debo indicar la lista de países del tipo de dato para el campo que guarda este valor en un pedido de ventas. Por lo tanto no es posible tomar la tabla de paises y meterla en la lista por que la interfaz no entrará en SAP.
En este caso en particular, los pedidos se cargan con el método CreateFromDat2 (función BAPI_SALESORDER_CREATEFROMDAT2). Ubicando esta BAPI en explorador de BAPI's puedo identificar los parametros que debo enviar a la función que cargará el combo:
OBJTYPE Es el tipo de objeto indicado en el grupo de objetos en el que esta el método, en este caso BUS2032. OBJNAME Es el nombre del grupo de objetos en el que esta el método, en este caso SalesOrder METHOD Es el método que usara el dato obtenido, aquí CreateFromDat2 PARAMETER Es el nombre del parámetro del método CreateFromDat2 que usará el dato. En este caso, OrderPartners. FIELD Es el nombre del campo de diccionario donde se usara el dato obtenido, llego a él, a través de la referencia de diccionario de PARAMETER. Aquí es COUNTRY. EXPLICIT_SHLP No se completa, gracias a Dios. MAX_OF_ROWS Número de resultados máximo que quiero obtener. DESCRIPTIONONLY Devolver solo la descripción si se envía una 'X' o ''. SELECTION_FOR_HELPVALUES Tabla de entrada indicando un filtro particular para la carga del combo. HELPVALUES Tabla de salida con una cadena concatenando código y descripción buscados. VALUES_FOR_FIELD Tabla de salida solo con códigos. DESCRIPTION_FOR_HELPVALUES Tabla de salida con estructura de registro de HELPVALUES.
Cuando se declaran los recorsets se debe usar el método DimAs, al que enviaremos como parámetro el nombre de la tabla de la que necesitas la estructura, (se debe enviar el nombre del parámetro que desde la BAPI devuelve la tabla). Es decir: una tabla de las que devuelve la función se llama SELECTION_FOR_HELPVALUES, el parámetro de la BAPI que te devuelve la tabla se llama Selection4HelpValues, pues bien, para declarar tu recordset al DimAs le tenés que indicar "Selection4HelpValues" que es el nombre del parámetro y no el nombre de la tabla.
Form y código Visual Basic.
Option Explicit Private Sesion As DESCargaCombosLib.CargaCombosSessionComponent Private Funcion As DESCargaCombosLib.Helpvalues Private Sub Form_Load() On Error GoTo errSAP Set Sesion = New DESCargaCombosLib.CargaCombosSessionComponent Sesion.Destination = "DES" Sesion.UserID = "usuario" Sesion.Password = "password" Sesion.Client = "100" CargarCombo Exit Sub errSAP: MsgBox Err.Number & " " & Err.Description End Sub Private Sub CargarCombo() ' en las variables enviare los parametros Dim stOBJTYPE As String Dim stOBJNAME As String Dim stMETHOD As String Dim stPARAMETER As String Dim stFIELD As String Dim stEXPLICIT_SHLP As String Dim stMAX_OF_ROWS As String Dim stDESCRIPTIONONLY As String Dim iCodigo As Integer Dim iDescripcion As Integer Dim iPaso As Integer Dim rsSELECTION_FOR_HELPVALUES As ADODB.Recordset Dim rsHELPVALUES As ADODB.Recordset Dim rsVALUES_FOR_FIELD As ADODB.Recordset Dim rsDESCRIPTION_FOR_HELPVALUES As ADODB.Recordset On Error GoTo errCarga Set Funcion = Sesion.CreateInstance("DES.HELPVALUES") ' formato a los recordsets Funcion.DimAs "BapiGetList", _ "SELECTION4HELPVALUES", rsSELECTION_FOR_HELPVALUES Funcion.DimAs "BapiGetList", "HELPVALUES", rsHELPVALUES Funcion.DimAs "BapiGetList", "VALUES4FIELD", rsVALUES_FOR_FIELD Funcion.DimAs "BapiGetList", _ "DESCRIPTION4HV", rsDESCRIPTION_FOR_HELPVALUES stOBJTYPE = "BUS2032" stOBJNAME = "SALESORDER" stMETHOD = "CREATEFROMDAT2" stPARAMETER = "ORDERPARTNERS" stFIELD = "COUNTRY" ' EXPLICIT_SHLP stMAX_OF_ROWS = "0" stDESCRIPTIONONLY = "" Funcion.BapiGetList stMETHOD, stPARAMETER, stOBJTYPE, stOBJNAME, _ stFIELD, stMAX_OF_ROWS, , , rsHELPVALUES, rsVALUES_FOR_FIELD, _ rsDESCRIPTION_FOR_HELPVALUES ' la descripcion del registros esta en el recordset ' rsDESCRIPTION_FOR_HELPVALUES, un campo en cada registro. ' asi que en el primero leo el lenght del código y despues ' la descripción, si hubiera mas campos, seguiría. With rsDESCRIPTION_FOR_HELPVALUES Do While Not .EOF If iPaso = 0 Then iCodigo = .Fields!Leng Else iDescripcion = .Fields!Leng End If iPaso = iPaso + 1 .MoveNext Loop End With ' luego extraigo de la string en el recordset rsHELPVALUES ' la parte que me interesa. Combo1.Clear With rsHELPVALUES Do While Not .EOF Combo1.AddItem Mid$(.Fields(0).Value, iCodigo + 1) Combo1.ItemData(Combo1.NewIndex) = Val(Left$(.Fields(0).Value, iCodigo)) .MoveNext Loop End With Set rsSELECTION_FOR_HELPVALUES = Nothing Set rsHELPVALUES = Nothing Set rsVALUES_FOR_FIELD = Nothing Set rsDESCRIPTION_FOR_HELPVALUES = Nothing Exit Sub errCarga: MsgBox Err.Number, Err.Description Set rsSELECTION_FOR_HELPVALUES = Nothing Set rsHELPVALUES = Nothing Set rsVALUES_FOR_FIELD = Nothing Set rsDESCRIPTION_FOR_HELPVALUES = Nothing End Sub Private Sub Form_Unload(Cancel As Integer) Set Sesion = Nothing Set Funcion = Nothing End SubHemos visto que para cargar un combo necesito dimensionar cuatro recorsets y luego una función de SAP le da la estructura. Uno de estos recorsets trae código y descripción concatenados (HelpValues) en una sola cadena y otro la estructura de esta cadena (DescriptionForHelpValues). Esta última tendrá un registro por cada campo en la cadena, en la cadena los campos están separados por un espacio, es decir que si pido una lista de países recibiré una cadena, por ejemplo, así: "AR··Argentina" en HelpValues y dos registros en DescriptionForHelpValues que entre sus campos informa largo, nombre y otras características de los campos de la cadena. Recorriendo DescriptionForHelpValues, el campo Leng vale 3 y 20, pero si mido la string veo que el espacio entre el código y la descripción no se cuenta y de los 20 de la descripción me manda los que encuentra, sin blancos al final.