lunes, 14 de julio de 2014

Abap in eclipse (AiE) - Instalación

Introducción
Esta entrada tiene como objetivo explicar la instalación del plugin ABAP IN ECLIPSE para.
  • Eclipse Indigo  3.7.2
  • Eclipse Juno     4.2  
  • Eclipse Kepler  4.3  
  • Eclipse Luna     4.4  
La versión de Eclipse recomendada actualmente por SAP es Eclipse Luna 4.4

Prerequisitos

Lo primero, aseguraros de tener instalado el SapGUI 7.20 con el nivel de parches 9, o un SapGUI superior, necesitáis tener como mínimo esta versión porque incluye el componente Microsoft Runtime DLLs VS201 necesario para que se comuniquen SAP y Eclipse.
Recordemos, que estamos trabajando en una arquitectura cliente/servidor. En un lado tenemos el cliente o front-end, Eclipse con Abap Development Tools(ADT) que solicitara servicios tales como actualizar código, compilar, revisar versiones a un servidor Sap, nuestro servidor o sistema back-end. Todo esto se realiza a través de conexiones basadas en el protocolo RFC/REST


Este tipo de arquitectura solo esta disponible en un sistema Sap con SAP_KERNEL 7.20 y con el componente SAP_BASIS 7.31 o superior. Si no tenemos acceso a un sistema Sap con estas características, podemos instalarnos una versión trial de sap.
Es una versión trial del minisap con el modulo SAP BASIS 7.31 nivel de parcheado 4.

INSTALACIÓN

La instalación es muy parecida en cualquier versión del ide de Eclipse.
Solo difieren en la actualización de componentes y el repositorio desde el que descargaremos el plugin.
Una vez descargado, descomprimir en c:\eclipse, y ejecutar el archivo eclipse.exe.



0. SOLO PARA ECLIPSE INDIGO 3.7.2:

Si vamos a utilizar Eclipse Indigo, lo primero sera asegurarnos que todos los componentes del IDE están actualizados.
Arrancamos Eclipse y ejecutamos  Help -> Check for update .
Es obligatorio  tener varios componentes instalados en eclipse indigo 3.7.2 para utilizar AiE :
  • EMF - Eclipse Modeling Framework Core Runtine
  • EMF Edit Data Binding
  • EMF Model Transaction Workbench Integration
  • EMF Validation Framework
  • Mylyn Commons
  • Mylyn Task-Focused Interface
Gracias a Gregor Wolf en SCN, tenemos un archivo P2F que instalara y actualiza todos estos componentes para Eclipse Indigo 3.7.2
Arrancar el Eclipse -> File -> Import.. -> Install -> Install Software Item from file

Seleccionamos el archivo P2F. Con este fichero instalaremos de golpe y automáticamente todos los plugins para Eclipse necesarios para el correcto funcionamiento de AiE.


 


Reiniciamos Eclipse, Help -> Check for update .


1. DESCARGAR E INSTALAR ABAP IN ECLIPSE:


1.1 ECLIPSE INDIGO 3.7.2 :

Descargar el plugin ABAP IN ECLIPSE 2.0  desde el centro de descargar de SAP.
Requiere un usuario autorizado para su descargar.
  • SAP Software Download Center ->  Search for Installations and Upgrades -> 
    •  Search Area -> all software download
    •  Search Term -> ABAP ECLIPSE 2.0



Instalamos el plugin en Eclipse. Help -> Install new Software -> Add.

Damos un nombre al repositorio y seleccionamos el archivo .zip del plugin descargado.



Marcamos todos los componentes, botón NEXT, revisamos componentes y aceptamos las licencias de uso.
Si todo va bien, empezara el proceso de instalación
Cuando termine, aparecerá la ventana de bienvenida del plugin.



1.2 ECLIPSE JUNO 4.2 / KEPLER 4.3 / LUNA 4.4.1 :

Con estas versiones, la instalación del plugin es muy sencilla.

Desde Eclipse -> Help -> Install new Software

En el campo Work with  introducir la siguiente dirección y una descripción (Ej: Abap in eclipse ):
  • https://tools.hana.ondemand.com/juno    para Eclipse Juno    4.2
  • https://tools.hana.ondemand.com/kepler para Eclipse Kepler 4.3
  • https://tools.hana.ondemand.com/luna    para Eclipse Luna    4.4.1
Seleccionar solo los componentes ABAP_DEVELOPMENT TOOLS FOR ABAP NetWeaver.



Botón NEXT, revisamos componentes y aceptamos las licencias de uso.
Si todo va bien, empezara el proceso de instalación.
Cuando termine, aparecerá la ventana de bienvenida del plugin.



1.3 AJUSTES EN LAS PREFERENCIAS DE ECLIPSE

Por ultimo, SAP recomienda configurar las opciones de runtime de Eclipse para asegurar que hay memoria suficiente y evitar  errores out.of.memory.

En la carpeta Eclipse, editamos el archivo eclipse.ini

Después de la linea donde esta el comando -vmargs añadimos:

 -XX:PermSize=32m 
 -XX:MaxPermSize=256m

Y añadimos las siguientes opciones para la Java VM

 -Dfile.encoding=UTF-8
 -XX:+HeapDumpONOutOfMemoryError
 -Dosgi.requiredJavaVersion=1.6

2. Configuración de la conexión y del Sistema SAP

Es necesario establecer Secure Network Communication por seguridad, dado que nos vamos a conectar a un sistema SAP y nos autentificaremos con  Usuario/Contraseña para obtener pleno acceso al repositorio de objetos del sistema.

Si no se activa esta opción, los datos de usuario/contraseña se enviaran sin cifrar y toda la comunicación podrá ser visible por terceros. 

Hay que asegurarse que el Secure Network Communication (SNC) esta activado.
  1. En el SapGUI seleccionamos la conexión y le damos a modificar -> Pestaña RED.
  2. Activar Secure Network Communication -> Sin Single Sign-On
Asignar los siguientes roles y autorizaciones al usuario que vamos a utilizar ( transacción SU01 )
  • SAP_BC_DWB_ABAPDEVELOPER
  • SAP_BC_DWB_WBDISPLAY
 
Activar los servicio web HTTP para acceder a la ayuda Abap. Los servicios web HTTP son formas programáticas para el envío y recepción de datos desde servidores remotos utilizando el protocolo HTTP. Nos va ha permitir acceder a la ayuda de Abap desde Eclipse. Los servicies web HTTP están predefinidos en sistemas 7.31 o superiores pero no viene activados por defecto.

    1. Accedemos a la transacción SICF en el sistema SAP.
    2. Como filtro, seleccionamos DEFAULT_HOST.


    3. Expandimos el nodo default_host -> sap -> bc -> abap.    
    4. Seleccionamos docu y botón derecho del ratón en el menú seleccionamos Activate Service.
    5. Repetimos para ToolsDocu.


    6. Desde la raíz, expandimos el nodo  default_host -> sap -> public -> bc - abap.
    7. Seleccionamos docu y botón derecho del ratón en el menú seleccionamos Activate Service.
    8. Repetimos para ToolsDocu.



    9.Testear que el servicio funciona, botón derecho -> Test Link



Si falla el Test Link, que a mi me paso después de una instalación limpia del SAP NetWeaver AS ABAP 7.03 SP04 64-bit Trial, revisar los puertos del ICM en la transacción SMICM , icono Services o Shift + F1.

 

Comprobar que tenéis estos puertos si estáis usando SAP NetWeaver AS ABAP 7.03 SP04 64-bit Trial.A mi me cambio os puertos a 0 y no me funcionaba nada.

Por ultimo , cambiamos 2 parámetros de perfil en el sistema, así cada vez que activemos o realicemos una acción que requiera otra vez la contraseña para acceder al servicio.

En la transacción RZ11 cambiamos el valor de estos dos parámetros:
  • login/create_sso2_ticket, le damos valor 3.
  • login/accept_sso2_ticket, le damos valor 1.
Felicidades, ya tenemos todo listo, vamos a crear nuestro primer programa en Abap desde Eclipse. Pero eso en la siguiente entrega: "Abap in Eclipse: Hola Mundo"

Referencias:

domingo, 18 de mayo de 2014

Añadir nuevos campos a las transacciones ME21N / ME22N / ME23N

En esta entrada, voy a explicar con un ejemplo como añadir nuevos campos a las transacciones ME21N, ME22N y ME23N.

Las transacciones ME21N / ME22N y ME23N pertenecen al componente compras, que a su vez pertenece al modulo de Gestión de Materiales MM. Este modulo se encarga de dar soporte a todas la fases de gestión de materiales Planificación de necesidades y control, compras, entrada de mercancías, gestión de stocks y verificación de facturas.

Estas transacciones dan soporte para crear, modificar y visualizar pedidos de compra de materiales a proveedores. Aunque el estándar cubre la mayoría de escenarios, es posible, realizar cambios a medida para adaptar estas transacciones a las necesidades del cliente.

Por ejemplo, el cliente necesita dos campos nuevos en las posiciones de los pedidos de compras:
  • ZZSOCIO: número de socio de la cooperativa que realiza la compra
  • ZZFACT:   número de doc. de compra del socio
Estos campos deben ser visibles y editables desde las transacciones de compras.


1. AÑADIR LOS NUEVOS CAMPOS A LA BASE DE DATOS
Desde la transacción SE11. Crear o modificar las siguientes estructuras de datos: :
  • CI_EKKODB: Si queremos añadir nuevos campos a la cabecera de los pedidos de compra 
  • CI_EKPODB: Si queremos añadir nuevos campos a las posiciones de los pedidos de compras
Los dos nuevos campos  pertenece a las posiciones del pedido de compra.
Añadir los nuevos campos a la estructura CI_EKPODB.

 Los nuevos campos para posiciones de pedidos de compra
Activar la estructura,  los nuevos campos aparecerán en la tabla EKPO.

EKPO con los nuevos campos
Crear una nueva estructura de datos, por ejemplo ZCI_EKPO_DB, con los siguientes campos:
  • MANDT
  • EBELN
  • EBELP
  • Todos nuestros campos a medida


2. GRUPO DE FUNCIONES MEPOBADIEX:
Es un grupo de funciones de ejemplo, no lo modifiquéis, copiarlo y utilizar la copia para los nuevos campos.
  1. Ejecutarla transacción SE80 
  2. Seleccionar grupo de funciones en el despegable
  3. Copiamos el grupo de funciones MEPOBADIEX y lo llamamos ZZMEPOBADIEX
  4. Copiamos todas las bapis de MEPOBADIEX. Añadir "ZZ" al principio del nombre de todas las funciones.




Variable y estructuras globales del grupo de funciones ZZMEPOBADIEX.

Accedemos al include LZZMEPOBADIEXTOP, cambiar el codigo por

* dynpro output structure
TABLES:  ZCI_EKPO_DB.                                        "Antes mepo_badi_struct.

* persistent data
DATA: gt_persistent_data TYPE SORTED TABLE OF  ZCI_EKPO_DB   "Antes mepo_badi_exampl
                         WITH UNIQUE KEY mandt ebeln ebelp,

* actual data
      gt_data            TYPE SORTED TABLE OF   ZCI_EKPO_DB  "Antes mepo_badi_exampl
                         WITH UNIQUE KEY mandt ebeln ebelp.

* definitions required for dynpro/framework integration
DATA: ok-code TYPE sy-ucomm.
INCLUDE lmeviewsf01.

Creamos una nueva dynpro con los campos a medida.

  1. Ejecutamos el screen painter, transacción SE51.
  2. Como programa, indicamos el prog de control del grupo de funciones: SAPLZZMEPOBADIEX.
  3. Indicar el numero de la nueva dynpro.
  4. En la pestaña Atributos marcar Tipo de dynpro -> Subscreen
  5. Diseñar la nueva dynpro.
  6. Añadir dos módulos a la lógica de proceso de la dynpro:
    • CUST_EVENT_PBO
    • CUST_EVENT_PAI
     6. Guardar  y activar la dynpro.


transaccion SE51 - Screen painter

SAPLZZMEPOBADIEX: Programa de control del grupo de funciones ZZMEPOBADIEX
Como origen de los campos :  ZCI_EKPO_DB  del include  LZZMEPOBADIEXTOP .

Dynpro con los nuevos campos a medida
Módulos PBO y PAI

Repito, muy  importante, comprobar que la estructura de los campos es ZCI_EKPO_DB y que no se referencia a la estructura del diccionario.

campos a medida de la dynpro 0100
Include LZZMEPOBADIEXTOP: variables globales del grupo de funciones ZZMEPOBADIEX.
  • Remplazamos MEPO_BADI_STRUCT  por  ZCI_EKPO_DB
  • Si no existe , incluir la instrucción  "TABLES:  ZCI_EKPO_DB".     
LZZMEPOBADIEXTOP
Continuamos con las bapis contenidas en el grupo de funciones ZZMEPOBADIEX.
Adaptamos el código  y parámetros de las bapis a nuestras necesidades.

Nos centramos en las siguientes funciones:

  • ZZMEPOBADIEX_INIT
  • ZZMEPOBADIEX_OPEN
  • ZZMEPOBADIEX_GET_DATA
  • ZZMEPOBADIEX_PUSH
  • ZZMEPOBADIEX_POP
  • ZZMEPOBADIEX_SET_DATA
En todas las Bapis reemplazamos:
  • Remplazamos MEPO_BADI_STRUCT  por  ZCI_EKPO_DB.
  • Remplazamos MEPO_BADI_EXAMPL por  ZCI_EKPO_DB. 
Cambiamos el SELECT de  ZZMEPOBADIEX_OPEN por otro que recupere nuestros campos a medida:

CHECK NOT im_ebeln IS INITIAL.

  SELECT mandt ebeln ebelp ZZSOCIO ZZFACT
    FROM ekpo
    INTO CORRESPONDING FIELDS OF TABLE gt_persistent_data
    WHERE ebeln = im_ebeln.

  gt_data = gt_persistent_data.


Cambiamos el código de ZZMEPOBADIEX_SET_DATA por:

DATA: ls_data LIKE LINE OF gt_data.

  FIELD-SYMBOLS: <data> LIKE LINE OF gt_data.

  CHECK NOT im_data-ebelp IS INITIAL.

  IF NOT im_physical_delete_request IS INITIAL.
* delete a line from gt_data
    DELETE TABLE gt_data WITH TABLE KEY mandt = sy-mandt
                                        ebeln = im_data-ebeln
                                        ebelp = im_data-ebelp.
  ELSE.
* update customer data
    READ TABLE gt_data ASSIGNING <data> WITH TABLE KEY
                                        mandt = sy-mandt
                                        ebeln = im_data-ebeln
                                        ebelp = im_data-ebelp.
    IF sy-subrc IS INITIAL.
     <data>-ZZSOCIO = im_data-ZZSOCIO.
     <data>-ZZFACT  = im_data-ZZFACT.
    ELSE.
      ls_data = im_data.
      ls_data-mandt = sy-mandt.
      INSERT ls_data INTO TABLE gt_data.
    ENDIF.
  ENDIF.

3. BADIS ME_GUI_PO_CUST / ME_PROCESS_PO_CUST:
  • Badi ME_GUI_PO_CUST:  controla el aspecto gráfico de la nueva pestaña
  • Badi ME_PROCESS_PO_CUST: para tratar los datos de la cabecea o de posición
Implementación de la Badi ME_GUI_PO_CUST

Esta badi contiene los métodos para poder añadir una nueva pestaña a nivel de cabecera o posición.

También contiene los métodos que permiten controlar el flujo de datos entre la Base de Datos y la Pestaña.

Voy a explicar las dos formas que conozco de implementar la badi.

Implementación a través de la SPRO:

Transacción SPRO -> Gestión de materiales -> Compras -> Add-ins empresariales para compras -> BAdI: Pantallas propias del cliente en el pedido (transacc.EnjoySAP)

Es necesario comprobar si ya existe alguna implementacíón de la Badi. Recordar que aunque la Badi se puede implementar más de una vez, soló una implementación puede estar activa al mismo tiempo. Si hay dos o mas implementaciones activas al mismo tiempo, saltara un DUMP al ejecutar cualquier transacción de compras.

Implementaciones existentes de la badi 
  • Crear una nueva implementación: Pulsar sobre el icono Crear.  El sistema solicitara introducir un  nombre para nueva implementación de la Badi. 
  • Modificar una implementación ya existente: Doble click sobre el nombre de la implementación.
Implementación a traves de la transaccion SE18:

Transaccion SE18 -> Nre.Badi -> ME_GUI_PO_CUST

Para ver la implementaciones existentes Menu superior -> Implementación ampliacion -> Resumen
Para crear una nueva implementación Pulsar sobre el icono Crear.




lo primero es modificar los atributos de la clase ->  Pestaña Interface.
Doble click sobre la clase que aparece en el campo Nombre de la clase de implementación.
Una vez dentro de la clase -> Pasar a -> Secciones -> Área publica.

Añadir las siguientes lineas debajo de PUBLIC SECTION.

type-pools MMMFD .
constants SUBSCREEN1 type MEPO_NAME value 'ITEMSCREEN1' .

Repetimos el proceso, pero esta vez seleccionamos Área Privada.
Añadir las siguientes a la  PRIVATE SECTION.

data DYNP_DATA_PBO type ZMEPO_CEB.
data DYNP_DATA_PAI type ZMEPO_CEB 

Clase de la implenetación

Acceso a los atributos Públicos


Acceso a los atributos Privados

Volvemos a la pestaña Interface. Tenemos que implementar los siguientes métodos:
  • SUBSCRIBE 
  • MAP_DYNPRO_FIELDS
  • TRANSPORT_FROM_MODEL
  • TRANSPORT_TO_DYNP
  • TRANSPORT_FROM_DYNP
  • TRANSPORT_TO_MODEL

Implementación de métodos de la Badi ME_GUI_PO_CUST
METODO SUBSCRIBE 


Permite configurar parámetros clave de la nueva pestaña y campos a medida.
La tabla RE_SUBSCRIBERS debe contener un registro por cada nueva pestaña que queramos añadir.
Siguiendo con el  ejemplo, añadimos una nueva pestaña a las posiciones del pedido de compra.

DATA: ls_subscriber LIKE LINE OF re_subscribers.

CHECK im_application = 'PO'.
CHECK im_element     = 'ITEM'.  "para cabecera -> 'HEADER'.

CLEAR re_subscribers[].

ls_subscriber-name = SUBSCREEN1.
ls_subscriber-program = 'SAPLZZMEPOBADIEX'. "El programa que contiene la dynpro
ls_subscriber-dynpro  = '0100'.             "Dynpro con los campos a medida
ls_subscriber-struct_name = 'ZCI_EKPO_DB'.  "Estructura campos dynpro
ls_subscriber-label = text-001.             "Texto de la pestaña
ls_subscriber-position = 13.                "Posición de la pestaña
ls_subscriber-height = 7.
APPEND ls_subscriber TO re_subscribers.

METODO  MAP_DYNPRO_FIELDS

Tenemos que crear un mapeo para los nuevos campos a medida con metafields, a cada nuevo campo le asignaremos un metafield existente en el grupo de tipos MMFD. La función de estos metafileds es la selección de campos y manejo de errores y excepciones que se puedan producir.

* Mapeo de los nuevos campos de la pestaña nueva en las trans. de compra
* Las definiciones standard se pueden encontrar en type pool MMMFD
* TODO  campo definido por el cliente debe 
* tener un mapeo superior a 90000000

FIELD-SYMBOLS: <mapping> LIKE LINE OF ch_mapping.

  LOOP AT ch_mapping ASSIGNING <mapping> .

    CASE <mapping>-fieldname.

      WHEN 'ZZSOCIO'.    <mapping>-metafield = mmmfd_cust_01.
      WHEN 'ZZFACT'.     <mapping>-metafield = mmmfd_cust_02.

    ENDCASE.

  ENDLOOP.

Grupo de tipos MMMFD y las definiciones estándar para mapear nuevos campos

Los siguientes métodos son para controlar el flujo entre la BBDD <---> Dynpro.

METODO TRANSPORT_FROM_MODEL.

Carga los datos desde la BBDD a la estructura de datos interna de


DATA: l_item       TYPE REF TO if_purchase_order_item_mm,
      ls_mepoitem  TYPE mepoitem,
      ls_customer  TYPE ZCI_EKPO_DB.

  CASE im_name.

    WHEN subscreen1.

      mmpur_dynamic_cast l_item im_model.
      CHECK NOT l_item IS INITIAL.

*     Recuperamos los datos actuales de la BD
      ls_mepoitem = l_item->get_data( ).

*     Recuperamos los actules de la transacción, pueden diferir
*     de los datos anteriores....
      CALL FUNCTION 'ZZMEPOBADIEX_GET_DATA'
        EXPORTING
          im_ebeln = ls_mepoitem-ebeln
          im_ebelp = ls_mepoitem-ebelp
        IMPORTING
          ex_data  = ls_customer.

      MOVE-CORRESPONDING ls_mepoitem TO dynp_data_pbo.
      MOVE ls_customer-ZZSOCIO TO dynp_data_pbo-ZZSOCIO.
      MOVE ls_customer-ZZFACT  TO dynp_data_pbo-ZZFACT.

    WHEN OTHERS.

  ENDCASE.

METODO TRANSPORT_TO_DYNP.

Actualiza los datos que nosotros vemos en la dynpro con los datos de la estructura interna

CASE im_name.

    WHEN subscreen1.

      CALL FUNCTION 'ZZMEPOBADIEX_PUSH'
        EXPORTING
          im_dynp_data = dynp_data_pbo.

    WHEN OTHERS.
  ENDCASE.

METODO TRANSPORT_FROM_DYNP.

Vuelca el contenido de los campos a medida en la dynpro a la estructura interna.

CASE im_name.

    WHEN subscreen1.

      CALL FUNCTION 'ZMEPOBADIEX_POP'
        IMPORTING
          ex_dynp_data = dynp_data_pai.

      IF dynp_data_pai NE dynp_data_pbo.
*       Si ha cambiado los datos, habra que notificarlo a la dynpro
        re_changed = mmpur_yes.
      ENDIF.

    WHEN OTHERS.

  ENDCASE.

METODO TRANSPORT_TO_MODEL.

Guardamos el contenido de la estructura dynp_data_pai en la BBDD

DATA: l_item       TYPE REF TO if_purchase_order_item_mm,
        ls_mepoitem  TYPE mepoitem,
        ls_customer  TYPE ZCI_EKPO_DB.


  CASE im_name.

    WHEN subscreen1.

*     ¿ Es un ITEM ? descartamos las cabeceras
      mmpur_dynamic_cast l_item im_model.
      CHECK NOT l_item IS INITIAL.

*     Recuperamos los datos
      ls_mepoitem = l_item->get_data( ).


*     Han Cambiado los datos ???? 
      IF ( dynp_data_pbo-ZZSOCIO NE dynp_data_pai-ZZSOCIO ) OR
         ( dynp_data_pbo-ZZFACT  NE dynp_data_pai-ZZFACT  ),


          CALL FUNCTION 'ZZMEPOBADIEX_GET_DATA'
            EXPORTING
             im_ebeln = ls_mepoitem-ebeln
             im_ebelp = ls_mepoitem-ebelp
           IMPORTING
             ex_data  = ls_customer.

         ls_customer-ZZSOCIO    = dynp_data_pai-ZZSOCIO.
         ls_customer-ZZFACT     = dynp_data_pai-FACT.

         CALL FUNCTION 'ZZMEPOBADIEX_SET_DATA'
           EXPORTING
              im_data = ls_customer.

ls_mepoitem-ZZSOCIO    = dynp_data_pai-ZZSOCIO.
         ls_mepoitem-ZZFACT     = dynp_data_pai-FACT.
         CALL METHOD l_item->set_data( ls_mepoitem ).
ENDIF. WHEN OTHERS. * ... ENDCASE.


Implementación de la Badi ME_PROCESS_PO_CUST
Para implementar la badi, comprobamos si ya hay implementaciones activas:
Desde la transacción SE18 -> Punto de ampliación -> ME_PROCESS_PO_CUST -> Visualizar


Bajo la pestaña Enhancem.Implementations aparecen las implementaciones existentes y las implementaciones activas.

implementaciones existentes y activas de ME_PROCESS_PO_CUST
Al igual que la anterior Badi, se puede implementar más de 1 vez, pero sólo una implementación puede estar activa al mismo tiempo. Si hay dos o mas implementaciones activas al mismo tiempo, saltara un DUMP al ejecutar cualquier transacción de compras.

Debemos crear una nueva implementación o modificamos una existente.

Para modificar una implementación existente, doble click soble la implementación en la pestaña Enhancem.Implementations del punto de ampliación ME_PROCESS_PO_CUST.

Para crear una nueva implementacion de la badi:

  1. Transacción SE18->Nre.Badi->ME_PROCESS_PO_CUST -> Visualizar.
  2. Dentro de la badi, en el menu superior Implementación -> Crear
  3. Introducir el nuevo nombre de la implementación. Por comprensión, suele ser el  ZZ + nombre de la badi.
  4. Introducir una descripción para la nueva implementación


Crear una nueva implementación de ME_PROCESS_PO_CUST
Nombre dela nueva implementación
Descripción de la nueva implementación

Dentro de la Badi, en la pestaña Interface, Es necesario implementar los siguientes métodos.
  • INITIALIZE
  • OPEN
  • PROCESS_ITEM
  • FIELDSELECTION_ITEM
Para implementarlos , hacer doble clic sobre el nombre del método
Implementación de métodos de la Badi ME_PROCESS_PO_CUST
METODO INITIALIZATE

* Rutinas de inicialización para los campos a medida
CALL FUNCTION 'ZZMEPOBADIEX_INIT'.




METODO OPEN

* Leer los datos del pedido de compra y cargar los datos
* de los campos a medida de la base de datos

DATA: ls_mepoheader TYPE mepoheader.

CHECK im_trtyp EQ 'V' OR im_trtyp EQ 'A'.

ls_mepoheader = im_header->get_data( ).

  CALL FUNCTION 'ZZMEPOBADIEX_OPEN'
    EXPORTING
      im_ebeln = ls_mepoheader-ebeln.

METODO PROCESS_ITEM

    DATA: ls_mepoitem TYPE mepoitem,
          ls_customer TYPE ZCI_EKPO_DB.


    ls_mepoitem = im_item->get_data( ).

    CALL FUNCTION 'ZZMEPOBADIEX_GET_DATA'
      EXPORTING
        im_ebeln = ls_mepoitem-ebeln
        im_ebelp = ls_mepoitem-ebelp
      IMPORTING
        ex_data  = ls_customer.

    MOVE ls_customer-ZZSOCIO TO ls_mepoitem-ZZSOCIO.
    MOVE ls_customer-ZZFACT  TO ls_mepoitem-ZZFACT.

    CALL METHOD im_item->set_data( ls_mepoitem ).

METODO FIELDSELECTION_ITEM 
Este método es muy importante porque determina las características de visualización de los campos dependiendo de la transacción que estemos ejecutando. Si no lo implementáis, los campos a medida serán editables en todas las transacciones y aunque nos encontremos en el modo de visualizar documento.
* Características de visualización de los campos
* '-' Field is suppressed
* '*' Field is purely a display field
* '.' Input field
* '+' Mandatory field

    DATA: wc_status  TYPE c.
    DATA: l_changeable TYPE mmpur_bool.
    FIELD-SYMBOLS:  <mapping> LIKE LINE OF ch_fieldselection.

    BREAK-POINT.

    l_changeable = IM_HEADER->is_changeable( ).
    IF l_changeable = 'X'.
      wc_status = '.'. " READY FOR INPUT
    ELSE.
      wc_status = '*'. " view
    ENDIF.

    LOOP AT ch_fieldselection ASSIGNING <mapping>.

      CASE <mapping>-metafield.
        WHEN mmmfd_cust_01.
          <mapping>-fieldstatus = wc_status.
        WHEN mmmfd_cust_02.
          <mapping>-fieldstatus = wc_status.
      ENDCASE.
    ENDLOOP.

Y si no hemos cometido algún error:
nuevos campos a medida en ME21N/ME22N/ME23
Campos a medida en la tabla EKPO



Algunos errores comunes:

  • La pestaña solo aparece en la ME23N
Comprueba el método ME_PROCESS_PO_CUST~FIELDSELECTION_ITEM.

  • Los campos son editables en la ME23N
Comprueba el método ME_PROCESS_PO_CUST~FIELDSELECTION_ITEM.
Comprueba los campos de la dynpro, que estén bien referenciados a la estructura.
Comprueba el método ME_GUI_PO_CUST~SUSCRIBE.

  • No se guardan los datos en la BD
Comprueba la bapi ZZMEPOBADIEX_SET_DATA.

martes, 1 de abril de 2014

Clasicos Abap: Subir un archivo Excel a una tabla interna II

Siguiendo con la anterior entrada, vamos a seguir explicando otras formas de subir archivos Excel a una tabla interna de SAP. En este  caso, vamos a utilizar un poco de programación dinámica para crear en tiempo de ejecución una tabla interna acorde al archivo Excel que vamos a subir.

Esta vez, voy a utilizar la bapi ALSM_EXCEL_TO_INTERNAL_TABLE para subir el fichero Excel.

Esta bapi, carga el Excel en una tabla interna estándar, donde las los primeras columnas son la fila y columna del campo y la tercera columna es el valor del campo. Es decir nos carga el Excel como si de coordenadas cartesianas se tratara ( coordenada X, coordenada Y , valor_de la celda ).

Upload archivo Excel a SAP
Que nos suba la información como coordenadas cartesianas, es una gran ventaja a la hora de diseñar el codigo. Por contra, esta bapi solo carga archivos que estén guardados en local,  en el ordenador donde se ejecuta, no sirve para cargar archivos desde un servidor.

Lo que mas me gusta de esta bapi es, que si utilizamos la primera linea como clave o enlace entre la columna del Excel y el elemento de datos de SAP correspondiente, con un poco de programación dinámica,  no necesitamos tener una tabla interna, con solo campos carácter,   exactamente igual que el Excel en el programa, podemos crear la tabla en tiempo de ejecución basándonos en la información del Excel.

Z_UPLOAD_EXCEL_TO_INTERNAL_TAB

Este programa de prueba, cargara un archivo Excel a Sap. La única condición es que la primera linea del archivo Excel, contiene el nombre del elemento de datos correspondiente en SAP.

La primera linea nos relaciona cada columna con su elemento de datos


*&---------------------------------------------------------------------*
*&      Form  UPLOAD_EXCEL_TO_ITAB
*&---------------------------------------------------------------------*
FORM upload_excel_to_itab USING pi_file   TYPE TYPE localfile 
                                pi_row    TYPE i
                                pi_column TYPE i
                          CHANGING TO_EXCEL_TAB TYPE type_excel_tab.

  REFRESH: TO_EXCEL_TAB.
  
  CALL FUNCTION 'ALSM_EXCEL_TO_INTERNAL_TABLE'
    EXPORTING
      filename                = pi_file
      i_begin_col             = 1
      i_begin_row             = 1
      i_end_col               = pi_column
      i_end_row               = pi_row 
    TABLES
      intern                  = TO_EXCEL_TAB
    EXCEPTIONS
      inconsistent_parameters = 1
      upload_ole              = 2
      OTHERS                  = 3.

  IF sy-subrc <> 0.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
            WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

ENDFORM.                    " UPLOAD_EXCEL_TO_ITAB

Es necesario indicar la fila, columna de inicio del excel y hasta que fila, columna del Excel queremos leer, no os preocupéis , si el numero de filas , columnas existentes en el archivo Excel es menor que el indicado, no salta ningún error, ni rellena con blancos, simplemente para de leer.

Llegamos a la parte dinámica, vamos a construir una tabla interna en tiempo de ejecución , en base a los datos de la primera linea del Excel. 

*&---------------------------------------------------------------------*
*&      Form  CREATE_DYNAMIC_ITAB_WA
*&---------------------------------------------------------------------*
FORM create_dynamic_itab_wa USING TI_EXCEL_TAB TYPE type_excel_tab.

  DATA: lr_datdr  TYPE REF TO cl_abap_datadescr,
        lr_struc  TYPE REF TO cl_abap_structdescr,
        gw_comp   TYPE abap_componentdescr,
        gt_comp   TYPE abap_component_tab.

  FIELD-SYMBOLS: <FS_EXCEL> TYPE LINE OF type_excel_tab.

  LOOP AT  TI_EXCEL_TAB ASSIGNING <FS_EXCEL> WHERE row EQ '1' .
    lr_datdr ?= cl_abap_datadescr=>describe_by_data( <FS_EXCEL>-VALUE ).
    gw_comp-name = <FS_EXCEL>-VALUE.
    gw_comp-type = lr_datdr.
    APPEND gw_comp  TO gt_comp. CLEAR gw_comp.
  ENDLOOP.

  TRY.
    lr_struc = cl_abap_structdescr=>create( p_components = gt_comp ).
  CATCH cx_sy_struct_creation.
    WRITE: / 'CX_SY_STRUCT_CREATION ERROR'.
  ENDTRY.

  CREATE DATA x_dyn_wa TYPE HANDLE lr_struc.
  ASSIGN x_dyn_wa->*  TO <FS_WA>.
  CREATE DATA t_dyn_tab LIKE STANDARD TABLE OF <FS_WA>.
  ASSIGN t_dyn_tab->* TO <FS_TABLE>.

ENDFORM.                    " CREATE_DYNAMIC_ITAB_WA

Y por ultimo,transportar los datos del Excel a la nueva tabla interna.


*&---------------------------------------------------------------------*
*&      Form  FILL_DATA
*&---------------------------------------------------------------------*
FORM fill_data USING TI_EXCEL_TAB TYPE type_excel_tab.

  FIELD-SYMBOLS: <FS_EXCEL> TYPE LINE OF type_excel_tab,
                 <FS_C>     TYPE LINE OF type_excel_tab,
                 <FS_FIELD> TYPE ANY.

  LOOP AT ti_excel_tab ASSIGNING <FS_EXCEL> WHERE row NE '1' . " 1 -> cabecera
*  ¿Que columna estamos tratando ?
   READ TABLE ti_excel_tab ASSIGNING <FS_C> WITH KEY ROW = '1' COL = <FS_EXCEL>-COL.
   CHECK ( SY-SUBRC EQ 0 ).

   READ TABLE <FS_TABLE> ASSIGNING <FS_WA> INDEX ( <FS_EXCEL>-ROW - 1 ).
   IF ( SY-SUBRC <> 0 ).
     APPEND INITIAL LINE TO <FS_TABLE>  ASSIGNING <FS_C>.
   ENDIF.

   ASSIGN COMPONENT <FS_C>-VALUE OF STRUCTURE <FS_TABLE> TO <FS_FIELD>.
   CHECK ( Sy-SUBRC EQ 0 ).

*  Realizar las conversiones de datos necesarias ahora
   <FS_FIELD> = <FS_EXCEL>-VALUE.

   ENDLOOP.

ENDFORM.                    " FILL_DATA





A partir de este punto, ya podemos leer, modificar o mostrar por pantalla en un ALV los datos.


A continuación, os dejo el código completo del programa.

*&---------------------------------------------------------------------*
*& Report  Z_UPLOAD_EXCEL_TO_INTERNAL_TAB
*& AUTOR: David Rueda barrón
*&---------------------------------------------------------------------*
REPORT  z_upload_excel_to_internal_tab.

*&---------------------------------------------------------------------*
*& Selection Screen
*&---------------------------------------------------------------------*
PARAMETERS: p_file   TYPE localfile OBLIGATORY,
            p_row    TYPE i         OBLIGATORY,
            p_column TYPE i         OBLIGATORY.

*&---------------------------------------------------------------------*
*& Constants
*&---------------------------------------------------------------------*
CONSTANTS: c_fs_wa       TYPE lvc_fname VALUE '-',
           c_ext_xls     TYPE string    VALUE '*.xls'.

*&---------------------------------------------------------------------*
*& Structure
*&---------------------------------------------------------------------*
TYPES: type_excel_tab TYPE  STANDARD TABLE OF alsmex_tabline.

*&---------------------------------------------------------------------*
*& Internal Tables
*&---------------------------------------------------------------------*
DATA: ti_excel_tab TYPE type_excel_tab.

*&---------------------------------------------------------------------*
*& Reference Variables
*&---------------------------------------------------------------------*
DATA: t_dyn_tab   TYPE REF TO data,
      x_dyn_wa    TYPE REF TO data.

*&---------------------------------------------------------------------*
*& Field Symbols
*&---------------------------------------------------------------------*
FIELD-SYMBOLS : <FS_TABLE> TYPE STANDARD TABLE,
                <FS_WA>    TYPE ANY.

*&---------------------------------------------------------------------*
*& At Selection-Screen
*&---------------------------------------------------------------------*
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
  PERFORM f4_open_file.  "Ayuda Busqueda, navegador de archivos

*&---------------------------------------------------------------------*
*& Start-of-Selection
*&---------------------------------------------------------------------*
START-OF-SELECTION.

  PERFORM upload_excel_to_itab USING  p_file
                                      p_row
                                      p_column
                               CHANGING ti_excel_tab.



  IF ti_excel_tab IS NOT INITIAL.

    PERFORM create_dynamic_itab_wa USING ti_excel_tab.
    PERFORM fill_data              USING ti_excel_tab.
    PERFORM display_output         USING ti_excel_tab.

  ELSE.
    MESSAGE text-s01 TYPE 'I'.
  ENDIF.

*&---------------------------------------------------------------------*
*&      Form  F4_OPEN_FILE
*&---------------------------------------------------------------------*
FORM f4_open_file.

  DATA: lt_filetable    TYPE filetable,
        lx_filetable    TYPE file_table,
        lv_return_code  TYPE i,
        lv_window_title TYPE string.

  lv_window_title = text-001.

  CALL METHOD cl_gui_frontend_services=>file_open_dialog
    EXPORTING
      window_title            = lv_window_title
      default_extension       = c_ext_xls
    CHANGING
      file_table              = lt_filetable
      rc                      = lv_return_code
    EXCEPTIONS
      file_open_dialog_failed = 1
      cntl_error              = 2
      error_no_gui            = 3
      not_supported_by_gui    = 4
      OTHERS                  = 5.

  IF sy-subrc <> 0.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
               WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

  READ TABLE lt_filetable INTO lx_filetable INDEX 1.
  p_file = lx_filetable-filename.

ENDFORM.                    " F4_OPEN_FILE
*&---------------------------------------------------------------------*
*&      Form  UPLOAD_EXCEL_TO_ITAB
*&---------------------------------------------------------------------*
FORM upload_excel_to_itab USING pi_file   TYPE localfile
                                pi_row    TYPE i
                                pi_column TYPE i
                          CHANGING TO_EXCEL_TAB TYPE type_excel_tab.

  REFRESH: TO_EXCEL_TAB.

  CALL FUNCTION 'ALSM_EXCEL_TO_INTERNAL_TABLE'
    EXPORTING
      filename                = pi_file
      i_begin_col             = 1
      i_begin_row             = 1
      i_end_col               = pi_column
      i_end_row               = pi_row
    TABLES
      intern                  = TO_EXCEL_TAB
    EXCEPTIONS
      inconsistent_parameters = 1
      upload_ole              = 2
      OTHERS                  = 3.

  IF sy-subrc <> 0.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
            WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

ENDFORM.                    " UPLOAD_EXCEL_TO_ITAB
*&---------------------------------------------------------------------*
*&      Form  CREATE_DYNAMIC_ITAB_WA
*&---------------------------------------------------------------------*
FORM create_dynamic_itab_wa USING TI_EXCEL_TAB TYPE type_excel_tab.

  DATA: lr_datdr  TYPE REF TO cl_abap_datadescr,
        lr_struc  TYPE REF TO cl_abap_structdescr,
        gw_comp   TYPE abap_componentdescr,
        gt_comp   TYPE abap_component_tab.

  FIELD-SYMBOLS: <FS_EXCEL> TYPE LINE OF type_excel_tab.

  LOOP AT  TI_EXCEL_TAB ASSIGNING <FS_EXCEL> WHERE row EQ '1' .

    lr_datdr ?= cl_abap_datadescr=>describe_by_data( <FS_EXCEL>-VALUE ).
    gw_comp-name = <FS_EXCEL>-VALUE.
    gw_comp-type = lr_datdr.
    APPEND gw_comp  TO gt_comp. CLEAR gw_comp.

  ENDLOOP.

  TRY.
    lr_struc = cl_abap_structdescr=>create( p_components = gt_comp ).
  CATCH cx_sy_struct_creation.
    WRITE: / 'CX_SY_STRUCT_CREATION ERROR'.
  ENDTRY.

  CREATE DATA x_dyn_wa TYPE HANDLE lr_struc.
  ASSIGN x_dyn_wa->*  TO <FS_WA>.
  CREATE DATA t_dyn_tab LIKE STANDARD TABLE OF <FS_WA&gt.
  ASSIGN t_dyn_tab->* TO <FS_TABLE&gt.

ENDFORM.                    " CREATE_DYNAMIC_ITAB_WA
*&---------------------------------------------------------------------*
*&      Form  FILL_DATA
*&---------------------------------------------------------------------*
FORM fill_data USING TI_EXCEL_TAB TYPE type_excel_tab.

  FIELD-SYMBOLS: <FS_EXCEL> TYPE LINE OF type_excel_tab,
                 <FS_C>     TYPE LINE OF type_excel_tab,
                 <FS_FIELD> TYPE ANY.

  LOOP AT ti_excel_tab ASSIGNING <FS_EXCEL> WHERE row NE '1' . " 1 -> cabecera
*  ¿Que columna estamos tratando ?
   READ TABLE ti_excel_tab ASSIGNING <FS_C> WITH KEY ROW = '1' COL = <FS_EXCEL>-COL.
   CHECK ( SY-SUBRC EQ 0 ).

   READ TABLE <FS_TABLE> ASSIGNING <FS_WA> INDEX ( <FS_EXCEL>-ROW - 1 ).
   IF ( SY-SUBRC <> 0 ).
     APPEND INITIAL LINE TO <FS_TABLE>  ASSIGNING <FS_C>.
   ENDIF.

   ASSIGN COMPONENT <FS_C>-VALUE OF STRUCTURE <FS_TABLE> TO <FS_FIELD>.
   CHECK ( Sy-SUBRC EQ 0 ).

*  Realizar las conversiones de datos necesarias ahora
   <FS_FIELD> = <FS_EXCEL>-VALUE.

   ENDLOOP.

ENDFORM.                    " FILL_DATA

*&---------------------------------------------------------------------*
*&      Form  DISPLAY_OUTPUT
*&---------------------------------------------------------------------*
FORM display_output USING TI_EXCEL_TAB TYPE type_excel_tab.

  DATA: lo_table     TYPE REF TO cl_salv_table,
        lo_functions TYPE REF TO cl_salv_functions_list,
        lo_column    TYPE REF TO cl_salv_column,
        lo_columns   TYPE REF TO cl_salv_columns.

  DATA: lv_short     TYPE scrtext_s,
        lv_medium    TYPE scrtext_m,
        lv_long      TYPE scrtext_l.

  DATA: wl_ddtext TYPE dd04t-ddtext,
        wl_FNAME  TYPE LVC_FNAME.

  FIELD-SYMBOLS: <FS_EXCEL> TYPE LINE OF type_excel_tab.

  TRY.
      CALL METHOD cl_salv_table=>factory
        IMPORTING
          r_salv_table = lo_table
        CHANGING
          t_table      = <FS_TABLE>.
    CATCH cx_salv_msg .                                 "#EC NO_HANDLER
  ENDTRY.


  lo_functions = lo_table->get_functions( ).
  lo_functions->set_all( abap_true ).

  lo_columns = lo_table->get_columns( ).

  lo_columns->set_optimize( abap_true ).

   LOOP AT  TI_EXCEL_TAB ASSIGNING <FS_EXCEL>.

    CLEAR: wl_ddtext, wl_FNAME .

    wl_FNAME = <FS_EXCEL>-VALUE.
    TRANSLATE wl_FNAME TO UPPER CASE.

    SELECT SINGLE ddtext
      INTO wl_ddtext
      FROM dd04t
      WHERE rollname   = wl_FNAME
        AND ddlanguage = sy-langu
        AND as4local   = 'A'.

    lv_short  = wl_ddtext+0(10).
    lv_medium = wl_ddtext+0(20).
    lv_long   = wl_ddtext.


      TRY.
          lo_column = lo_columns->get_column( wl_FNAME  ).
          lo_column->set_short_text( lv_short ).
          lo_column->set_medium_text( lv_medium ).
          lo_column->set_long_text( lv_long ).
        CATCH cx_salv_not_found.                        "#EC NO_HANDLER
      ENDTRY.


  ENDLOOP.

  lo_table->display( ).

ENDFORM.                    " DISPLAY_OUTPUT


Entradas relacionadas:
Clasicos ABAP: Descargar tabla interna a una hoja Excel
Clasicos Abap: Subir un archivo Excel a una tabla interna