E-Mail Template, Daten per Code statt CDS

Manchmal reicht eine einfache CDS View nicht aus, um die Daten für eine E-Mail zusammen zu tragen. Bei komplexen Business Logiken kann der Aufwand, dies in Views abzubilden, groß sein. Möglicherweise existieren aber bereits Funktionsbausteine oder Klassen, welche die Daten bereitstellen.

Auf der folgenden Seite ist beschrieben, wie ein E-Mail Template mittels ABAP Code mit Daten versorgt werden kann.

Voraussetzungen

Zunächst muss ein E-Mail Template, basierend auf einer CDS View, erstellt werden. Auch wenn die Daten nicht mit der CDS View ermittelt werden, muss diese dennoch erstellt werden. Zudem muss die View alle Spalten enthalten, die in der E-Mail verwendet werden.

Lösung

Wie zuvor werden zunächst Instanzen der BCS und E-Mail API erstellt

    DATA(lo_bcs) = cl_bcs=>create_persistent( ). " Create instance of BCS class

    " create instance of eMail API, provide name of Mail template
    DATA(lo_email_api) = cl_smtg_email_api=>get_instance( iv_template_id = 'ZMAIL_TEMPLATE1'  ).

Um Daten an die Schnittstelle übergeben zu können, muss eine Referenz vom Typ DATA erstellt werden. Dies kann beispielsweise wie folgt geschehen.

       DATA: lt_comp TYPE cl_abap_structdescr=>component_table.
        DATA: ls_comp LIKE LINE OF lt_comp.
        DATA lr_data TYPE REF TO data.

        ls_comp-name = 'PRUEFLOS'.
        ls_comp-type ?= cl_abap_typedescr=>describe_by_name( 'QPLOS' ).
        APPEND ls_comp TO lt_comp.

        " Fill table with concrete data
        DATA(lo_struct) = cl_abap_structdescr=>get( p_components = lt_comp ).
        CREATE DATA lr_data TYPE HANDLE lo_struct.
        ASSIGN lr_data->* TO FIELD-SYMBOL(<ls_data>).
        ASSIGN COMPONENT 1 OF STRUCTURE <ls_data> TO FIELD-SYMBOL(<fs_value>).
        <fs_value> = '777888999'. " No of inspection lot

Das Rendern des E-Mail Dokuments erfolgt diesmal mit der Methode RENDER_BCS_W_DATA. Leider ist diese seitens SAP nicht dokumentiert und allgemein finden sich kaum Beispiele hierzu.

        " Render E-Mail for CL_BCS class. This replaces all placeholders with real data
        lo_email_api->render_bcs_w_data(
          EXPORTING
            io_bcs              = lo_bcs
            iv_language         = sy-langu
            ir_data             = lr_data
        ).

Die weitere Verarbeitung, etwa das Hinzufügen der Absender und Empfänger, sowie das senden selbst, entspricht dem bereits beschriebenen vorgehen.

Vollständiges Programm

*&---------------------------------------------------------------------*
*& Report z_mail_template1
*&---------------------------------------------------------------------*
*& Send E-Mail based on Template by using data from code instead from CDS View
*&
*& E-Mail Template must be defined with relation to an existing CDS view.
*& CDS View needs to contain all relevant columns but we will not use it to read
*& data.
*& Instead, relevant data gets collected by ABAP. Therefore a reference to data table
*& gets instantiated and we use method render_bcs_w_data of
*& class cl_smtg_email_api
*&
*& This demo can be helpful, if you can't simply read data by CDS view because of
*& business complexity.
*&---------------------------------------------------------------------*
REPORT z_mail_template1.



DATA: gv_subrc_send_email TYPE sy-subrc.

DATA: ls_recip          TYPE bcss_re3,
      lr_recipient      TYPE REF TO if_recipient_bcs,
      lo_email_document TYPE REF TO cl_document_bcs.


TRY.
    DATA(lo_bcs) = cl_bcs=>create_persistent( ). " Create instance of BCS class

    " create instance of eMail API, provide name of Mail template
    DATA(lo_email_api) = cl_smtg_email_api=>get_instance( iv_template_id = 'ZMAIL_TEMPLATE1'  ).

    TRY.
        " Here, we define the data table, which contains all dynamic data of eMail. There table gets used by API to
        " replace placeholder with current values.
        DATA: lt_comp TYPE cl_abap_structdescr=>component_table.
        DATA: ls_comp LIKE LINE OF lt_comp.
        DATA lr_data TYPE REF TO data.
        ls_comp-name = 'PRUEFLOS'.
        ls_comp-type ?= cl_abap_typedescr=>describe_by_name( 'QPLOS' ).
        APPEND ls_comp TO lt_comp.
        ls_comp-name = 'MATERIAL'.
        ls_comp-type ?= cl_abap_typedescr=>describe_by_name( 'MATNR' ).
        APPEND ls_comp TO lt_comp.
        ls_comp-name = 'AUFNR'.
        ls_comp-type ?= cl_abap_typedescr=>describe_by_name( 'AUFNR' ).
        APPEND ls_comp TO lt_comp.
        ls_comp-name = 'MAKTX'.
        ls_comp-type ?= cl_abap_typedescr=>describe_by_name( 'KZTXT' ).
        APPEND ls_comp TO lt_comp.

        " Fill table with concrete data
        DATA(lo_struct) = cl_abap_structdescr=>get( p_components = lt_comp ).
        CREATE DATA lr_data TYPE HANDLE lo_struct.
        ASSIGN lr_data->* TO FIELD-SYMBOL(<ls_data>).
        ASSIGN COMPONENT 1 OF STRUCTURE <ls_data> TO FIELD-SYMBOL(<fs_value>).
        <fs_value> = '777888999'. " No of inspection lot
        ASSIGN COMPONENT 2 OF STRUCTURE <ls_data> TO <fs_value>.
        <fs_value> = '123456'. " material no
        ASSIGN COMPONENT 3 OF STRUCTURE <ls_data> TO <fs_value>.
        <fs_value> = '111234'. " pp order no
        ASSIGN COMPONENT 4 OF STRUCTURE <ls_data> TO <fs_value>.
        <fs_value> = 'Screw M5x40'. " material text

        " Render E-Mail for CL_BCS class. This replaces all placeholders with real data
        lo_email_api->render_bcs_w_data(
          EXPORTING
            io_bcs              = lo_bcs
            iv_language         = sy-langu
            ir_data             = lr_data
        ).


      CATCH cx_smtg_email_common INTO DATA(ls_cx).
        DATA(lv_message) = ls_cx->get_text( ).
        MESSAGE s899(id) WITH 'Unable to send message:'(004) lv_message.
        FREE lo_bcs.
        gv_subrc_send_email = 1.
    ENDTRY.

    IF gv_subrc_send_email = 0.

      TRY .
          lo_email_document ?= lo_bcs->document( ).
        CATCH cx_root.
      ENDTRY.

      TRY.
          " Define recipient
          CLEAR: lr_recipient.
          lr_recipient =
             cl_cam_address_bcs=>create_internet_address(
                                 'XY@Z.com' ).
          IF lr_recipient IS BOUND.
            ls_recip-recipient = lr_recipient.
            lo_bcs->add_recipient( EXPORTING i_recipient = lr_recipient ).
          ENDIF.
        CATCH cx_address_bcs INTO DATA(lr_bcs_exception).

      ENDTRY.

      " Define sender
      DATA(lo_sender) = cl_sapuser_bcs=>create( sy-uname ).
      lo_bcs->set_sender( i_sender = lo_sender ).

      " Send Email
      lo_bcs->send( ).

      IF sy-subrc IS INITIAL.
        COMMIT WORK. " Commit to really send the message
        FREE lo_bcs.
      ENDIF.

    ELSEIF gv_subrc_send_email = 4.
      MESSAGE s167(qm).
    ENDIF.

  CATCH cx_send_req_bcs INTO DATA(lr_send_req_bcs_excp).
*     possible error types of CX_SEND_REQ_BCS
*    (without those inherited from cx_bcs):
*    ALLREADY_RELEASED   RECIPIENT_EXISTS  DOCUMENT_NOT_EXISTS
*    DOCUMENT_NOT_SENT   NOT_SUBMITED      FOREIGN_LOCK
*    TOO_MANY_RECEIVERS  NO_INSTANCE       CANCELLED
*    NOT_RELEASED        MIME_PROBLEMS     NO_RECIPIENTS
*    SENDER_PROBLEMS


    DATA(lv_diagnosis) = lr_send_req_bcs_excp->get_text( ).
    MESSAGE s899(id) WITH
    'Unable to send message:'(004) lv_diagnosis.
    FREE lo_bcs.

*   possible bcs errors
  CATCH cx_bcs INTO DATA(lr_bcs).
*     possible error types of cx_bcs:
*     INTERNAL_ERROR     OS_EXCEPTION     X_ERROR
*     PARAMETER_ERROR    INVALID_VALUE    CREATION_FAILED
*     NO_AUTHORIZATION   LOCKED           CAST_ERROR

    lv_diagnosis = lr_bcs->get_text( ).
    MESSAGE s899(id) WITH
    'Unable to send message:'(004) lv_diagnosis.
    FREE lo_bcs.

  CATCH cx_smtg_email_common INTO DATA(lr_common).
    lv_diagnosis = lr_common->get_text( ).
    MESSAGE s899(id) WITH 'Unable to send message:'(004) lv_diagnosis .

  CATCH cx_os_object_not_found INTO DATA(lr_object_not_found).
    lv_diagnosis = lr_object_not_found->get_text( ).

ENDTRY.