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 

martes, 4 de febrero de 2014

ANST - Automated Note Search Tool

Gracias a un tweet de  @oarranzli, he descubierto la nueva herramienta de sap: ANST - "Automated Note Search Tool"

Esta nueva herramienta, cuando se produzca un dump en el sistema,  nos ayudara a reducir el tiempo que gastamos en navegar por el Sap Support Portal para encontrar la nota que lo corrige, automáticamente nos dará un listado de notas de corrección relacionadas con el dump.

ANST ha superado el periodo de pruebas de dos años y ahora es una transacción estándar de SAP.
ANST se encuentra disponible en cualquier sistema sap a partir de los siguientes niveles, en el modulo SAP_BASIS:
  • SAP_BASIS 700 nivel 28 Support Package SAPKB70028
  • SAP_BASIS 701 nivel 13 Support Package SAPKB70113
  • SAP_BASIS 702 nivel 13 Support Package SAPKB70213
  • SAP_BASIS 731 nivel 06 Support Package SAPKB73106

Los requerimientos mínimos y funcionamiento de ANST se explican en las siguientes notas:

La mejor forma de entender las ventajas de esta nueva herramienta es ver el siguiente vídeo, cortesía de Carlos Martínez Escribano:



Antes que os lancéis corriendo a probar la herramienta, tener en cuenta las siguientes notas, es posible que las necesitéis.


Aplicar nota 1909768 para rellenar la tabla con los valores correctos
Al acceder a la transacción ANST_SEARCH_TOOL da un error de que falta rellenar la tabla de customizing.
Para solucionar el problema, aplicar la siguiente nota 1909768 de SAP.


Transacción ANST_SEARCH_TOOL

Esta nota explica como acortar el nombre de la transacción ANST_SEARCH_TOOL a solamente ANST , mucho mas fácil de manejar.

Fuentes:
The power of tools - How ANST can help you to solve billing problems yourself!
SAP ANST: experience the magic...
What is ANST....and why aren't you using it?

viernes, 10 de enero de 2014

Cerrado por reformas

Este mes estoy bastante ocupado con otros proyectos, tengo que entregar un trabajo para el master que estoy haciendo y lo mas importante, no retrasarme en los proyectos en los que trabajo.
Aunque tengo  tengo nuevas ideas para el blog ,que espero que os gusten, también necesito tiempo para desarrollarlas.

En breves prometo volver. Gracias a todos!!!




lunes, 16 de diciembre de 2013

Definir una impresora PDF en Sap

Es un tema muy común que piden muchos clientes, la posibilidad de generar un documento PDF en vez de imprimir el documento o listado en papel a través de un impresora.

Primero,  en el equipo local debe estar configurada una impreso tipo Generic/ Text Only y que como formato de salida tenga FILE:

Ir a  Inicio -> Panel de control -> Dispositivos e Impresoras.

Seleccionamos Agregar una impresora -> Agregar una impresora local
Cuando nos pida elegir un puerto para la impresora -> FILE


Puerto FILE: la salida sera un archivo

Como controlador o driver, selecciona Generic -> Generic text only.

Dejar el nombre que nos da por defecto para la impresora o escribir uno nuevo y siguiente.
Para finalizar, como se ejecutara locamente, yo le doy no compartir y desmarco impresora predeterminada


Controlador Generic/Text Only

Para definir una impresora PDF en Sap, nos vamos a la transacción SPAD y  pulsar el botón Visual del campo Dispositivos salida de la pestaña Dispos.y servidor.


Definir nueva impresora en el sistema Sap
En pantalla aparece una lista de todos los dispositivos de impresión existente.
Nos posicionamos sobre cualquiera de ellos y pulsamos el lápiz en la barra de herramientas

Modificar Dispositivo de salida

Aparecerá el icono CREAR en la barra de herramientas, pulsa sobre el .

Crear nuevo dispositivo de salida

Pestaña Atrib.dispos. -> Dispositivo de salida:  nombre del dispistivo de salida en sap, por ejemplo PDF_PRUEBA.
Pestaña Atrib.dispos. -> Nombre breve: PDF1
Pestaña Atrib.dispos. -> Tipo de dispositivo:  PDF1 ISO Latin-1



En la pestaña Acoplam. SPOOL host para Forma acopl.a SPOOL host -> seleccionamos G: Impresión front-end c/tecnologia control.

En Impresora Host escribimos cualquier nombre y grabamos el nuevo dispositivo de salida

Para probarlo, por ejemplo, podemos usar la transacción SM04 he intentar imprimir en pdf la lista de usuarios conectados al sistema Sap. 

SM04 y si impresión en PDF

Entradas relacionadas
Definir una nueva impresora en SAP
Upload drivers de impresora a SAP

jueves, 12 de diciembre de 2013

Definir una nueva impresora en SAP

Para definir nuevas impresoras en SAP , nos vamos a la transacción SPAD  , pulsar el botón Visual del campo Dispositivos salida de la pestaña Dispos.y servidor.


Definir nueva impresora en el sistema Sap
En pantalla aparece una lista de todos los dispositivos de impresión existente.
Nos posicionamos sobre cualquiera de ellos y pulsamos el lápiz en la barra de herramientas

Modificar Dispositivo de salida

Aparecerá el icono CREAR en la barra de herramientas, pulsa sobre el .

Crear nuevo dispositivo de salida

En la siguiente pantalla, vamos primero a la pestaña Atrib.dispos.

Dispositivo salida: Asignamos un nombre para Sap a la impresora.
Tipo de dispositivo, si es impresora láser podéis usar HPLJ4 o cualquiera que indique el fabricante que sea compatible con Sap.

Pestaña Atrib.dispos

En la Pestaña Acoplam.SPOOL host -> Forma acopl.a SPOOL host , como se va ha comunicar Sap con la impresora y como le va ha transmitir los datos:
  • F:  Si queremos imprimir a través de la impresora predeterminada en windows en el equipo local
  • C: Cuando se realiza a través de un servidor de impresión.
  • L:  Si el servidor de impresión se ejecuta sobre un sistema Unix
  • G:  Cuando la salida no es una impresora sino un programa externo ( ej: PDF )
Existen mas formas , pero yo soló he necesitado usar estas 4. Aquí esta la lista completa.

Impresora Host: nombre REAL o ID de la impresora configurada en el servidor de impresión.
Sí hemos seleccionado F como Forma de acoplamiento sera __DEFAULT.

Acoplam.SPOOL host


Volvemos a la primera pestaña y ahora podemos introducir el servidor de impresión (si hemos seleccionado una forma de acoplamiento diferente de F ó G). Si no sabéis cual es podéis usar la ayuda búsqueda.

Ayuda búsqueda para el servidor de impresión

Por ultimo , grabar todo y probar a imprimir algún formulario o  lista para comprobar que todo esta bien

Seleccionando el nuevo disp. de salida para imprimir

Fuente: SAP Printing Guide

martes, 10 de diciembre de 2013

Upload drivers de impresora a SAP

Lo primero, conseguir los drivers de la impresora. Lo mas seguro es que podamos descargarlos en la pagina oficial del fabricante de la impresora o bien desde el Sap Service Marketplace. La gran mayoría de los fabricantes como HP, Cannon, Zebra o  Epson tienen drivers para sap. 

Podemos consultar las siguientes  notas de Sap, para encontrar los link de los drivers de los principales fabricantes de impresoras y comprobar si sin compatibles con Sap:
Cuando me ha tocado trabajar con impresoras de etiquetas, estos link me han sido muy utilies:
Si  vuestra impresora no esta soportada, solo os queda buscar un driver genérico.

Una vez tenemos el driver de la impresora, Ejecutar el programa RSTXSCRP desde la transacción SE38.

Rellenar los siguientes campos de la pantalla de selección: 
  1. Marcar Tipo dispositivo.
  2. En Nombre del objeto introducir un nombre que empeze con Z por el cual se identifica los drivers en SAP.
  3. Modo IMPORT
  4. En Fichero aquí va la ruta al fichero que contiene los driver de la impresora.
Por ejemplo, en este caso voy a hacer un upload de los drivers para una HP laserjet 4300:

Upload driver para HP LaserJet 4300
Ejecutar ( F8 ) y ya podemos seleccionar los nuevos drivers desde la SPAD:

Driver ya esta cargado en Sap