jueves, 2 de mayo de 2013

Clasicos ABAP: Descargar tabla interna a una hoja Excel

Esto es un clásico de la programación ABAP que no puede faltar en todo desarrollo , proyecto , listado , etc... siempre habrá un usuario que te pedirá que el listado 'A', 'B' o 'C' tenga la opción de descargarse  a una hoja Excel.

Para descargar una tabla interna a una hoja de excel:  bapi MS_EXCEL_OLE_STANDARD_DAT

IMPORTANTE: Para que esta bapi os funcione, es necesario que la aplicación Microsoft EXCEL este instala en el equipo local.  Esta bapi envía los datos al SapGUI y después al EXCEL, por tanto tampoco funcionara si ejecutáis el programa como un job en fondo o si queréis que el fichero se cree en el servidor o en una unidad de red.

Si  la tabla interna tiene campos numéricos, por ejemplo integer, float... al ejecutar la bapi MS_EXCEL_OLE_STANDARD_DAT saltara un precioso dump. Esto esta relacionado con lo que anteriormente os he comentado, la Bapi no realiza ningún ajuste, transformación o cast con los datos, lo que puede derivar en problemas con campo numérico, decimales o fechas.

Hay que crear otra tabla interna,  idéntica a la que queremos descargar a excel, pero con todos sus campos de tipo carácter. Traspasaremos los registros a la nueva tabla y descargaremos el contenido de la tabla a una hoja excel con la bapi MS_EXCEL_OLE_STANDARD_DAT.

Un ejemplo vale mas que  mil palabras....

Listado ALV que muestra los vuelos para las compañías y fechas seleccionadas:

Nota: solo voy a pegar las partes importantes del código, si queréis el código completo del ejemplo lo tenéis aquí.


Añadir un pulsador para descargar el ALV a una hoja excel y el siguiente código para controlar el evento.

*---------------------------------------------------------------------*
*       FORM USER_COMMAND_01  Process Call Back Events (Begin)         *
*---------------------------------------------------------------------*
FORM user_command_01 USING ucomm    LIKE sy-ucomm
                        selfield TYPE slis_selfield.

  DATA: wl_file TYPE string.

  CASE ucomm.
    WHEN '&EXC'.
      PERFORM get_filename  CHANGING wl_file.
      PERFORM download_to_exc  USING wl_file ti_spfli.
  ENDCASE.
ENDFORM.                    "user_command_01

*---------------------------------------------------------------------*
*      Form  GET_FILENAME
*---------------------------------------------------------------------*
*     Abrir navegador para seleccionar fichero
*----------------------------------------------------------------------*
FORM get_filename  CHANGING p_file TYPE string.

  DATA w_file LIKE rlgrap-filename.

  CALL FUNCTION 'KD_GET_FILENAME_ON_F4'
    CHANGING
      file_name     = w_file
    EXCEPTIONS
      mask_too_long = 1
      OTHERS        = 2.

  p_file = w_file.

ENDFORM.                      "GET_FILENAME

*---------------------------------------------------------------------*
*      Form  DOWNLOAD_TO_EXC
*---------------------------------------------------------------------*
FORM download_to_exc  USING Pi_file  TYPE STRING
                            ti_spfli TYPE type_t_spfli.

  TYPES: BEGIN OF tl_name,
    fldname(11) TYPE c,
   END OF tl_name.

  DATA: tl_name  TYPE STANDARD TABLE OF tl_name.

  DATA: wl_file TYPE  rlgrap-filename.

  FIELD-SYMBOLS:  <FL> TYPE tl_name.

  wl_file = Pi_file.

  LOOP AT gt_fieldcat .
    APPEND INITIAL LINE TO tl_name ASSIGNING <FL>.
    <FL>-fldname =  gt_fieldcat-fieldname.
  ENDLOOP.

  CALL FUNCTION 'MS_EXCEL_OLE_STANDARD_DAT'
    EXPORTING
      file_name                 = wl_file
      data_sheet_name           = 'SPFLI'
      password_option           = 0
    TABLES
      data_tab                  = ti_spfli
      fieldnames                = tl_name
    EXCEPTIONS
      file_not_exist            = 1
      filename_expected         = 2
      communication_error       = 3
      ole_object_method_error   = 4
      ole_object_property_error = 5
      invalid_filename          = 6
      invalid_pivot_fields      = 7
      download_problem          = 8
      OTHERS                    = 9.

  CASE sy-subrc.
    WHEN 1.
      MESSAGE e000(yv01) WITH 'File does not exist'.
    WHEN 2.
      MESSAGE e000(yv01) WITH 'Filename expected'.
    WHEN 3.
      MESSAGE e000(yv01) WITH 'Communication error'.
    WHEN 4.
      MESSAGE e000(yv01) WITH 'OLE object method error'.
    WHEN 5.
      MESSAGE e000(yv01) WITH 'OLE object property error'.
    WHEN 6.
      MESSAGE e000(yv01) WITH 'Invalid filename'.
    WHEN 7.
      MESSAGE e000(yv01) WITH 'Invalid pivot fields'.
    WHEN 8.
      MESSAGE e000(yv01) WITH 'Download problem'.
    WHEN 9.
      MESSAGE e000(yv01) WITH 'Other problem'.
  ENDCASE.


ENDFORM.                    " DOWNLOAD_TO_EXC


La tabla interna TL_NAME son las cabeceras de las columnas. Si comentáis el parámetro de entrad de la bapi o le pasáis una tabla interna vacía, generara la hoja excel sin cabecera para las columnas.

Preguntamos donde quiere guardar la hoja excel


Tabla interna descargada a hoja excel

En este ejemplo , como todos los campos son de tipo caracter ( CONNID es NUMC, texto numérico ) , no existe ningun problema en pasar directamente la tabla a la bapi MS_EXCEL_OLE_STANDARD_DAT. Pero, si añadimos un nuevo campo numerico a la tabla interna:

TYPES: BEGIN OF type_spfli,
  carrid     TYPE spfli-carrid,    "compañía aérea
  connid     TYPE spfli-connid,    "Código de conexión de vuelo directo
  countryf   TYPE spfli-countryfr, "Clave de país
  cityfrom   TYPE spfli-cityfrom,  "Ciudad de salida
  airpfrom   TYPE spfli-airpfrom,  "Aeropuerto de salida
  countryto  TYPE spfli-countryto, "Clave de país
  cityto     TYPE spfli-cityto,    "Ciudad de llegada
  airpto     TYPE spfli-airpto,    "Aeropuerto de destino
  integer    TYPE i,               "CAMPO NUMERICO
END OF type_spfli.

Aunque el campo integer este vació en todos los registros, al descargar a excel dará el siguiente DUMP

sólo se permiten caracteres
Hay que traspasar, antes de llamar a la bapi, todo el contenido de la tabla ti_spfli a una tabla cuyos campos sean todos de tipo caracter.



*---------------------------------------------------------------------*
*      Form  DOWNLOAD_TO_EXC
*---------------------------------------------------------------------*
FORM download_to_exc  USING pi_file  TYPE string
                            ti_spfli TYPE type_t_spfli.

  TYPES: BEGIN OF tl_name,
    fldname(11) TYPE c,
   END OF tl_name.

  TYPES: BEGIN OF type_excel,
    carrid(3)    TYPE c,
    connid(4)    TYPE c,
    countryfr(3) TYPE c,
    cityfrom(20) TYPE c,
    airpfrom(3)  TYPE c,
    countryto(3) TYPE c,
    cityto(20)   TYPE c,
    airpto(3)    TYPE c,
    integer(15)  TYPE c,
  END OF type_excel.

  DATA: tl_name  TYPE STANDARD TABLE OF tl_name.
  DATA: tl_excel TYPE STANDARD TABLE OF type_excel.

  DATA: wl_file TYPE  rlgrap-filename.

  FIELD-SYMBOLS:  <FN> TYPE tl_name,
                  <FO> TYPE TYPE_spfli,
                  <FD> TYPE type_excel.

  wl_file = pi_file.

  LOOP AT gt_fieldcat .
    APPEND INITIAL LINE TO tl_name ASSIGNING <FN>.
    <FN>-fldname =  gt_fieldcat-fieldname.
  ENDLOOP.

  LOOP AT ti_spfli ASSIGNING <FO>.

    APPEND INITIAL LINE TO TL_EXCEL ASSIGNING <FD>.

    MOVE: <FO>-carrid    TO <FD>-CARRID,
          <FO>-connid    TO <FD>-CONNID,
          <FO>-countryfr TO <FD>-countryfr,
          <FO>-cityfrom  TO <FD>-cityfrom,
          <FO>-airpfrom  TO <FD-airpfrom,
          <FO>-countryto TO <FD>-countryto,
          <FO>-cityto    TO <FD>-cityto,
          <FO>-airpto    TO <FD>-airpto.

    WRITE <FO>-integer TO <FD>-INTEGER.

  ENDLOOP.

  CALL FUNCTION 'MS_EXCEL_OLE_STANDARD_DAT'
    EXPORTING
      file_name                 = wl_file
      data_sheet_name           = 'SPFLI'
      password_option           = 0
    TABLES
      data_tab                  = TL_EXCEL
      fieldnames                = tl_name
    EXCEPTIONS
      file_not_exist            = 1
      filename_expected         = 2
      communication_error       = 3
      ole_object_method_error   = 4
      ole_object_property_error = 5
      invalid_filename          = 6
      invalid_pivot_fields      = 7
      download_problem          = 8
      OTHERS                    = 9.

  CASE sy-subrc.
    WHEN 1.
      MESSAGE e000(yv01) WITH 'File does not exist'.
    WHEN 2.
      MESSAGE e000(yv01) WITH 'Filename expected'.
    WHEN 3.
      MESSAGE e000(yv01) WITH 'Communication error'.
    WHEN 4.
      MESSAGE e000(yv01) WITH 'OLE object method error'.
    WHEN 5.
      MESSAGE e000(yv01) WITH 'OLE object property error'.
    WHEN 6.
      MESSAGE e000(yv01) WITH 'Invalid filename'.
    WHEN 7.
      MESSAGE e000(yv01) WITH 'Invalid pivot fields'.
    WHEN 8.
      MESSAGE e000(yv01) WITH 'Download problem'.
    WHEN 9.
      MESSAGE e000(yv01) WITH 'Other problem'.
  ENDCASE.


ENDFORM.                    " DOWNLOAD_TO_EXC



Después de ajustar el código, si volvemos a ejecutar y descargar a excel:


Descargar a hoja excel tabla interna con campos numéricos
Notas relacionadas:
   721793 - Problems with MS_EXCEL_OLE_STANDARD_DAT
   104165 - Problems with MS EXCEL-download from HR-reports

30 comentarios:

  1. Y a un excel creado añadirle una linea encima de la tabla???

    ResponderEliminar
  2. Hola, sabes si hay alguna forma de lograr que el texto de uno de los campos salga en varias lineas dentro de su campo?

    ResponderEliminar
    Respuestas
    1. Puedes utilizar el separador cl_abap_char_utilities=>NEWLINE. Prueba así y me cuentas

      DATA: C_NEW_LINE type c value cl_abap_char_utilities=>NEWLINE.
      DATA: WL_MULTIPLE_LINE(10) TYPE C.

      CONCATENATE 'AA' 'BB' 'CC' INTO WL_MULTIPLE_LINE SEPARATED BY C_NEW_LINE.

      Eliminar
  3. Hola, habrá alguna manera de hacer que un campo String de mi tabla con el valor 6897653452769 llegué a salir así y no con notación científica 6.89765E+12 ??

    ResponderEliminar
  4. Espero puedas ayudarme, gracias (comento de nuevo para que me notifique si me respondes, olvide marcar la opción en el comentario anterior jajajaja)

    ResponderEliminar
    Respuestas
    1. Creo que se ha perdido tu pregunta :)

      Eliminar
    2. Cierto, que raro. Me impresiono tu rápida respuesta!! Ojala sea de nuevo así jajaj para poder resolver esto. Replique tu ejemplo, y un par de cosas no me quedaron igual (no me aparecen todos los nombres de las columnas en la tabla que se muestra por pantalla) pero lo que mas me urge es que no me genera el archivo, me sale error 4: "OLE object method error".

      Espero puedas ayudarme. Gracias!!!

      Eliminar
    3. "OLE object method error" -> Revisa las dos tablas que le pasas data_tab / fieldname.
      Por cada columna de data_tab tienes que tener una entrada en fieldname con el nombre de esa columna.
      Lo que pasa es que en este ejemplo aprovecho que el catalogo del ALV coincide exactamente con la tabla que quiero bajar.

      Eliminar
    4. Yo creo que deberías revisar como creas el catalogo de campos del alv .

      Eliminar
    5. Hola David, buenos dias!!!

      Estoy usando la función MS_EXCEL_OLE_STANDARD_DAT para descargar a un excel las caracteristicas de un material.

      *CALL FUNCTION 'MS_EXCEL_OLE_STANDARD_DAT'
      * EXPORTING
      * FILE_NAME = LV_FILE
      * TABLES
      * DATA_TAB = IT_CARACTERISTICAS
      * FIELDNAMES = IT_CABECERA
      * EXCEPTIONS
      * FILE_NOT_EXIST = 1
      * FILENAME_EXPECTED = 2
      * COMMUNICATION_ERROR = 3
      * OLE_OBJECT_METHOD_ERROR = 4
      * OLE_OBJECT_PROPERTY_ERROR = 5
      * INVALID_PIVOT_FIELDS = 6
      * DOWNLOAD_PROBLEM = 7
      * OTHERS = 8

      Está funcionando fenomenal, pero he visto un par de cosillas:

      1.- El nº de material viene sin 0 por delante. Necesitaría que los llevase.
      2.- Los importes vienen con comas. Necesitaríamos que fuese con puntos.

      Aunque antes de llamar a la función está bien formateado, al generar el fichero, aparece así.

      Si me puedes echar una manilla, te lo agradecería!!!

      Un saludo.

      Raúl

      Eliminar
  5. Revise lo que me indicas, y también necesito exportar lo que tiene el mismo catalogo del ALV.
    Al definir el ALV, lo hago asi: http://prntscr.com/57mju6 . Siguiendo tu ejemplo.

    Lo que se entrega en las tablas de data_tab y fieldname, segun el debug es esto: http://prntscr.com/57mkqx

    Y ambas tablas tienen la información necesaria:
    tabla 1: http://prntscr.com/57mkv6
    tabla 2: http://prntscr.com/57ml04

    Lo deje con capturas para que fuera mas fácil verlo. Segui al pie de la letra tu ejemplo, solo cambie los nombres de las variables y de la tabla que se usa =/.

    Una vez más, gracias!!!

    ResponderEliminar
    Respuestas
    1. Veo que tienes un campo de tipo DATS ( fecha ) , prueba sin ese campo, todos campos caracter.

      Eliminar
    2. Deje comentado ese campo de todas las definiciones y de la consulta de la tabla, de la defincion del ALV, etc. Lo comente en todas partes, y nada =/

      Eliminar
    3. Curioso... dudo entre la tabla data_tab o alguna incompativilidad con el excel, esta bapi envía los datos a excel sin tratarlos. Podrías mandarme el código para que lo vea mejor? crounly@gmail.com

      Eliminar
  6. Hola, muchas gracias! te pasaste por la ayuda y disposición. Te envíe una invitación a un documento en Google docs al correo que me dejaste. y de todas formas te envíe un copia al mail. Muchas Gracias!

    ResponderEliminar
  7. Creo que ya lo he visto. Es la tabla que le pasas a la bapi con los datos que quieres descargar.
    Le estas pasando la misma tabla que muestras en el ALV.
    Tienes que volcar los datos que quieres descargar de TI_REG_HIST a TL_EXCEL
    TL_EXCEL es la tabla que descargaremos a excel y tiene todos sus campos de tipo caracter
    TL_NAME tiene un registro por cada columna de TL_EXCEL, con su nombre.

    Vuelca los datos de TI_REG_HIST -> TL_EXCEL y pasale esta ultima a la bapi.

    A ver si asi va.

    ResponderEliminar
  8. Estimados, comento por acá cual era mi error por si le pasa a otra persona. A veces nos toca trabajar desde escritorios remotos o en ambientes donde desconocemos toda su configuración. Por mi parte, estaba trabajando mediante escritorio remoto donde no estaba instalado Office, al parecer es necesario para que funcione la exportación. Pensé que serviría generar el archivo y después poder verlo en otro ambiente, pero no.

    Agradezco mucho la tremenda ayuda prestada por quienes llevan el blog, se pasaron. Un abrazo y muchas gracias.
    Saludos!!

    ResponderEliminar
  9. Hola, antes de nada gracias por la función.

    He conseguido sacar el excel, pero necesito sacar la fecha con el formato __.__.____. Aunque en mi tabla interna lo parametrice, cuando la función genera la hoja de excel me lo cambia a __/__/____.

    Es posible que la fecha salga con el separador "."?

    Gracias.

    ResponderEliminar
    Respuestas
    1. Tienes dos formas:
      1. Usar la bapi KCD_EXCEL_DATE_CONVERT para convertir la fecha en excel a formato interno SAP y usar un WRITE para darle formato
      2. Usar la intruccion TRANSLATE -> TRANSLATE wl_data_excel USING '/.' ( te cambiara los / por . )

      Eliminar
  10. Hola, espero esten bn.
    Tengo un reporte con una ALV, y el usuario quiere que haya un boton de envio masivo por correo, el desea que estos registros que salen en la ALV, se adjunten en un archivo de Excel y sean enviados por correo.
    Veo que en su foro, da la opción de guardarlo localmente, pero yo solo necesito que al presionar enviar, se adjunte esta tabla interna en un excel en el correo.
    Como podria hacer?

    Gracias.

    ResponderEliminar
  11. HOLA NECESITO AYUDA CON UN REPORTE QUE DEBE EXPORTAR UNOS DATOS EN EXCEL Y OTROS DATOS EN ARCHIVO PLANO GRACIAS SI PUEDES ACLARA MIS DUDAS

    ResponderEliminar
    Respuestas
    1. Para exportar datos a excel puedes seguir esta entrada.
      En cuanto a un archivo plano , utiliza la instrucción OPEN DATASET
      En el siguiente enlace tienes un ejemplo de como escribir un archivo
      Leer y Escribir en local y servidor

      Eliminar
  12. hola necesito ayuda con lo de tu ejemplo tengonduda pase tu codigo soy nuevo en sap espero me puedas ayudar

    ResponderEliminar
  13. disculpa en esta parte ya no entendi en que parte de tu codigo se cambia y por que ??
    La tabla interna TL_NAME son las cabeceras de las columnas. Si comentáis el parámetro de entrad de la bapi o le pasáis una tabla interna vacía, generara la hoja excel sin cabecera para las columnas.

    ResponderEliminar
    Respuestas
    1. Exacto, son las cabeceras de las columnas del excel, si no quieres cabeceras en el excel , no lo rellenas. En mi código reutilizo el nombre de las columnas en el ALV como cabeceras del excel.

      Eliminar
  14. Saludos!! Comentas que esto no funcionara si ejecutas el programa como un job en fondo o si quieres que el fichero se cree en el servidor o en una unidad de red, espero y me puedas ayudar tengo la siguiente situación creo un archivo plano de una tabla interna (OPEN DATASET lv_ruta
    FOR OUTPUT IN TEXT ), y realizo un loop dentro de la tabla interna para pasarlo al archivo plano, el problema es que muy tardado ya que son muchos registros, sabes si es posible pasar la tabla interna a un objeto como en el caso de ALV, y procesar en fondo y dejarlo en servidor, para que sea mas rápido el proceso, o alguna alternativa. Gracias de antemano.

    ResponderEliminar
    Respuestas
    1. Lo siento, no conozco otra manera que volcar una tabla a un fichero en el servidor que no sea con OPEN DATASET. ¿El loop lo haces con field-symbols o con workareas? Los loop con field-symbols son muchos más rápidos que los loop con workareas.

      Eliminar
  15. como puedo crear mas pestañas en el mismo excel.
    3 tablas internes en 3 hojas de mi libro excel.

    ResponderEliminar
    Respuestas
    1. Eso es algo más complicado, puedes usar OLE, aqui te dejo un enlace con un ejemplo
      https://answers.sap.com/questions/1420555/download-to-multiple-sheets-in-excel.html

      O puedes usar ABAPxlsx
      https://wiki.scn.sap.com/wiki/display/ABAP/abap2xlsx

      Eliminar