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.