Friday, August 29, 2014

HTML Email with tables generated by SmartForm

Intro

There are various possibilities how to work with emails in SAP. Previously I used some function module. When I obtained a task to prepare an email message with some log info content e.g. this job passed the other failed. I decided to create a HTML email message with nice colors, green for passed, red for failed tasks.

At first I decided to use Nguyen Van Thao's pattern. It uses CL_BCS class and SF. I used it by a bit by my own way. So let me introduce a clone with tables inside the smart form + some my experience notes.

Content generator Class

There is class responsible for the filling out needed tables which should be displayed inside the email. I just took a real example from my work.

*----------------------------------------------------------------------*
*       CLASS lcl_manager_email DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_manager_email DEFINITION.
  PUBLIC SECTION.
    METHODSconstructor
              IMPORTING
                io_data_sel  TYPE REF TO lcl_data_sel
                io_data_user TYPE REF TO lcl_data_user,
             send_email_log
              IMPORTING iv_subject TYPE so_obj_des
              RAISING lcx_msg_exception.

  PRIVATE SECTION.
    DATAo_data_sel  TYPE REF TO lcl_data_sel,
          o_data_user TYPE REF TO lcl_data_user.

    METHODSget_mail_body
                RETURNING value(rt_soliTYPE soli_tab
                RAISING lcx_msg_exception" get mail body from Smart form

ENDCLASS.                    "lcl_manager_email DEFINITION

*----------------------------------------------------------------------*
*       CLASS lcl_manager_email IMPLEMENTATION
*----------------------------------------------------------------------*
* Any email stuff
*----------------------------------------------------------------------*
CLASS lcl_manager_email IMPLEMENTATION.

  METHOD constructor.
    o_data_sel  io_data_sel.
    o_data_user io_data_user.
  ENDMETHOD.                    "constructor

  METHOD send_email_log.
    " Send message only in case of real action
    IF o_data_sel->v_locka IS NOT INITIAL )    " do admin lock
        OR o_data_sel->v_lockl IS NOT INITIAL )  " do local lock
        OR o_data_sel->v_vali  IS NOT INITIAL )" set validation

      DATAlo_send_request TYPE REF TO cl_bcs,
            lo_document     TYPE REF TO cl_document_bcs,
            lo_recipient    TYPE REF TO if_recipient_bcs,
            lo_sender       TYPE REF TO cl_sapuser_bcs,
            lt_soli         TYPE soli_tab,
            lv_sent_flag    TYPE abap_bool,
            lx_oref         TYPE REF TO lcx_msg_exception,
            lx_bcf          TYPE REF TO cx_bcs,
            lv_msg          TYPE string.

      lt_soli me->get_mail_body).

      " Instantinate CL_BCS and specify options
      TRY .
          " Create persistent
          lo_send_request cl_bcs=>create_persistent).

          " Email FROM
          lo_sender cl_sapuser_bcs=>createsy-uname ).
          " Add sender to send request
          CALL METHOD lo_send_request->set_sender
            EXPORTING
              i_sender lo_sender.

          " Email TO
          lo_recipient cl_cam_address_bcs=>create_internet_addresso_data_sel->v_email ).
          " Add recipient to send request
          CALL METHOD lo_send_request->add_recipient
            EXPORTING
              i_recipient lo_recipient
              i_express   'X'.

          " Email BODY from SmartForm
          lo_document cl_document_bcs=>create_document(
              i_type        'HTM'
              i_subject     iv_subject
              i_text        lt_soli ).
          " Add document to send request
          CALL METHOD lo_send_request->set_documentlo_document ).

          " Send email
          lo_send_request->set_send_immediatelyi_send_immediately abap_true ).

          lv_sent_flag lo_send_request->sendi_with_error_screen 'X' ).
          IF lv_sent_flag EQ abap_false.
            RAISE EXCEPTION TYPE lcx_msg_exception
              EXPORTING iv_text =  'Error Sending Email!'.
          ENDIF.

          "Commit to send email
          COMMIT WORK.

        CATCH cx_bcs INTO lx_bcf.
          RAISE EXCEPTION lx_bcf.
      ENDTRY.
    ENDIF.
  ENDMETHOD.                    "send_email_log

  METHOD get_mail_body.

*---Data declaration
    DATAlt_lines TYPE TABLE OF tline,
          ls_line  TYPE tline,
          ls_soli  TYPE soli.
    DATAlv_fname      TYPE rs38l_fnam,
          ls_job_output TYPE ssfcrescl"Structure to return value at the end of form printing
    DATAls_ctrl_form  TYPE ssfctrlop"Smart Form Control Structure
          ls_output_opt TYPE ssfcompop"Smart Form Transfer Options

*---Pass data to Smartforms to receive itab of HTML Email

    "Get Smart Form Function Module Name
    CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME'
      EXPORTING
        formname           cv_sf_htm_mail
      IMPORTING
        fm_name            lv_fname
      EXCEPTIONS
        no_form            1
        no_function_module 2
        OTHERS             3.
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE lcx_msg_exception
        EXPORTING is_syst sy.
    ENDIF.

    "Spool parameters
    ls_output_opt-tdimmed 'X'.      "Print Immediately (Print Parameters)
    ls_output_opt-tddelete 'X'.     "Delete After Printing (Print Parameters)
    ls_output_opt-tdlifetime 'X'.   "Spool Retention Period (Print Parameters)
    ls_output_opt-tddest 'LOCL'.    "Spool: Output device
    ls_output_opt-tdprinter 'SWIN'"Spool: Device type name
    ls_ctrl_form-no_dialog 'X'.     "SAP Smart Forms: General Indicator
    ls_ctrl_form-preview 'X'.       "Print preview
    ls_ctrl_form-getotf 'X'.        "Return of OTF table. No printing, display, or faxing
    ls_ctrl_form-langu 'EN'.        "Language key
    ls_ctrl_form-device 'PRINTER'.  "Output device

    "Call Smart Form Function Module
    CALL FUNCTION lv_fname
      EXPORTING
        control_parameters ls_ctrl_form
        output_options     ls_output_opt
      IMPORTING
        job_output_info    ls_job_output
      TABLES
        it_users1          o_data_user->t_users1
        it_users2          o_data_user->t_users2
        it_users3          o_data_user->t_users3
        it_users4          o_data_user->t_users4
        it_result_statuses o_data_user->t_result_statuses
        it_result_errors   o_data_user->t_result_errors

      EXCEPTIONS
        formatting_error   1
        internal_error     2
        send_error         3
        user_canceled      4
        OTHERS             5.
    IF ls_job_output-otfdata IS INITIAL.
      RAISE EXCEPTION TYPE lcx_msg_exception
        EXPORTING is_syst sy.
    ENDIF.

    "Convert OTF to TLINE
    CALL FUNCTION 'CONVERT_OTF'
      EXPORTING
        format                'ASCII'
        max_linewidth         132
      TABLES
        otf                   ls_job_output-otfdata
        lines                 lt_lines
      EXCEPTIONS
        err_max_linewidth     1
        err_format            2
        err_conv_not_possible 3
        err_bad_otf           4
        OTHERS                5.
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE lcx_msg_exception
        EXPORTING is_syst sy.
    ENDIF.

    "Remove empty lines
    DELETE lt_lines WHERE tdline EQ space.
    "Convert itab of HTML Email to itab of sending format in class CL_BCS
    LOOP AT lt_lines INTO ls_line.
      ls_soli ls_line-tdline.
      APPEND ls_soli TO rt_soli.
      CLEAR ls_soli.
    ENDLOOP.

  ENDMETHOD.                    "get_mail_body

ENDCLASS.                    "lcl_manager_email IMPLEMENTATION



Smart form

By above class we have some prepared data/tables which should be placed into email. Please acknowledge the data types transferred into SmartForm must be data dictionary based. Here I used also some our custom types. 



Tables

I had a bit bad experience with SmartForm's TABLE elements. The text inside the SOLI table was truncated and useless for HTML content. So I decided to use simple LOOP instead of table. Inside the loop you can achieve the same functionality. In connection with IF conditions inside the loop it gives you quite powerful tool for email generation.



In text element you can place your HTML code with inside CSS style. Inside text element you may work normally with variables as usual e.g. &GS_RESULT_STATUS-STATUS_LOCKED&.




Together it produces following tables within email. Now email dynamically shows the result of the last taken action according to the conditions within table loops inside the SmartForm.



As you see, it's some mix up of SmartForm's elements and HTML/CSS styles inside texts elements and that's all. So now is your turn...









Friday, August 1, 2014

Handy local exception class

During my exception exploration I came to such conclusion, within local local exception class is sometime handy to have both input parameters, at first message with all its attributes or simple string. Below class fulfill both cases. Below exception class have two optional input parameters, so it's up to programmer what to choose. See below usage examples.

*----------------------------------------------------------------------*
*       CLASS lcx_msg_exception DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcx_msg_exception DEFINITION
  INHERITING FROM cx_static_check.

  PUBLIC SECTION.
    DATAmsgv1 TYPE symsgv READ-ONLY,
          msgv2 TYPE symsgv READ-ONLY,
          msgv3 TYPE symsgv READ-ONLY,
          msgv4 TYPE symsgv READ-ONLY.
    INTERFACES if_t100_message.
    METHODS constructor
      IMPORTING
        iv_text     TYPE clike       OPTIONAL  " simple text input
        is_t100_key TYPE scx_t100key OPTIONAL" message input
ENDCLASS.                    "lcx_msg_exception DEFINITION

*----------------------------------------------------------------------*
*       CLASS lcx_msg_exception IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcx_msg_exception IMPLEMENTATION.
  METHOD constructor.
    super->constructor).

    " simple string input
    IF iv_text IS NOT INITIAL.
      cl_message_helper=>set_msg_vars_for_clikeiv_text ).
      if_t100_message~t100key-attr1 sy-msgv1.
      if_t100_message~t100key-attr2 sy-msgv2.
      if_t100_message~t100key-attr3 sy-msgv3.
      if_t100_message~t100key-attr4 sy-msgv4.
    ENDIF.

    " message input
    IF is_t100_key IS NOT INITIAL.
      MOVE-CORRESPONDING is_t100_key TO if_t100_message~t100key.
    ENDIF.

    " special cases
    IF if_t100_message~t100key-msgid IS INITIAL )
      AND  if_t100_message~t100key-msgno IS INITIAL )
      AND  if_t100_message~t100key-attr1 IS NOT INITIAL ).
      if_t100_message~t100key-msgid '00'.
      if_t100_message~t100key-msgno '001'.
    ENDIF.

    " set message attributes
    msgv1 if_t100_message~t100key-attr1.
    msgv2 if_t100_message~t100key-attr2.
    msgv3 if_t100_message~t100key-attr3.
    msgv4 if_t100_message~t100key-attr4.
    if_t100_message~t100key-attr1 'MSGV1'.
    if_t100_message~t100key-attr2 'MSGV2'.
    if_t100_message~t100key-attr3 'MSGV3'.
    if_t100_message~t100key-attr4 'MSGV4'.

  ENDMETHOD.                    "constructor
ENDCLASS.                    "lcx_msg_exception IMPLEMENTATION


START-OF-SELECTION.

  DATAlx_oref TYPE REF TO lcx_msg_exception,
        ls_msg  TYPE scx_t100key.

* example 1 - simple success message
  ls_msg-msgid 'VL'.
  ls_msg-msgno '017'.
  TRY.
      RAISE EXCEPTION TYPE lcx_msg_exception
        EXPORTING
          is_t100_key ls_msg.
    CATCH lcx_msg_exception INTO lx_oref.
      MESSAGE lx_oref TYPE 'S'.
  ENDTRY.

* example 2 - error message with attributes
  ls_msg-msgid 'VL'.
  ls_msg-msgno '046'.
  ls_msg-attr1 '41000064'" some VBELN
  ls_msg-attr2 'IT007'.    " some sy-uname user

  TRY.
      " ...some coding raising custom exception
      RAISE EXCEPTION TYPE lcx_msg_exception
        EXPORTING
          is_t100_key ls_msg.
    CATCH lcx_msg_exception INTO lx_oref.
      MESSAGE lx_oref TYPE 'E'.
  ENDTRY.

* example 3 - simple error text message
  DATAlv_text TYPE string.
  TRY.
      " ...some coding raising custom exception
      lv_text 'Some ugly error occcured in program 1! Some stupid error occcured in program 2! Some silly error occcured in program 3!'.
      RAISE EXCEPTION TYPE lcx_msg_exception
        EXPORTING
          iv_text lv_text.
    CATCH lcx_msg_exception INTO lx_oref.
      MESSAGE lx_oref TYPE 'E'.
  ENDTRY.