domingo, 27 de septiembre de 2015

Clase CL_SALV_TABLE: Modificar atributos de las columnas

En el anterior post mostramos como generar un ALV utilizando solo la clase CL_SALV_TABLE. A continuación vamos a explicar como se modifican los atributos de las columnas del ALV cuando lo generamos utilizando la clase CL_SALV_TABLE.

A diferencia de los ALV generados con la bapi REUSE_ALV_GRID_DISPLAY o con la clase CL_GUI_ALV_GRID  la clase  CL_SALV_TABLE no utiliza un catalogo de campos o fieldcat para determinar las características de las columnas del listado ALV.  Cada columna del ALV debe ser tratada como un objeto individual con métodos que nos permitirán modificar sus atributos y  características como su descripción, longitud, ocultar la columna, etc...

Necesitamos instanciar dos clases:
  • CL_SALV_COLUMNS_TABLE  : Gestionar las columnas que componen el ALV
  • CL_SALV_COLUMN    : Gestionar los atributos de una columna del ALV
La primera clase CL_SALV_COLUMNS_TABLE nos permitirá gestionar atributos y características de las columnas del ALV. Para mas información podéis acceder a los métodos y atributos de la clase CL_SALV_COLUMNS_TABLE desde la transacción SE24 -> Pasar a -> Documentación -> Clase.

Transacción SE24 -> Clase CL_SALV_COLUMNS_TABLE

Para modificar las características y atributos propios de cada columna del ALV  instanciaremos un objeto CL_SALV_COLUMN_TABLE utilizando el metodo GET_COLUMN de la clase CL_SALV_COLUMNS_TABLE. Este método recibe como parámetro de entrada el nombre de una columna del ALV y retorna un objeto de la clase CL_SALV_COLUMN_TABLE que nos permite con sus métodos cambiar las características y atributos de la columna.

Transacción SE24 -> Clase CL_SALV_COLUMN_TABLE

Desde la transacción SE24  -> Pasar a -> Documentación -> Clase  podéis ver todos los métodos de la clase CL_SALV_COLUMN y que atributos nos permiten modificar.  Los métodos que mas se suelen utiliza son:


  • SET_LONG_TEXT:  Descripción larga de la columna
  • SET_MEDIUM_TEXT:Descripción media de la columna
  • SET_SHORT_TEXT: Descripción corta de la columna
  • SET_VISIBLE:   Oculta o muestra la columna ( 'X' muestra , '  '  oculta la columna )
  • SET_OUTPUT_LENGTH:  Especifica el ancho de la columna 
  • SET_OPTIMIZED:  Optimiza el ancho de la columna automáticamente
  • SET_CELL_TYPE: formato de la celda ( ej: campo, checkbox, etc.. ) 

A continuación, ampliamos el código del anterior post para modificar las características de algunas columnas.


*&---------------------------------------------------------------------*
*& Report:  ZZCL_SALV_TABLE_FULL_SCREEN
*& Fecha :  29.07.2015
*& Autor :  David Rueda Barrón
*&---------------------------------------------------------------------*
*& Creacion de ALV a pantalla completa con la clase CL_SALV_TABLE
*& Modificacion de los atributos de la columnas con las clases:
*&   - CL_SALV_COLUMNS_TABLE
*&   - CL_SALV_COLUMN_TABLE
*&---------------------------------------------------------------------*

report zzcl_salv_table_full_screen.

types: begin of type_matnr,
         matnr type mara-matnr,
         maktx type makt-maktx,
         mtart type mara-mtart,
         matkl type mara-matkl,
         meins type mara-meins,
         box   type c,
       end of type_matnr.

data: ti_mara type standard table of type_matnr.

data gr_table     type ref to cl_salv_table.         "Instancia de la clase
data gr_columns   type ref to cl_salv_columns_table. "Para gestionar las columnas
data gr_column    type ref to cl_salv_column_table.  "Para gestionar atrb. de una columna
data cx_salv      type ref to cx_salv_msg.
data cx_not_found TYPE ref to cx_salv_not_found.

data gr_msg  type string.
*&---------------------------------------------------------------------*
*& START-OF-SELECTION
*&---------------------------------------------------------------------*
start-of-selection.

  select m~matnr t~maktx m~mtart m~matkl m~meins
    into corresponding fields of table ti_mara
    from mara as m
    inner join makt as t
       on m~matnr eq t~matnr
      and t~spras eq sy-langu.

  try.
      cl_salv_table=>factory(
        importing
          r_salv_table = gr_table
        changing
          t_table      = ti_mara ).
    catch cx_salv_msg into cx_salv.
*     Gestionamos las excepciones que puedan suceder
      gr_msg = cx_salv->get_text( ).
      message gr_msg type 'E'.
  endtry.

  try.
      gr_columns ?= gr_table->get_columns( ).
*     gr_columns->set_optimize( 'X' ).   "Optimizar automa. abcho de TODAS las columnas

*     Cambiamos la descripción de la columna MATNR - MATERIAL
      gr_column  ?= gr_columns->get_column( 'MATNR' ).
      gr_column->set_short_text( 'Cod.Mat.' ).
      gr_column->set_medium_text( 'Cod. Material' ).
      gr_column->set_long_text( 'Código Material SAP' ).

*     Ocultamos la columna MTART - Tipo de material
      gr_column  ?= gr_columns->get_column( 'MTART' ).
      gr_column->set_visible( value  = if_salv_c_bool_sap=>false ).

*     Cambiamos la longitud de la columnas:
*     MAKTX descripción del material
*     MATKL Grupo de articulos
      gr_column  ?= gr_columns->get_column( 'MAKTX' ).
      gr_column->set_output_length( 30 ).

      gr_column  ?= gr_columns->get_column( 'MATKL' ).
      gr_column->set_output_length( 20 ).

*     Cambiamos la descripcion y formato de la columna box
      gr_column  ?= gr_columns->get_column( 'BOX' ).
      gr_column->set_short_text( 'box' ).
      gr_column->set_medium_text( 'Checkbox' ).
      gr_column->set_long_text( 'Checkbox' ).
      gr_column->set_cell_type( if_salv_c_cell_type=>checkbox ).

    catch  cx_salv_msg into cx_salv.
      gr_msg = cx_salv->get_text( ).
      message gr_msg type 'E'.
    catch  cx_salv_not_found into cx_not_found.
       gr_msg = cx_not_found->get_text( ).
      message gr_msg type 'E'.
  endtry.

  gr_table->display( ).

CL_SALV_TABLE  con los atributos de las comunas modificados

Entradas anteriores:
Clase CL_SALV_TABLE: Introducción

jueves, 30 de julio de 2015

Clase CL_SALV_TABLE: Introducción

La clase CL_SALV_TABLE es una aproximación  basada  en el paradigma de la programación orienta objetos para el desarrollo de listados ALV en aplicaciones SAP. La clase CL_SALV_TABLE forma parte del modelo basado en objetos para ALV ( ALV Object Model ) que agrupa diferentes clases globales para la implementación de listados ALV y que esta  incluido a partir de la versión 6.40 de SAP.

CL_SALV_TABLE se utiliza para crear listados ALV de dos dimensiones y con un estilo de hoja de calculo de forma fácil y rápida en nuestras aplicaciones ABAP.

La única limitación de esta clase es que no existe ningún  método para crear ALV editables.

Para crear la instancia de la clase CL_SALV_TABLE invocamos a su método FACTORY.
Una vez creado, para mostrar por pantalla el ALV por primera vez invocamos el método DISPLAY
Para refrescar los datos del ALV en la pantalla, invocamos al método REFRESH

Podeis visualizar sus atributos y métodos desde la transacción SE24.

Clase global CL_SALV_TABLE

CL_SALV_TABLE a pantalla completa:
En el siguiente ejemplo, implementamos un ALV a pantalla completa utilizando la clase CL_SALV_TABLE.

*&---------------------------------------------------------------------*
*& Report:  ZZCL_SALV_TABLE_FULL_SCREEN
*& Fecha :  29.07.2015
*& Autor :  David Rueda Barrón
*&---------------------------------------------------------------------*
*& Creacion de ALV a pantalla completa con la clase CL_SALV_TABLE
*&---------------------------------------------------------------------*

REPORT ZZCL_SALV_TABLE_FULL_SCREEN.

TYPES: BEGIN OF type_matnr,
  matnr type mara-matnr,
  maktx type makt-maktx,
  MTART type mara-MTART,
  MATKL type mara-matkl,
  MEINS type mara-MEINS,
 END OF type_matnr.

DATA: ti_mara type STANDARD TABLE OF type_matnr.

DATA gr_table type ref to cl_salv_table.  "instancia de la clase
DATA cx_salv  TYPE REF TO cx_salv_msg.

DATA gr_msg  TYPE string.
*&---------------------------------------------------------------------*
*& START-OF-SELECTION
*&---------------------------------------------------------------------*
START-OF-SELECTION.

SELECT m~matnr t~maktx m~mtart m~matkl m~meins
  INTO CORRESPONDING FIELDS OF TABLE ti_mara
  FROM mara as m
  INNER JOIN makt as t
     ON m~matnr EQ t~matnr
    AND t~spras EQ sy-langu.

try.
      cl_salv_table=>factory(
        importing
          r_salv_table = gr_table
        changing
          t_table      = ti_mara ).
catch cx_salv_msg INTO cx_salv.
* gestionamos las excepciones que puedan suceder
  gr_msg = cx_salv->get_text( ).
  MESSAGE gr_msg TYPE 'E'.
endtry.

gr_table->display( ).

CL_SALV_TABLE a pantalla completa

CL_SALV_TABLE dentro de una dynpro:
En el siguiente ejemplo, implementamos un ALV dentro de un objeto contenedor para que pueda ser visualizado en una  dynpro de una aplicación ABAP de tipo modul pool.

Como hemos comentado, en este caso, el ALV va ha estar contenido en una dynpro.
El programa principal se encargara  de rellenar la tabla interna y llamar a la dynpro.


*&---------------------------------------------------------------------*
*& Report:  ZZCL_SALV_TABLE_DYNPRO
*& Fecha :  29.07.2015
*& Autor :  David Rueda Barrón
*&---------------------------------------------------------------------*
*& Creación de ALV dentro de Dynpro con la clase CL_SALV_TABLE
DATA ti_mara type STANDARD TABLE OF type_matnr.
*&---------------------------------------------------------------------* REPORT ZZCL_SALV_TABLE_DYNPRO. TYPES: BEGIN OF type_matnr, matnr type mara-matnr, maktx type makt-maktx, MTART type mara-MTART, MATKL type mara-matkl, MEINS type mara-MEINS, END OF type_matnr.
DATA ok_code type sy-ucomm. 

DATA gr_table     TYPE REF TO cl_salv_table.  "instancia de la clase
DATA gr_container TYPE REF TO cl_gui_custom_container.

DATA cx_salv  TYPE REF TO cx_salv_msg.

DATA gr_msg  TYPE string.
*&---------------------------------------------------------------------*
*& START-OF-SELECTION
*&---------------------------------------------------------------------*
START-OF-SELECTION.

SELECT m~matnr t~maktx m~mtart m~matkl m~meins
  INTO CORRESPONDING FIELDS OF TABLE ti_mara
  FROM mara as m
  INNER JOIN makt as t
     ON m~matnr EQ t~matnr
    AND t~spras EQ sy-langu.

CALL SCREEN 4001.


Para crear la dynpro, tenéis que hacer doble click sobre el número de dynpro 4001.

Doble click sobre el número de dynpro para crearla

Como cualquier dynpro, lo primero es introducir una descripción, el resto de campos los dejamos como están, no hace falta modificarlos para esta prueba .

descripción de la dynpro

En la pestaña Lista Elementos, escribimos OK_CODE  en la única celda que hay editable.
Nota: OK_CODE es una variable global del programa principal de tipo SY-UCOMM.

OK_CODE para recoger el código de función de cada evento de la dynpro

Después hay que implementar los módulos STATUS y USER_COMMAND en la pestaña Lóg. proceso.

   1. Descomentamos la linea de MODULE STATUS_4001
   2. Doble click sobre nombre del modulo STATUS_4001 para crearlo.
   3. Guardamos el nuevo modulo en el include ZZCL_SALV_TABLE_DYNPRO_PBO
Crear el Modulo STATUS_4001
 
   4. Descomentamos la linea de código que empieza por SET_STATUS....
   5. Sustituimos las  XXXXXXX  por el nombre de nuestro status, por ejemplo: ZZSTATUS_4001
   6. Doble click sobre el nombre del status y creamos un nuevo Status diálogo.
   7. Desplegar la sección Teclas de función.
   8. En los siguientes iconos, escribimos los siguientes códigos de funciones:


Códigos de funciones para el status de la dynpro 4001

Lo siguiente es implementar el modulo USER_COMMAND_4001:

   1. Descomentamos la instrucción MODULE USER_COMMAND_4001.
   2. Doble click sobre nombre del modulo USER_COMMAND_4001 para crearlo.
   3. Guardamos el nuevo modulo en el include ZZCL_SALV_TABLE_DYNPRO_PAI

Crear el modulo USER_COMMAND_4001
   4. Dentro del modulo USER_COMMAND_4001 colocamos el siguiente código:

DATA wl_command TYPE sy-ucomm.

wl_command = ok_code.
clear ok_code.

CASE wl_command.
   WHEN 'BACK' OR 'EXIT' OR 'CANCEL'.
      SET SCREEN 0.
      LEAVE SCREEN.
ENDCASE.

Activamos todos los includes , la dynpro 4001 y el status ZZSTATUS_4001.
Con esto ya tenemos creada una dynpro y las funcione para poder cerrarla.

Ahora toca incrustar en ella nuestro ALV de la clase CL_SALV_TABLE.

   1. Volvemos ala dynpro 4001 y pulsamos el boton LAYOUT en la barra de herramientas.
   2. Dibujamos un objeto contenedor en la dypro, le damos el nombre de ZCONTENEDOR_ALV.
   3. Activamos y cerramos el editor de Dynpros

Añadimos un contenedor a la dynpro 4001

   4.Creamos un nuevo modulo en la sección PROCESS BEFORE OUTPUT de la dynpro 4001.
      Guardamos el modulo en el include ZZCL_SALV_TABLE_DYNPRO_PBO
     
nuevo modulo PBO - SET_ALV
   5. Incluimos el siguiente código dentro del modulo:

if ( gr_container is not bound ).
*   Si es la primera vez, instancia las clases y muestra los datos de la tabla
    create object gr_container
      exporting
        container_name              = 'ZCONTENEDOR_ALV'
      exceptions
        cntl_error                  = 1
        cntl_system_error           = 2
        create_error                = 3
        lifetime_error              = 4
        lifetime_dynpro_dynpro_link = 5
        others                      = 6.

    if ( sy-subrc <> 0 ).
      message id sy-msgid type sy-msgty number sy-msgno
                 with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    endif.

    cl_salv_table=>factory(
             exporting
                 r_container    = gr_container
                 list_display   = ' '
               importing
                 r_salv_table   = gr_table
               changing
                 t_table        = ti_mara ).

    gr_table->display( ).
  else.
*   Si ya estan instanciadas las clases, pues refrescamso los datos de la pantalla
    gr_table->refresh( ).
  endif.


Activamos todos los includes, dynpros y status.
Ejecutamos para ver el resultado

ALV con CL_SALV_TABLE dentro de una dynpro


En resumen, la clase CL_SALV_TABLE reduce el código que necesitamos para crear listados ALV por pantalla pero no podemos crear ALV editables..

Entradas siguientes:
Clase CL_SALV_TABLE: Modificar atributos de las columnas

sábado, 4 de julio de 2015

SAP HANA Cloud Platform - Conseguir una maquina SAP HANA para pruebas

Una de los problemas para aprender SAP HANA es conseguir acceso a una maquina SAP HANA para poder practicar con ella. La plataforma SAP HANA Cloud Platform Developer Edition nos ofrece nuestra propia instancia en la nube de una maquina SAP HANA para poder practicar y aprender HANA solo necesitamos una cuenta en SAP HANA Cloud Platform y ECLIPSE.

Lo primero, hay que registrarse en SAP HANA Cloud Platform.

Portal SAP HANA Clound Platform

Una vez registrados, accedemos a nuestra cuenta.
Para crear una nueva instancia SAP HANA:

  1.-En el menú de la izquierda pulsamos sobre SAP HANA instances -> New trial instances
Crear una nueva instancia SAP HANA trial
   2.- Introducir  un nombre identificativo para la instancia, por ejemplo hana001 y pulsar SAVE.
        Solo se permiten caracteres en minúsculas para el nombre identificativo.
Nombre identificativo en minúsculas
  3.- Si todo va bien, se crea la instancia y podemos ver los datos de la misma.
        
Datos de la instancia hana001 creada

SAP HANA Web-based Development Workbench

Desde esta ventana ya podríamos acceder a un entorno de desarrollo basado en web accediendo al enlace SAP HANA Web-based Development Workbench pero en este post vamos a mostrar como hacerlo desde ECLIPSE  para poder practicar con una maquina SAP HANA.

Arrancamos ECLIPSE e instalamos los plugins de SAP HANA y   ya que estamos de paso también ABAP. Desde el  menú superior HELP -> Install new Software -> repositorio SAP para LUNA
Marcamos los siguientes plugins:
  • ABAP development Tools for SAP NetWeaver
  • SAP HANA Cloud Integration Tools
  • SAP HANA Cloud Plataform Tools
  • SAP HANA Tools
SAP HANA y ABAP plugins para ECLIPSE 

La dirección del repositorio SAP para ECLIPSE LUNA es https://tools.hana.ondemand.com/luna.
Si usáis ECLIPSE KEPLER, la dirección del repositorio es https://tools.hana.ondemand.com/kepler.

Una vez instalados, solo queda crear el acceso a la instancia SAP HANA de nuestra cuenta.
Lo primero cambiamos a la perspectiva de SAP HANA Administration console.
Menu de Eclipse -> Windows -> Open Perpective -> Other -> SAP HANA Administration console.

Perspectiva de SAP HANA Administration console

Para crear el acceso a la instancia SAP HANA en la nube que hemos creado:

  1.- En la pestaña System ->> pulsar el icono  ->> Add  Cloud System....

Add Cloud System..
  2.- Configurar la información de nuestra cuenta en   SAP HANA Cloud Platform.

  •        Landscape host -> hanatrial.ondemand.com
  •        Account name    -> La encontraremos en nuestra cuenta, suele ser pxxxxtrial
  •        User name        -> Lo  encontraremos en nuestra cuenta, suele ser pxxxx
  •        Password          -> La contraseña de nuestra cuenta en  SAP HANA Cloud Platform.
Account name y User Name
Información de nuestra cuenta en SAP HANA Cloud Platform

   3.- Por ultimo seleccionamos la instancia a la que queremos conectarnos



Y ya tenemos una maquina SAP HANA  para practicar!!!



Referencias:
Get your free developer license for SAP HANA Cloud Platform in 5 minutes

Entradas relacionadas:
Abap in eclipse (AiE) - Instalación

miércoles, 24 de junio de 2015

Enviar documentos anexos como adjuntos de un email


En uno de mis últimos proyecto, el cliente , anexaba archivos PDF a las entregas de pedidos de ventas en la transacción VL03N y nos solicito un desarrollo para poder enviar al destinatario de la entrega, un email donde se adjuntasen automáticamente los anexos de la entrega

El proceso constaría de los siguientes pasos:
  1. El usuario selecciona las entregas a través de una pantalla de selección standard.
  2. Para cada entrega seleccionada:
    1. El programa busca los documentos PDF anexados a la entrega.
    2. El programa recupera el email asignado al cliente.
    3. Crear y enviar un email al cliente con los documentos PDF anexados como adjunto
diagrama de actividades
    Antes de empezar con el desarrollo, conviene repasar como y donde se almacena los anexos.
    También asegurarnos que esta correctamente configurado el SAPconnect desde la transacción SCOT.


    El primer paso, una vez tenemos el número de entrega, es recuperar los enlaces a los documentos que tiene anexados. Estos enlaces están guardados en la tabla SRGBTBREL y podemos acceder a ellos con un SELECT como el siguiente:

    SELECT instid_b INTO TABLE tl_instid_b
       FROM srgbtbrel
       WHERE reltype  EQ 'ATTA'
         AND instid_a EQ pi_vbeln  "nº de la entrega
         AND typeid_a EQ 'LIKP'.
    

    Ahora que ya tenemos los enlaces, podemos utilizar la bapi SO_DOCUMENT_READ_API1 para recuperar de cada enlace el archivo PDF . Esta bapi nos retornara el archivo en una tabla  de tipo SOLIX con una longitud de 255 para almacenar datos raw. Si concatenásemos todos los registros de la tabla SOLIX obtendríamos una cadena binaria que representaría el archivo PDF.

    Estructura SOLIX

    La configuración del email, adjuntar los archivos PDF y su envió es la parte mas fácil de todo el desarrollo. Desde la versión 6.10 se incluyen en SAP varias funciones y clases para el procesamiento del correo vía SMTP desde nuestras aplicaciones:
    • CL_BCS
    • SO_DOCUMENT_SEND_API1
    • SO_NEW_DOCUMENT_ATT_SEND_API1
    • SO_NEW_DOCUMENT_SEND_API1
    Para este desarrollo utilizamos la clase estándar CL_BCS. Esta clase sirve como interfaz para Business Communication Services (BCS) que es un componente de SAP que permite a los desarrolladores una forma sencilla de integrar el envió de la información a través de mensajes, ya sean email, fax, etc... Puede utilizarse también para muchas mas tareas como control de estados pero en este caso nos centramos en el envio de emails con documentos adjuntos.

    BCS se apoya sobre SAPconnect para enviar los emails.

    BCS se apoya en SAPconnect para enviar los objetos al exterior

    La dirección de email del cliente se encuentra en la tabla ADR6 en el campo SMTP_ADDR.
    Se puede acceder a ella a través del código del cliente KUNNR  pasando por la tabla KNA1.

    KNA1 -ADR6


    Para las pruebas, he adjuntado dos PDF a una entrega en la transacción VL03N

    Entrega 80000035 con dos archivos PDF anexados

    Una vez ejecutado el desarrollo, en la transacción SOST podemos ver el email creado listo para salir.

    Email generado en la transacción SOST

    Contenido del email generado, están adjuntos los archivos PDF de la entrega

    A continuación os dejo el código completo

    *&---------------------------------------------------------------------*
    *& Report  ZZPRUEBA_ANEXOS_EMAIL
    *& Autor   David Rueda Barrón
    *&---------------------------------------------------------------------*
    *& Envio de documentos PDF anexos a las entregas por email
    *&---------------------------------------------------------------------*
    
    REPORT zzprueba_anexos_email.
    
    
    *&---------------------------------------------------------------------*
    *& Parámetros de Selección
    *&---------------------------------------------------------------------*
    SELECTION-SCREEN BEGIN OF BLOCK block01 WITH FRAME TITLE text-001.
    PARAMETERS: p_vbeln TYPE likp-vbeln.
    SELECTION-SCREEN END OF BLOCK block01.
    
    *&---------------------------------------------------------------------*
    *& START-OF-SELECTION
    *&---------------------------------------------------------------------*
    START-OF-SELECTION.
      PERFORM f_enviar_email_adjuntos USING p_vbeln.
    
    *&------------------------------------------------------------------
    *& F_ENVIAR_EMAIL_ADJUNTOS
    *&------------------------------------------------------------------
    *  Enviar los doc. anexados de la entrega como adjuntos de email
    *&------------------------------------------------------------------
    FORM f_enviar_email_adjuntos USING pi_vbeln TYPE likp-vbeln.
    
      TYPES: BEGIN OF type_instid_b,
               instid_b TYPE srgbtbrel-instid_b,
             END OF type_instid_b.
    
      DATA: tl_instid_b TYPE STANDARD TABLE OF type_instid_b.
      DATA: wl_doc_id   TYPE sofolenti1-doc_id.
      DATA: tmp_solix   TYPE STANDARD TABLE OF solix.
      DATA: wl_file     TYPE string.
      DATA: wl_data     TYPE sofolenti1.
    
      DATA: wl_filename(20) TYPE c,
            wl_extension(3) TYPE c.
    
      DATA: lo_send_request TYPE REF TO cl_bcs,
            lo_document     TYPE REF TO cl_document_bcs,
            lo_sender       TYPE REF TO if_sender_bcs,
            lo_recipient    TYPE REF TO if_recipient_bcs.
    
      DATA wl_kunnr TYPE likp-kunnr.
      DATA wl_SMTP_ADDR TYPE ADR6-SMTP_ADDR.
    
      DATA: wl_email_line(80) TYPE c,
            lt_message_body   TYPE bcsy_text,
            wl_message_body   TYPE bcsy_text,
            w_subject(50)     TYPE c,
            l_sub             TYPE so_obj_des.
    
      DATA wl_error_ref TYPE REF TO cx_root.
      DATA wl_string    TYPE string.
    
      FIELD-SYMBOLS: <fs_instid_b> TYPE type_instid_b.
    
      CLEAR wl_SMTP_ADDR.
      REFRESH tl_instid_b.
    
      SELECT instid_b INTO TABLE tl_instid_b
       FROM srgbtbrel
       WHERE reltype  EQ 'ATTA'
         AND instid_a EQ pi_vbeln  "nº de la entrega
         AND typeid_a EQ 'LIKP'.
    
      IF tl_instid_b[] IS INITIAL.
        MESSAGE text-e01 TYPE 'I' DISPLAY LIKE 'E'.  "La entrega no tiene documentos anexados
        EXIT.
      ENDIF.
    
    * Recuperamos la direccion email del cliente de la entrega, sera el destinatario
      SELECT SINGLE kunnr into wl_kunnr
        FROM LIKP
        WHERE vbeln EQ pi_vbeln.
    
      SELECT SINGLE s~SMTP_ADDR INTO WL_SMTP_ADDR
        FROM kna1 as K
        INNER JOIN adr6 as s
           ON k~ADRNR EQ s~ADDRNUMBER
        WHERE kunnr EQ wl_kunnr.
    
      IF ( sy-subrc <> 0 ) OR ( WL_SMTP_ADDR IS INITIAL ).
    *   El cliente no tiene direción de email
        MESSAGE text-e02 TYPE 'I' DISPLAY LIKE 'E'.
        EXIT.
      ENDIF.
    
    * Inicializamos el objeto lo_send_request, utilizaremos sus metodos para enviar el email
    * Inicializamos el objeto lo_document, sera el objeto que enviaremos  por BCS<
      wl_message_body = 'Le adjuntamos los documentos PDF correspondientes  a su entrega'.
      APPEND wl_message_body TO lt_message_body.
      wl_message_body = 'Atentamente'.
      APPEND wl_message_body TO lt_message_body.
      wl_message_body = 'El supervisor'.
      APPEND wl_message_body TO lt_message_body.
    
      lo_send_request = cl_bcs=>create_persistent( ).
    
      lo_document = cl_document_bcs=>create_document(
                                    i_type = 'RAW'
                                    i_text = lt_message_body   "Mensaje del email
                                    i_subject = text-s01 && pi_vbeln ).        "Asunto del email
    
      CALL METHOD lo_send_request->set_document( lo_document ).
    
    * Añadimos el remitente del email, en este caso el usuario de sap que lo crea
      lo_sender = cl_sapuser_bcs=>create( sy-uname ).
      CALL METHOD lo_send_request->set_sender( i_sender = lo_sender ).
    
    * Añadimos la dirección email del destinatario, sera la direccion email del cliente.
    * Las direcciones de correo electronico se encuentranen al tabal ADR6
      lo_recipient = cl_cam_address_bcs=>create_internet_address( '' ).
      CALL METHOD lo_send_request->add_recipient
        EXPORTING
          i_recipient = lo_recipient
          i_express   = 'X'.
    
    * Añadimos los documentos adjuntos que hemos recuperado
    * Utilizamos la BAPI SO_DOCUMENT_READ_API1 para recuperarlos
      LOOP AT tl_instid_b ASSIGNING <fs_instid_b>.
    
        CLEAR:  wl_doc_id, wl_file, wl_extension.
        REFRESH: tmp_solix.
    
        wl_doc_id = <fs_instid_b>-instid_b.
    
        CALL FUNCTION 'SO_DOCUMENT_READ_API1'
          EXPORTING
            document_id                = wl_doc_id
    *       FILTER                     = 'X '
          IMPORTING
            document_data              = wl_data
          TABLES
    *       OBJECT_HEADER              =
    *       OBJECT_CONTENT             =
    *       OBJECT_PARA                =
    *       OBJECT_PARB                =
    *       ATTACHMENT_LIST            =
    *       RECEIVER_LIST              =
            contents_hex               = tmp_solix
          EXCEPTIONS
            document_id_not_exist      = 1
            operation_no_authorization = 2
            x_error                    = 3
            OTHERS                     = 4.
    
        CHECK tmp_solix[] IS NOT INITIAL.
    
        TRY.
    
            w_subject = wl_data-obj_descr.
            CALL METHOD lo_document->add_attachment
              EXPORTING
                i_attachment_type    = 'PDF'
                i_attachment_subject = w_subject
                i_att_content_hex    = tmp_solix[].
    
          CATCH cx_root INTO wl_error_ref.
    *      Error al adjuntar el archivo.
            wl_string = wl_error_ref->get_text( ).
            MESSAGE wl_string TYPE 'I' DISPLAY LIKE 'E'.
        ENDTRY.
    
      ENDLOOP.
    
    *  Enviamos el email, ira directamente a la SOST en cuanto hagamos el commit
      CALL METHOD lo_send_request->set_document( lo_document ).
      lo_send_request->send( i_with_error_screen = 'X' ).
    
      IF sy-subrc <> 0 .
        CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
      ELSE.
        CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
          EXPORTING
            wait = 'X'.
      ENDIF.
    
    
    ENDFORM.                    " F_ENVIAR_EMAIL_ADJUNTOS
    
    

    Referencias:
    ABAP Development - Sending Mails
    Send email from ABAP with class CL_BCS

    martes, 14 de abril de 2015

    Un ALV popup rápido y sencillo

    Esta es una forma rápida y sencilla de  lanzar un ALV en un popup o ventana emergente utilizando la clase CL_SALV_TABLE.  Esta clase es muy útil pues permite generar listados ALV con solo llamar a dos métodos de al misma, pero lo mejor es que también tiene un método para poder lanzar el ALV como si fuese un popup o ventana emergente a través de un método llamado SET_SCREEN_POPUP.


    Como hemos dicho, es muy sencillo manejar esta clase, solo necesitamos:
    • Una tabla interna con la información del listado
    • Una variable que instancie a la clase  cl_salv_table.

    Lo mejor, es ver el código, ya veréis como con 3 métodos creamos todo :

    REPORT zzprueba_alv_popup.
    
    TYPES: BEGIN OF type_matnr,
             matnr TYPE mara-matnr,
             meins TYPE mara-meins,
             maktx TYPE makt-maktx,
           END OF type_matnr.
    
    * Tabla interna con los datos del listado ALV
    DATA tl_matnr TYPE STANDARD TABLE OF type_matnr.
    
    * Objeto de la clase CL_SALV_TABLE
    DATA obj_alv_table TYPE REF TO cl_salv_table.
    
    SELECT m~matnr m~meins t~maktx
      INTO CORRESPONDING FIELDS OF TABLE tl_matnr
      FROM mara AS m
      INNER JOIN makt AS t
              ON m~matnr EQ t~matnr
      WHERE t~spras EQ sy-langu.
    
    * Instanciamos la clase con la tabla que contiene los datos
    cl_salv_table=>factory( IMPORTING r_salv_table = obj_alv_table
                            CHANGING t_table       = tl_matnr ).
    
    * Caracteristicas del POPUP
    obj_alv_table->set_screen_popup(
      start_column = 1
      end_column   = 160
      start_line   = 1
      end_line     = 20 ).
    
    * Lanzamos el ALV
    obj_alv_table->display( ).
    


    Resultado,  un ALV en una ventana emergente o popup .

    ALV dentro de un popup

    Si comentáis el método SET_SCREEN_POPUP el ALV sera a pantalla completa.

    El mismo ALV a pantalla compelta
    Muy sencillo, ¿no?

    jueves, 22 de enero de 2015

    Manejar campos a medida en pedidos de venta

    Es muy común añadir campos a medida o "campos Z" a los pedidos de venta de SAP para poder adaptar SAP a los procesos de negocio del cliente. Si se quiere añadir campos a medida, basta con crear un append a la tabla correspondiente:

    • VBAK si son campos a medida para la cabecera del pedido de ventas.
    • VBAP  si son campos a medida para las posiciones del pedido de venta.
    1. Desde la transacción SE11, dentro de la tabla correspondiente pulsamos el botón APPEND
    2. Creamos un nuevo append y añadimos nuestros campos a medida.
    3. Guardad y activar.

    Crear un nuevo append a la tabla
    Nuevos campos a medida para la tabla VBAP
    ATENCIÓN: Si añadís campos a través de append a las tablas estándar, SAP recomienda que el nombre del campo empiece por dos ZZ o dos YY, si no, os saltara una advertencia al activar el append a la tabla como me acaba de pasar a mi al utilizar una sola Z.

    Aviso: el nombre del campo debería empezar por ZZ o YY

    El problema surge cuando queremos gestionar estos campos con alguna bapi de ventas como:

    • SD_SALESDOCUMENT_CREATE
    • BAPI_SALESORDER_CHANGE
    • BAPI_SALESORDER_CREATEFROMDAT2
    • .... y más que hay....

    ¿Donde están nuestros campos a medida? ¿ como puedo usarlos en estas bapis?
    Para estas bapis, los campos a medida se gestionan a través del parámetro EXTENSIONIN.

    Primero, añadir los nuevos campos a las siguientes tablas a través de un APPEND:

    Si son campos a medida en las posiciones del pedido de venta:
    • BAPE_VBAP
    • BAPE_VBAPX
    • VBAPKOZ
    • VBAPKOZX
    Si son campos a medida en la cabecera del pedido de venta:
    • BAPE_VBAK
    • BAPE_VBAKX
    • VBAKKOZ
    • VBAKKOZX
    NOTA  MUY IMPORTANTE
    En las estructuras BAPE_VBAK/BAPE_VBAP todos los campos deben ser de tipo CHAR sino cuando más adelante rellenéis la estructura de la bapi, los campos numéricos saldrán con caracteres extraños como &# y no coincidirán las longitudes.

    Por ejemplo:

    •  Un campo DEC 15 con 3 decimales , lo definiremos como CHAR de 15
    •  Un campo INT de longitud, lo definiremos como CHAR de 8.



    En las estructuras  BAPE_VBAPX / BAPE_VBAKX los campos deben ser CHAR1 (char de long. 1)
    En las estructuras  VBAPKOZX o VBAKKOZX los campos deben ser CHAR1 (char de long. 1)

    Centrándonos en el ejemplo, ampliamos las tablas para posiciones de pedido:
    BAPE_VBAP - todos los campos son de tipo CHAR
    BAPE_VBAPX - todos los campos son de tipo CHAR
    VBAPKOZ
    VBAPKOZX

    Con esto hemos terminado la parte de las estructuras que necesitamos.
    Ahora vamos con el código.

    EXTENSIONIN es de tipo BAPIPAREX, su estructura es la siguiente:

    Estructura BAPIPAREX
    En el primer campo indicamos que estructura vamos a pasarle BAPE_VBAP o BAPE_VBAPX
    En los siguientes campos guardamos los valores de los campos concatenados como un solo registro.
    Lo mejor es utilizar el método fill_container_c de la  cl_abap_container_utilities.

    Aquí os dejo un ejemplo para rellenar la estructura EXTENSIONIN:

    DATA: wl_vbape  TYPE  bape_vbap,
          wl_vbapex TYPE  bape_vbapx.
    
    DATA: tl_extin type STANDARD TABLE OF bapiparex.
    
    FIELD-SYMBOLS: <fs_extin> TYPE bapiparex.
    
    wl_vbape-VBELN = wg_vbeln.   "nº de documento de ventas
    wl_vbape-POSNR = wg_posnr.   "nº de posición
    
    wl_vbapex-VBELN = wg_vbeln.  "nº de documento de ventas
    wl_vbapex-POSNR = wg_posnr.  "nº de posición
    
    wl_vbape-zzcampo1 = 'prueba'.
    wl_vbape-zzcampo2 = 'de campos'.
    wl_vbape-zzcampo3 = 'a medida'.
    
    wl_vbapex-zzcampo1 = 'X'.   "o ABAP_TRUE si usais TYPE-POOLS: ABAP.
    wl_vbapex-zzcampo2 = 'X'.
    wl_vbapex-zzcampo3 = 'X'.
    
    APPEND INITIAL LINE TO it_extin ASSIGNING  <fs_extin>.
    <fs_extin>-structure = 'BAPE_VBAP'.
    
    
    *si parecen caracteres extraños, revisar que todos los campos de la estrc son CHAR
    cl_abap_container_utilities=>fill_container_c( EXPORTING im_value  = wl_vbape
                                                   IMPORTING ex_container = <fs_extin>+30 ).
    
    
    APPEND INITIAL LINE TO it_extin ASSIGNING  <fs_extin>.
    <fs_extin>-structure = 'BAPE_VBAPX'.
    
    cl_abap_container_utilities=>fill_container_c( EXPORTING im_value  = wl_vbapex
                                                   IMPORTING ex_container = <fs_extin>+30 ).
    
    * ya podemos usarlo en la bapi:
        CALL FUNCTION 'SD_SALESDOCUMENT_CREATE'
          EXPORTING
            sales_header_in      = wa_so_hearder
            sales_header_inx     = wa_so_hearderx
            testrun              = test
          IMPORTING
            salesdocument_ex     = wa_saorder
          TABLES
            return               = lt_return
            sales_items_in       = it_items
            sales_partners       = it_fi
            extensionin          = it_extin. "<Campos a medida del pedido


    Notas relacionadas:
    Note 143580 - Information on SD BAPIs and customer enhancement