Creación de IDOCs custom

Definir el sistema lógico desde la transacción SALE.

Nos aparecer un mensaje como el siguiente, el mismo es una advertencia, diciéndonos que la tabla no es dependiente del mandante.

En el caso de no tener uno definido lo definimos.

Creamos los segmentos de nuestro IDOC custom. Para esto vamos a la transacción WE31.

Definimos los campos del segmento, la descripción y le damos guardar.

Nos pedirá un responsable (Nuestro usuario)

Creamos un tipo básico de IDOC desde la transacción WE30.

Al darle OK nos llevara a la siguiente ventana donde hacemos click en el icono marcado para agregar al IDOC los segmentos que creamos anteriormente.

Nos aparecerá la siguiente ventana:

Completamos el tipo (El nombre del segmento que creamos anteriormente).

Le indicamos que es mandatorio.

El máximum y mínimum number indican cuantas “filas” un segmento del IDOC puede tener, lo podemos imaginar como si fuera una tabla interna.

Si estamos creando un IDOC de ítems de una factura, y le ponemos que como máximo puede tener 10, entonces no podremos tener mas de 1 items en el IDOC.

Hacemos click en guardar:

Creamos un MessageType desde la transacción WE81.

Un Message Type es una forma de determinar para que se usa un IDOC, por ejemplo los IDOCs de ordenes de compras pueden estar en un Message Type ORDER.

Hacemos click en New Entries…

Ingresamos el nombre, una descripción y hacemos click en guardar.

Asignamos el Message Type a un IDOC usando la transacción WE82.

Usando los datos de todo lo que creamos anteriormente completamos la siguiente ventana:

Creamos un grupo de función usando la transacción SE80.

Hacemos click en Edit object

Hacemos click en Enhanced Options.

Seleccionamos del icono de la carpeta Function Group.

Ingresamos un nombre y hacemos click en New.

Ingresamos una descripción y hacemos click en Save.

Creamos un modulo de función para procesar nuestro IDOC desde la transacción SE37.

Los modulo de función para el procesamiento de IDOCs tienen que tener ciertos parámetros definidos para que funcionen correctamente.

Los parámetros que DEBEN tener son los siguientes:

INPUT_METHOD              LIKE        BDWFAP_PAR-INPUTMETHD

MASS_PROCESSING       LIKE        BDWFAP_PAR-MASS_PROC

WORKFLOW_RESULT     LIKE        BDWF_PARAM-RESULT

APPLICATION_VARIABLE              LIKE        BDWF_PARAM-APPL_VAR

IN_UPDATE_TASK           LIKE        BDWFAP_PAR-UPDATETASK

CALL_TRANSACTION_DONE       LIKE        BDWFAP_PAR-CALLTRANS

IDOC_CONTRL   LIKE        EDIDC

IDOC_DATA        LIKE        EDIDD

IDOC_STATUS   LIKE        BDIDOCSTAT

RETURN_VARIABLES      LIKE        BDWFRETVAR

SERIALIZATION_INFO    LIKE        BDI_SER

WRONG_FUNCTION_CALLED

Podemos crear la función a mano o copiarnos una función standard como la BAPI_IDOC_INPUT1.

Ingresamos el nombre del modulo de función nuevo y el grupo de funciones que creamos anteriormente y hacemos click en Copy.

Ingresamos a nuestra función recién creada, y le borramos el código, dado que haremos nuestra propia lógica y la activamos.

El siguiente paso es asignar el Message Type y IDOC type al modulo de funciones. Para esto ingresamos a la transacción WE57. Esto es para saber el IDOC a que modulo de función debe llamar.

Hacemos click en New Entries.

Completamos los datos marcados con los diferentes objetos que creamos anteriormente.

Configuramos las características del modulo de función que creamos anteriormente usando la transacción BD51.

Hacemos click en New Entries

Completamos los datos con el modulo de función, el input type y si esta permitido el modo dialogo.

Creamos un Process Code en la transacción WE42.

Hacemos click en New Entries

Ingresamos un nombre, descripción y seleccionamos Function Module.

Hacemos click en guardar.

Seleccionamos nuestra función:

Hacemos click en guardar.

Hacemos doble click en Logical Message:

Para nuestro process code hacemos click en New entries.

Ingresamos nuestro Message Type que creamos anteriormente y hacemos click en guardar.

En la transacción WE20 creamos el Partner Profile (Si no existe ya)

En este caso como ya existe, hacemos click en el botón Editar.

Hacemos click en Create Inbound parameter

Ingresamos el Process Code y Message Type creados anteriormente y hacemos click en guardar.

Ahora necesitamos agregar código a nuestra función que creamos en uno de los pasos anteriores.

La idea es que nuestra función inserte un registro en una tabla al recibir un IDOC.

Primero creamos la tabla:

Vamos a nuestra función y completamos el código:

  DATA: lt_idoc_data TYPE TABLE OF EDIDD,
        lwa_auto TYPE ZAUTOS_IDOC,
        lwa_zauto TYPE ZAUTO.

"Pasamos los datos del parametro de la funcion a una tabla interna.
  lt_idoc_data = idoc_Data[].

"Leemos los datos del segmento ZAUTO y lo asignamos a una WA de la misma estructura
  lwa_zauto = lt_idoc_data[ segnam = 'ZAUTO' ]-sdata.

  lwa_auto-marca = lwa_zauto-marca.
  lwa_auto-modelo = lwa_zauto-modelo.
  lwa_auto-mandt = sy-mandt.

"Actualizamos la tabla de BD.
  MODIFY ZAUTOS_IDOC FROM lwa_auto.

Para probar nuestro IDOC podemos ir a la transacción WE19:

Hacemos click en la línea vacía y completamos los datos:

Volvemos a la pantalla anterior y hacemos click en Inbound Function Module

Si no nos carga automáticamente el modulo de función lo ingresamos.
También tenemos la opción de ejecutarlo en modo debug.

Luego de ejecutarlo nos aparecerá un mensaje como el siguiente dándonos el numero de IDOC.

Si vamos a nuestra tabla Z, ahora debería contener la información que ingresamos en el IDOC.

Para ver el IDOC que creamos podemos ir a la transacción WE05 e ingresamos el numero de IDOC.

Desde esta pantalla podemos ver el segmento, los datos y el status del IDOC.

Usando Table Functions y Vistas CDS

Al igual que cuando creamos la vista CDS, para crear una table function debemos crear un data definition como hicimos antes, pero al momento de llegar al template seleccionamos “Define Table Function with parameters”

Al hacer click en finish nos aparecerá el template seleccionado el cual debemos completar para que quede similar a lo siguiente:

@EndUserText.label: 'Table function para vuelos'
define table function ZTF_VUELOS
with parameters 
@Environment.systemField: #CLIENT
client : mandt,
returns {
  client : abap.clnt;
  carrid : s_carr_id;
  connid : s_conn_id;
  fldate : s_date;
  carrname : s_carrname;
  URL : s_carrurl;
  price : s_price;
  currency : s_currcode;
  passname : s_passname;
}
implemented by method zcl_vuelos2=>obtener_datos_pasajeros;

Vemos que al final le estamos especificando la clase (ZCL_VUELOS2) y el método (obtener_datos_pasajeros) que vamos a llamar.

El próximo paso será crear esta clase y método, para esto hacemos click derecho sobre nuestro package o objeto local y seleccionamos crear una clase.

Ingresamos un nombre (El que usamos en la Table function) y descripción.

De nuevo seleccionamos una orden de transporte y le damos click a Finish o si es un objeto local directamente hacemos click en finish.

Nos creara el template que se vera similar a:

Adaptamos la clase para que tenga la lógica que nosotros queramos, por ejemplo:

CLASS zcl_vuelos2 DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    INTERFACES : if_amdp_marker_hdb.
    CLASS-METHODS :  obtener_datos_pasajeros FOR TABLE FUNCTION ZTF_VUELOS.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS zcl_vuelos2 IMPLEMENTATION.

        METHOD obtener_datos_pasajeros BY DATABASE FUNCTION FOR HDB
                        LANGUAGE SQLSCRIPT
                        OPTIONS READ-ONLY
                        USING scarr sflight sbook.

               RETURN

                    SELECT a.mandt AS client, a.carrid AS carrid, a.connid AS connid, a.fldate AS fldate,
                           b.carrname AS carrname, b.URL as URL, a.price AS price, a.currency AS currency,
                           c.passname AS passname FROM sflight AS a
                           INNER JOIN scarr AS b
                            ON a.carrid = b.carrid
                           INNER JOIN sbook AS c
                            ON a.carrid = c.carrid AND
                               a.connid = c.connid AND
                               a.fldate = c.fldate
                           WHERE a.mandt = c.mandt;
        ENDMETHOD.
ENDCLASS.

Ahora debemos creamos una vista CDS que utilice nuestra table function. Para esto, como hicimos antes nos creamos una vista CDS.

Nuestra vista debería ser algo similar a esto:

Lo importante a ver acá, es que estamos haciendo el SELECT desde la Table Function (SELECT FROM ZTF_VUELOS ).

@AbapCatalog.sqlViewName: 'ZCDS_TF_DATOS_V'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Datos de pasajeros usando TF'
define view ZCDS_TF_DATOS as select from ZTF_VUELOS ( client : $session.client ) {
    carrid,
    connid,
    fldate,
    carrname,
    URL,
    @Semantics.amount.currencyCode: 'CurrencyCode' 
    price,
    @Semantics.currencyCode
      cast ('USD' as abap.cuky) as CurrencyCode,
    currency,
    passname
}

Una ves con nuestra nueva vista creada, la podemos consultar como hicimos antes desde la SE16. Pero también lo que podemos hacer es ponerle un breakpoint en la clase que creamos.

Para hacer esto, en eclipse, vamos a la clase que creamos y hacemos doble click donde esta marcado en la captura de abajo. Nos debería aparecer un puntito en color verde si el breakpoint esta activo.

Una ves con el breakpoint activo, podemos ejecutar la vista desde la SE16 y se nos debería activar el debugger en eclipse.

Para ver mejor por debug podemos cambiar el código por:

CLASS zcl_vuelos2 DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    INTERFACES : if_amdp_marker_hdb.
    CLASS-METHODS :  obtener_datos_pasajeros FOR TABLE FUNCTION ZTF_VUELOS.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS zcl_vuelos2 IMPLEMENTATION.

        METHOD obtener_datos_pasajeros BY DATABASE FUNCTION FOR HDB
                        LANGUAGE SQLSCRIPT
                        OPTIONS READ-ONLY
                        USING scarr sflight sbook.

/*               RETURN */

                    lt_datos = SELECT a.mandt AS client, a.carrid AS carrid, a.connid AS connid, a.fldate AS fldate,
                           b.carrname AS carrname, b.URL as URL, a.price AS price, a.currency AS currency,
                           c.passname AS passname FROM sflight AS a
                           INNER JOIN scarr AS b
                            ON a.carrid = b.carrid
                           INNER JOIN sbook AS c
                            ON a.carrid = c.carrid AND
                               a.connid = c.connid AND
                               a.fldate = c.fldate
                           WHERE a.mandt = c.mandt;

                    RETURN :lt_datos;

        ENDMETHOD.
ENDCLASS.

En la imagen de abajo podemos ver el breakpoint en la línea 22, a la derecha del código podemos ver nuestra variable lt_datos donde se guarda el resultado de la consulta y en la parte de inferior de la pantalla podemos ver el contenido de la tabla interna.

Tambien al igual que con una vista CDS común, la podemos usar desde un reporte ABAP:

SELECT * FROM ZCDS_TF_DATOS( pcarrid = 'AA', pconnid = '0017', pfldate = @lv_fecha )
  INTO TABLE @DATA(lt_datos2).
IF sy-subrc = 0.

ENDIF.

Tambien podemos usar un ALV IDA para mostrar los datos:

REPORT zida_cds.
data: lo_exc type ref to cx_salv_function_not_supported.

try.
  cl_salv_gui_table_ida=>create_for_cds_view( 'ZCDS_VUELOS' )->fullscreen( )->display( ).
  catch cx_salv_function_not_supported into lo_exc.
    message lo_exc type 'I'.
endtry.

Creando una vista CDS

En este post rápido, les voy a mostrar como crear una vista CDS fácilmente.

Ingresamos a Eclipse, vamos al package que usemos o creamos un Data Definition como objeto local.

En la ventana que se abre, buscamos Core Data Services y seleccionamos Data Definition.

Ingresamos un nombre / Descripción y hacemos click en next.

En la siguiente pantalla, se nos dará la opción de elegir una orden de transporte, en el caso que estemos trabajando en un package, si estamos en un objeto local nos aparecerá la pantalla griseada.

Seleccionamos la orden de transporte y hacemos click en next.

Si estamos en un objeto local hacemos click en next.

En la siguiente pantalla se nos dará a seleccionar un template para la vista CDS, seleccionamos Define View y hacemos click en Finish.

Al hacer click en Finish volveremos a Eclipse y veremos algo como lo siguiente:

En esta pantalla, debemos ingresar un nombre para nuestra vista ABAP (sqlViewName) y reemplazar el data_source_name por las tablas de donde sacamos los datos. Por ejemplo:

@AbapCatalog.sqlViewName: 'ZCDS_VUELOS_V'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'CDS Para aerolineas y vuelos'
define view ZCDS_VUELOS as select from scarr 
                        join sflight 
                            on scarr.carrid = sflight.carrid
                        join sbook
                            on sflight.carrid = sbook.carrid and
                               sflight.connid = sbook.connid and
                               sflight.fldate = sbook.fldate
{
    key scarr.carrid,
    key sflight.connid,
    key sflight.fldate,
    key sbook.bookid,
    scarr.carrname,
    scarr.url,
    sflight.price,
    sflight.currency,
    sflight.planetype,
    sflight.seatsmax,
    sflight.seatsocc,
    sbook.customid,
    sbook.custtype,
    sbook.class,
    sbook.loccuram,
    sbook.loccurkey,
    sbook.order_date,
    sbook.passname
}

En esta vista CDS podemos ver que estamos haciendo un JOIN de 3 tablas.

Una ves que tenemos nuestra vista CDS lista, activamos (CTRL+F3).

Con nuestra vista CDS ya activa podemos ir a la SE16 con el nombre, en este caso ZCDS_VUELOS_V y probarla.

Vemos que nos trae datos:

También la podemos probar desde eclipse, haciendo click derecho sobre nuestra vista CDS y yendo a:

Tambien podemos hacer uso de la vista CDS desde un reporte de ABAP común y corriente.

Como podemos ver en la captura, estamos parados después de que se realizo el SELECT y la tabla interna LT_DATOS contiene datos.