Thursday, October 28, 2010

Creating a class to use the functionality of the persistent class CL_BCS

Creating a class to use the functionality of the persistent class CL_BCS:
I was searching for a way to use the newer class based methods for sending e-mail instead of the old function module "CALL FUNCTION 'SO_NEW_DOCUMENT_ATT_SEND_API1'".
I searched Google, SDN, SAP etc, and came up with a blog by Thomas Jung that came real close to doing what I was looking for. He actually created a function module to encapsulate the persistent class and then a special user interface to call it with which was real good. You can see his blog here.
After looking over his blog and getting a grasp on how the e-mail class worked, I thought I would put together something similar but keep it in its own class that my fellow developers could leverage in some of their requirements where e-mailing would be required. I developed a global class named ZCL_EMAIL_CLASS which can then send the e-mail with or without an attachment. 


I started by going to SE24 (Class builder) and creating me a new class which I named ZCL_EMAIL_CLASS and then gave it the following attributes:



I started by declaring my first attribute called ca_x - level constant - visibility private type char1 with a value of abap_true. It looked like this:

















I then pressed the right arrow: 

 


which took me to the definitions page where
I did some old fashioned coding which turned out to be pretty cool.













I typed in the remaining constants, data types and tables here and it looked like this:
























Press 'save' and the class builder will setup your attributes for you:



























Now comes the fun part. Create three methods. One is the main method to call from your program and the other two are to setup the email message and optional attachment.

















Set up importing and returning parameters in the main method.


















*note added another importing parameter called copy_me importing, pass by value, optional type checkbox so that you won't always get a copy, just when you want one. put a check box parameter in the main program called p_copy to pass to the class.

These are the tables and variables that are to be passed to the class from your programs to be e-mailed.
Now let's put some code into the methods:

METHOD SEND_MAIL_BCS.
*Protect your program from shortdumps by wrapping it in try catch endtry.
  TRY.
      send_request = cl_bcs=>create_persistent( ).

      me->cl_message_prep( EXPORTING subject_email = subject_email
                                     i_email_body  = i_email_body
                           RECEIVING return        = return ).
      "set up the body of the e-mail
      me->cl_attachment_prep( EXPORTING subject_attachment = subject_attachment
                                        i_attachment       = i_attachment
                              RECEIVING return             = return ).
      "create the attachemnt

* load the document and the attachment into the perrsistent class
      CALL METHOD send_request->set_document( document_bcs ).

*set the sender. (using sy-uname)
      sender = cl_sapuser_bcs=>create( sy-uname ).
      CALL METHOD send_request->set_sender
        EXPORTING
          i_sender = sender.

      LOOP AT to INTO wa_to.
*    create recipients using from and to where "from" is you and "to" is from the selection screen.
        recipient = cl_cam_address_bcs=>create_internet_address( wa_to ).
*     create the list for the e-mail class:
        CALL METHOD send_request->add_recipient
          EXPORTING
            i_recipient = recipient
            i_express   = ca_x.
      ENDLOOP.
*    create recipients using from and to where "from" is you and "to" is from the selection screen.
      if copy_me = 'X'.
*    (This is used if you want a copy of the output.)
      recipient = cl_cam_address_bcs=>create_internet_address( from ).
      CALL METHOD send_request->add_recipient
        EXPORTING
          i_recipient = recipient
          i_express   = ca_x.
      endif.

* Set that you don't need a Return Status E-mail
      CALL METHOD send_request->set_status_attributes
        EXPORTING
          i_requested_status = 'E'
          i_status_mail      = 'E'.

*     sets the send immediate flag bypassing the background job wait time.
      CALL METHOD send_request->set_send_immediately
        EXPORTING
          i_send_immediately = ca_x.

*     This actually sends the e-mail using all of the wonderfulness you just created above.
      CALL METHOD send_request->send(
        EXPORTING
          i_with_error_screen = ca_x
        RECEIVING
          result              = sent_to_all ).
      COMMIT WORK.

    CATCH cx_bcs INTO bcs_exception.
      DATA: l_message TYPE string.
      l_message = bcs_exception->get_text( ).
      return-message = l_message.
  ENDTRY.

ENDMETHOD.


And for the other two as well:

METHOD cl_message_prep.
  att_type = ca_raw.
  DESCRIBE TABLE i_email_body LINES n10.
  n10 = ( n10 - 1 ) * 255 + STRLEN( wa_text ).
  i_length = n10.
  TRY.
      document_bcs = cl_document_bcs=>create_document(
                i_type          = att_type
                i_text          = i_email_body[]
                i_length        = i_length
                i_subject       = subject_email ).
    CATCH cx_bcs INTO bcs_exception.
      DATA: l_message TYPE string.
      l_message = bcs_exception->get_text( ).
      return-message = l_message.
  ENDTRY.
ENDMETHOD.

METHOD cl_attachment_prep.
  DATA : lw_error LIKE LINE OF i_attachment.
  att_type = ca_raw."raw format used for sending human readable
  IF i_attachment IS INITIAL.
    "skip
  ELSE.
* length of Att_Text
    DESCRIBE TABLE i_attachment LINES n10.
    READ TABLE i_attachment INTO lw_error INDEX n10.
    n10 = ( n10 - 1 ) * 255 + STRLEN( lw_error ).
    i_length = n10.
    TRY.
        CALL METHOD document_bcs->add_attachment
          EXPORTING
            i_attachment_type    = att_type
            i_att_content_text   = i_attachment[]
            i_attachment_size    = i_length
            i_attachment_subject = subject_attachment.
*Error Message
      CATCH cx_bcs INTO bcs_exception.
        DATA: l_message TYPE string.
        l_message = bcs_exception->get_text( ).
        return-message = l_message.
    ENDTRY.
  ENDIF.
ENDMETHOD.

Now in the attachment method I put the "if initial" for the table statement so I don't create a blank attachment if I only wanted to send my data in the body of the e-mail.


The hard part is done. Save and activate your class.


It's time to play with a program to use it. I created a sample program called ZTST_EMAIL_CLASS_USAGE_OO to use as an example of how this will all work together.


*&---------------------------------------------------------------------*
*& Report  ZTST_EMAIL_CLASS_USAGE
*&---------------------------------------------------------------------*

REPORT  ztst_email_class_usage_oo.
DATA: lv_index TYPE sy-tabix VALUE 0,
      lv_text  TYPE char8,
      lv_num   TYPE char1.
DATA: to                 TYPE bcsy_smtpa,
      wa_to              LIKE LINE OF to,
      from               TYPE adr6-smtp_addr,
      subject_email      TYPE              so_obj_des,
      subject_attachment TYPE      so_obj_des,
      i_attachment       TYPE zsolisti_tab,"Table for the attachement.
      i_email_body       TYPE bcsy_text,"Table for the e-mail body.
      i_attachment_s     TYPE solisti1,"line of attachment table.
      i_email_body_s     LIKE LINE OF i_email_body,"line of e-mail body table.
      send_mail          TYPE REF TO zcl_email_class,
      return             TYPE bapiret2.
CONSTANTS:
      ca_email           TYPE string VALUE '@yourDNS.COM'.
SELECTION-SCREEN BEGIN OF BLOCK bl2 WITH FRAME TITLE text-tt2.

PARAMETER : p_to         TYPE adr6-smtp_addr DEFAULT 'my_coworker@yourDNS.com',
            p_send       AS CHECKBOX,
            p_body       AS CHECKBOX DEFAULT 'X',
            p_file       AS CHECKBOX,
            p_copy   AS CHECKBOX.
SELECTION-SCREEN END   OF BLOCK bl2.

START-OF-SELECTION.
  CONCATENATE sy-uname ca_email INTO from.
  CONDENSE from.
* suppose you want to just send the data in the body of the e-mail.
  IF p_body = 'X' AND p_file = ' '.
    DO 10 TIMES.
      lv_num = lv_index.
      CONCATENATE 'text' '-' 'wa' lv_num INTO lv_text.
      CONDENSE lv_text.
      i_email_body_s  = lv_text.
      APPEND i_email_body_s  TO i_email_body.
      lv_index = lv_index + 1.
    ENDDO.
* THIS IS PLACED INTO THE BODY OF THE E-MAIL
  ELSEIF p_file = 'X' AND p_body = ' '.
*Suppose you want to send the contents of the internal table i_attachment in the attchment to the group/ID
    DO 10 TIMES.
      lv_num = lv_index.
      CONCATENATE 'text' '-' 'wa' lv_num INTO lv_text.
      CONDENSE lv_text.
      i_attachment_s-line = lv_text.
      APPEND i_attachment_s TO i_attachment.
      lv_index = lv_index + 1.
    ENDDO.
    "this becomes the attachment
  ELSEIF p_body = 'X' AND p_file = 'X'.
    DO 10 TIMES.
      lv_num = lv_index.
      CONCATENATE 'text' '-' 'wa' lv_num INTO lv_text.
      CONDENSE lv_text.
      i_attachment_s-line = lv_text.
      APPEND i_attachment_s TO i_attachment.
      i_email_body_s  = lv_text.
      APPEND i_email_body_s  TO i_email_body.
      lv_index = lv_index + 1.
    ENDDO.
  ELSE.
    MESSAGE i208(00) WITH 'No attachemnt or email body will be processed'(001).
  ENDIF.
  subject_email = 'This is the subject line of the e-mail'.
  IF p_file = 'X'.
    subject_attachment = 'attachment_name'.
  ENDIF.
  MOVE p_to TO wa_to.
  APPEND wa_to TO to.
  IF p_send = 'X'.
    CREATE OBJECT send_mail.
    send_mail->send_mail_bcs(
      EXPORTING
        i_email_body               = i_email_body[]
        subject_email              = subject_email
        subject_attachment     = subject_attachment
        i_attachment               = i_attachment[]
        from                           = from
        to                               = to
       copy_me                    = p_copy       
IMPORTING
        return = return ).
    DATA: l_message TYPE string.
    IF NOT return IS INITIAL.
      return-message = l_message.
      MESSAGE i208(00) WITH l_message.
    ENDIF.
  ENDIF.


Execute the program and you will see the options that are available for e-mailing with. If the 'ready to send' check box is not checked the program will not send the e-mail and if the 'send data as an attachment' is not checked no attachment will be sent. If all three are checked you will get both data in the e-mail body and in the attachment.  You are completely flexible in this as the attachment table, the attachment title, the email subject and the message body tables are all separate and you can load them as you wish in your program.











With the send data in e-mail body, you get an email that looks like this:

























The table passed to the class (I_EMAIL_BODY) had ten lines in it as text_wa0 - text-wa9.
A good way to pass information to the class is to concatenate variable_1 through variable_~ into your work area (type line of table) respecting space (of needed) separated by c_tab (data element abap_char1). This will put your data in columns as a string to be passed to the table and laid out accordingly.


The e-mail that was returned with the attachment checkbox, and the "data in the email box checked" looks like this:



















So you would get a message in the e-mail and an attachment.
(They are the same because I was coding a simple test using the same data.)


And with just the attachment only checked you would get the same as above but without anything in the message body.

If you have any positive suggestion, or even constructive criticism that will be helpful in making this better and maybe even simpler post a comment.
Thanks






Post a Comment