This action might not be possible to undo. Are you sure you want to continue?
Sending E-Mail from ABAP - Version 610 and Higher - BCS Interface
Thomas Jung Business Card Company: Kimball International Sep. 08, 2004 03:13 AM Permalink
There seems to be a lot of requests in the forums for tips on how to send E-Mail from ABAP. Based upon these requests, I thought I would pull together some resources and samples on the subject. The setup and samples are quite different on different releases. Yesterday I posted a weblog for releases <=46D that used the API function modules. This weblog focuses on releases 610 and higher. There are many changes in this release. For one there is no longer any reliance on external executables to send the mail. The SAP Kernel now contains native SMTP processing capabilities in the Internet Connection Manager (ICM). Also we have a new ABAP interface that can be used called the Business Communication Services (BCS). All samples came from a 620 system.</p>
Configuration ICM Setup
Before we start with any ABAP coding, we first have to setup and configure a few things. Much of this configuration may have already been completed by your Basis group. Unlike in release 46D and lower, we have no external executables to setup. There is no need to configure the Internet Mail Gateway or create any RFC destinations. However there is some setup that needs to be done to the ICM to activate and configure SMTP processing. OSS Note 455140 gives an excellent step-by-step guide to configuring SMTP. I will just give you a quick check list here without going into all the configuration details provided by this note. 1. If you are one release 610 only, you must add the SMTP Plug-In entry in your instance profile. This is not needed as of release 620 because the system is delivered with SMTP as an integrated part of the ICM. 2. In the instance profile, specify the TCP/IP port that SMTP will listen on. Port 25 is the default. 3. If you have multiple clients in your SAP system, you will want to setup a virtual host for each client. 4. Verify in transaction SICF that you have a SMTP node and that it is activated. It should look
something like the following:
Well we are finished with all the low level setup. Now we need to connect the lowest layer (the ICM) to SAP's middle message layer: SAPConnect. SAPConnect sits between the application layer (such as SAP Office) and the lowest layer hiding all the technical details. All of the SAPConnect monitoring and configuration can be reached from one transaction called SCOT. First SCOT has several different views that can be used. One shows the Jobs that have been setup, the other shows setup by routings, etc. I prefer to start SCOT in System Status View. This shows a tree with the communication types, the nodes setup for each type and the number of messages in each status.
The following is a screen shot of this view:
We want to create a new node in SAPConnect for our E-Mail. As of release 46C you have a quite nice Wizard that walks you through the process. However you can also control all settings directly in a dialog maintenance window. This is a screen shot of our Email Node.
You can see that to setup this node all we have to do is specify the connection information for an external mail server. In our case we give the host name for our Corporate Microsoft Exchange
Server. Also at this level we can specify the code page that we want all messages converted to before they are sent. I choose Unicode UTF-8 because we have an MDMP system that contains English, German, Spanish, Thai, and Polish languages. By converting to UTF-8 we get around the problem described in OSS note 331418. You do NOT have to have a Unicode system or kernel to support this functionality. Please refer to OSS Note 633265 for the necessary support package levels for this functionality to be complete. Now lets look at the configuration on our only configured address type: Internet. We don't do any outbound filtering to different nodes. Therefore the address areas is set to the wildcard '*'. To comply with the Unicode setup we have all output formats being converted to PDF as OSS note 633265 suggests.
Next we need to schedule a job that will transmit out E-mails. Normally E-Mails will only go out when this job runs, so you probably want to schedule it quite frequently. (Later in my code example, I will show you how using the BCS interface you can send E-mails immediately as well). In our production system we run this job every 5 minutes. The job setup can also be done from SCOT. Switch your view to JOBS. You can then hit CREATE to start the process of scheduling a new Send Job.
Running a Test
We are just about ready to run our first test. The only other requirement is that the sender of any Email, must have their return address setup in their user master. If you don't maintain this data, you will get a send error. If you want an SAP program to generate these addresses for you, have a look at OSS note 104263. Also in 46C and higher you have the option to configure the system to generate the sender address on the fly. OSS note 320443 gives you the details of this solution. You
can see from the following screen shot the field that must be maintained in the user master:
Let's try a test now. I like to start by testing using the SAP short message functionality. You will want to use menu System->Short Message. From here we will prepare a short test message. If you don't want to wait for your batch job to run to find out if your message went through, just jump back to transaction SCOT. From the System Status view you can choose Ctrl+F7 or the Execute Icon to start the send process for a communication type. You should receive your test message if everything is setup fine.
Let's say that everything didn't go fine. Where do we start trouble shooting? Once again start with the transaction SCOT. The system status shows us our status overview. You can see the number of messages in Error or Waiting. If you have a lot of messages in Waiting status, your send job probably isn't scheduled. The overview screen is nice, but you probably want more details. From SCOT you can choose Utilities->Overview of Send Orders (Or Tcode SOST). This transaction will give a detailed report by Address type, status, date, time and sender. For each message we can view the transmission history, the message itself, and the trace log. You can also try and resubmit the message from here. If you need to activate tracing, you want to return to transaction SCOT. You then choose Utilities>Trace->Internal Trace. You can also view all traces from here without having to go through SOST.
If everything has worked up to this point, then you are ready to start writing your own E-mail calls in ABAP. The example I am going to share with you is actually a function module that we wrote. We wanted to write a function module that would really simplify the process of sending E-mail for our newer ABAP developers. We wanted them to just specify a few parameters and be off and running without having to understand the BCS classes. Hopefully you will find this function module helpful as well as being a good tutorial on how to use the BCS classes.
The function module Interface is quite simple in that it only has 3 input parameters.
The first is an optional parameter used to set what requested statuses types will be returned. You can specify N for No Statuses, E for Only Error Statuses, or A for All Statuses. The second parameter is an internal table that will hold all of our document parts. The following is the line type that defines this internal table type:
The first entry in this table will become our E-Mail Body. All other parts will become attachments in the email. For each record in this table you must specify the document type as a value defined in the foreign key table TSOTD. You can also supply the subject line (or attachment name) in the subject field. You can then specify the content of this document part in either the CONTENT_TEXT (ASCII text) or CONTENT_HEX (Binary data such as PDFs). You will notice that the structure of these two content areas match those used by the API function modules in 46D and lower. This makes for a very easy change from the API function modules to the BCS Classes. The third parameter is another internal table that will hold all of our recipients. The following is the line type that defines this internal table type:
The calling program has the choice of setting a full email address directly into the C_ADDRESS field or just specifying an SAP User ID in the UNAME field. If the calling program chooses to use the UNAME field, the function module will lookup the user's E-Mail address from their user master record. Finally there are several flags that can be set per recipient such as No Forward, Express Message, etc. The function module has no Exporting, Changing, or table parameters. The only exception that is returned is the BCS Exception class itself: CX_BCS.
Function Module Code
We start off the coding in our function module with a few class definitions. We then some variable declarations. *----------------------------------------------------------------------* * CLASS-DEFINITIONS * *----------------------------------------------------------------------* DATA: send_request TYPE REF TO cl_bcs. DATA: document TYPE REF TO cl_document_bcs. DATA: sender TYPE REF TO cl_sapuser_bcs. DATA: recipient TYPE REF TO if_recipient_bcs. DATA: exception_info TYPE REF TO if_os_exception_info, bcs_exception TYPE REF TO cx_bcs.
*----------------------------------------------------------------------* * INTERNAL TABLES * *----------------------------------------------------------------------* DATA: l_mailtext TYPE soli_tab. DATA: l_mailhex TYPE solix_tab. DATA: iaddsmtp TYPE bapiadsmtp OCCURS 0 WITH HEADER LINE. DATA: ireturn TYPE bapiret2 OCCURS 0 WITH HEADER LINE. *----------------------------------------------------------------------* * VARIABLES *
Next we start the execution code itself. We will wrap all of our code inside a try...catch statement in order to trap any BCS errors. We will then start by creating an instance of the BCS persistent class that we will call send_request. TRY. * Create persistent send request send_request = cl_bcs=>create_persistent( ). Now we are ready to create the e-mail content itself. The first record in our documents importing parameter will become the main e-mail document. All other content will be inserted as attachments to this document. DATA: first(1) TYPE c. CLEAR first. DATA: documents_line LIKE LINE OF documents.
LOOP AT documents INTO documents_line. IF first IS INITIAL. MOVE 'X' TO first. * Build the Main Document IF documents_line-content_hex IS INITIAL. document = cl_document_bcs=>create_document( i_type = documents_line-type i_text = documents_line-content_text i_subject = documents_line-subject ). ELSE. document = cl_document_bcs=>create_document( i_type = documents_line-type i_hex = documents_line-content_hex i_subject = documents_line-subject ). ENDIF. ELSE.
Now we can add our document (and its attachments) to our send request. * Add document to send request CALL METHOD send_request->set_document( document ).
Next we will create a sender object (this function module automatically uses the current user) and place it into our send request. * Get sender object sender = cl_sapuser_bcs=>create( sy-uname ). * Add sender CALL METHOD send_request->set_sender EXPORTING i_sender = sender. Following the sender, we can add our recipients to the e-mail. If requested by the calling program we will lookup the recipients e-mail address from their SAP User Master using the BAPI, BAPI_USER_GET_DETAIL. If an e-mail address isn't maintained there, we will try and create one by
concatenating their user id together with our default company domain name. DATA: recipients_line LIKE LINE OF recipients. LOOP AT recipients INTO recipients_line. IF recipients_line-c_address IS INITIAL. * Create recipient CLEAR iaddsmtp. REFRESH iaddsmtp. CLEAR bapiadsmtp. CLEAR recipient. * Read the E-Mail address for the user CALL FUNCTION 'BAPI_USER_GET_DETAIL' EXPORTING username = recipients_line-uname TABLES return = ireturn addsmtp = iaddsmtp. LOOP AT iaddsmtp WHERE std_no = 'X'. CLEAR bapiadsmtp. MOVE iaddsmtp TO bapiadsmtp. ENDLOOP. * If no E-mail address w as found, create one. We are almost done now. All we have to do is set the return status attributes of the message. * Set that you don't need a Return Status E-mail DATA: status_mail TYPE bcs_stml. status_mail = requested_status. CALL METHOD send_request->set_status_attributes EXPORTING i_requested_status = requested_status i_status_mail = status_mail. Remember earlier that I said that e-mails had to wait for a job to run to actually send them out. Well here is opportunity to by-pass this job and send them immediately. The E-mails still get logged in the system and can be monitored from SCOT (or SOST). The following code will trigger the send immediately. * set send immediately flag send_request->set_send_immediately( 'X' ).
Finally we tell the system to send our email. We follow this up with a commit work statement. Because the BCS is written as a Persistent Object Class, no activity will take place until we trigger the commit work. We close by catching the BCS errors into the BCS exception class. * Send document CALL METHOD send_request->send( ).
COMMIT WORK. CATCH cx_bcs INTO bcs_exception. RAISE EXCEPTION bcs_exception. ENDTRY.
Finally here is the complete code sample provided for easier viewing: FUNCTION z_e_keg_send_simple_email. *"---------------------------------------------------------------------*"*"Local interface: *" IMPORTING *" VALUE(REQUESTED_STATUS) TYPE BCS_RQST DEFAULT 'E' *" VALUE(DOCUMENTS) TYPE ZES_KEG_EMAIL_DOCUMENTS *" VALUE(RECIPIENTS) TYPE ZES_KEG_EMAIL_RECIPIENTS *" RAISING *" CX_BCS *"---------------------------------------------------------------------Thomas Jung is an Application Developer for Kimball Electronics Group (www.kegroup.com) and a huge fan of ABAP. Comment on this weblog Showing messages 1 through 25 of 25. Titles Only Main Topics Oldest First • If the e-mail address is illegle,how can I catch it? 2005-06-30 23:31:44 Vincent zklee Business Card [Reply] Hi Thomas, Thanks for your excellent contributions. When I use BCS to send mail form SAP, I found I can't mark out these which send successfully to the target address and which do not.So can you tell me how to differenitiate the two sitiuations. For example: if the mail address(email@example.com) is legle,I can marked it with the title:mail successfully send to And if the mail address(firstname.lastname@example.org) is illegle,it will be marked with the title:mail not send to. Regards, Vincent.
Difference between API & BCS 2005-06-30 13:40:27 Ramki Maley Business Card [Reply] Hi Thomas,
Thanks for your excellent contributions. I have implemeted the code for BCS in our 620 system as recommended and it works great. I did notice that mail sent using BCS is instantly pushed out of SAP where as mail sent through the SO_NEW_DOCUMENT_ATT_SEND_API1 function module does not. It goes out if I manually start the send process from txn SCOT or a scheduled job for program RSCONN01 runs. Are you aware of any settings/config that would make the API FM mail to be sent instantly?
Difference between API & BCS 2005-06-30 13:51:01 Thomas Jung Business Card [Reply] That is one of the nicest advantages of using the new interface.
The thing I have seen is sort of a hack. Right after you call the API function module, in code you force the start of RSCONN01. I have seen this programmed different ways. You could schedule a job to run on event and then trigger the event, or sumit the program (inline or to the background). But obviously you have the overhead of running RSCONN01 every time you send an email.
Distribition list 2005-06-23 01:52:06 Sven Gießner Business Card [Reply] Hi Thomas ,
Thank you for this ... Previously I was using the API function moduls to create mails. Now I switch to the BCS functions and have a problem ... How can I use predefined distribution lists with the BCS methods? Is there such a possibility to avoid loop constructs to add the adresses ? Thank you, Sven
Distribition list 2005-06-23 06:13:02 Thomas Jung Business Card [Reply] I haven't used a distribution list before, but it looks like you could use the method ADD_RECIPIENT_FROM_SOOS1 of the Send Request object to add a recipient via an internal table instead of the Recipient object. This internal table appears to support distribution lists. According to the online help you should also be able to use class CL_DISTRIBUTIONLIST_BCS to create a recipient object from a distribution list. This class does inherit interface IF_RECIPIENT_BCS, so this should work as well.
As to the loop constructs: I'm not sure what you mean. You want to avoid coding a loop? Perhaps if you explain a little more what you are wanting to avoid I could help you out.
Distribition list 2005-06-27 02:42:12 Sven Gießner Business Card [Reply] Hi Thomas, thanks for your help and hints ... Unfortunately I have no clue about this OO codings ... but I will try to solve it ...
and as to the loop constructs: I meant that I don't wan't to create the recipients one by one within a loop (with send_request->add_recipient) but in one ot two steps :-)
No text in email body, attachment only 2005-05-24 15:54:24 Bruce Tjosvold Business Card [Reply] Thomas,
Thanks for this example. Previously I was using function module SO_NEW_DOCUMENT_ATT_SEND_API1 to create e-mails either with or without attachments. One question that I have for you is, how is your source code displaying the first line of the documents table in the e-mail body. I only see a method being called to create the attachment, text or hex. In the function module that I created from your example, the attachment shows up in the email body, but the first line from the document table is missing from the e-mail body. Thanks Bruce
No text in email body, attachment only 2005-05-25 06:51:08 Thomas Jung Business Card [Reply] Well I have the IF check for the first record. If it is the first record in the Interal table I am processing, I call the create_document method. On all subsequent passes throught the table loop I use the Add_Attachment method.
I wasn't able to recreate the problem you describe in my system. However I would suggest debugging and focusing on this are of code to see what is happening in this loop.
No text in email body, attachment only 2005-05-25 13:04:52 Bruce Tjosvold Business Card [Reply] Thomas,
Your code is working exactly as you intended. I misread the documentation. I was expecting that the first line of each documents CONTENT_TEXT internal table would be written to the body of e-mail message. Thanks Bruce
Example programs 2005-05-13 05:11:23 Renske van Weperen Business Card [Reply] Hi all,
Maybe it is also useful to look at the following example programs: BCS_EXAMPLE_1 - BCS_EXAMPLE_6. Regards, Renske van Weperen
Extra FM 2005-03-30 00:43:35 BOBET Francois Business Card [Reply]
Thanks for this coding, But could you explain how to do an attach doc with n lines. When I fill DOCUMENTS, first line is for the mail ( ok ) and next lines create n documents instead of one document with n lines. Best regards François
Extra FM 2005-03-30 04:13:49 BOBET Francois Business Card [Reply] After test I find how to do it Your Work is very famous.
Others FM ?
Extra FM 2005-03-30 06:17:01 Thomas Jung Business Card [Reply] I'm glad to see that you got it worked out.
What did you mean by other FM? Did you have a suggestion for a future topic?
FM works fine but i want to run it through program 2005-03-17 22:01:42 Kapil Sonavane Business Card [Reply] Dear Tomas,
Thank you very much for such good explanation. I have followed all the step which are explained here. When I run Function Module through transaction code SE37 I am able to send mail to one recipent. Now i want to call above created function module through program so that I can send mail to multiple users and with attachments. I am facing problem while developing a program. Here is the code which I am trying ************************************* REPORT Z_SENDMAIL . data : i_doc type ZES_TIS_EMAIL_DOCUMENTS, i_recp type ZES_TIS_EMAIL_RECIPIENTS.
clear : i_doc . i_doc-TYPE = 'INT'. i_doc-SUBJECT = 'TEST MAIL FROM KAPIL THROUGH SAP'. i_doc-CONTENT_TEXT-LINE = 'TEST LINE 1'. append i_doc.
clear : i_recp .
i_recp-UNAME = 'TABAP'. i_recp-C_ADDRESS = 'email@example.com'. append i_recp. i_recp-UNAME = 'THR'. i_recp-C_ADDRESS = 'firstname.lastname@example.org'. append i_recp. i_recp-UNAME = 'TBASIS'. i_recp-C_ADDRESS = 'email@example.com'. append i_recp.
CALL FUNCTION 'Z_SEND_EMAIL' EXPORTING REQUESTED_STATUS = 'E' DOCUMENTS = i_doc RECIPIENTS = i_recp. ******************************************* It is Giveing syntax Error : "I_DOC" is a table without a header line and therefore has no component called "TYPE". when i declare DATA : I_DOC TYPE ZES_TIS_EMAIL_DOCUMENTS WITH HEADER LINE. DATA : I_RECP TYPE ZES_TIS_EMAIL_RECIPIENTS WITH HEADER LINE. then there is no syntax error but while executing it give short dump saying that: Type conflict when calling a function module. The function module interface allows you to specify only fields of a particular type under "DOCUMENTS". The field "I_DOC" specified here has a different field type. Please tell me how to correct the error.
FM works fine but i want to run it through program 2005-03-18 05:10:16 Thomas Jung Business Card [Reply] The problem is that the table I_DOC and I_RECP need to be tables without header lines. This is the new approach that SAP is using and is a requirement in ABAP OO programming. Instead of the implicit header line you are used to, you must explicitly create one. So change your declaration of the two tables back to the way you originally had them. Then use the following to append data into them.
intead of: clear : i_doc . i_doc-TYPE = 'INT'. i_doc-SUBJECT = 'TEST MAIL FROM KAPIL THROUGH SAP'. i_doc-CONTENT_TEXT-LINE = 'TEST LINE 1'. append i_doc.
Use the following. field-symbols: <wa_doc> like line of i_doc. append initial field to i_doc assigning <wa_doc>. <wa_doc>-type = 'INT'. <wa_doc>-subject = 'TEST MAIL FROM KAPIL THROUGH SAP'. Now the next problem you might run into is the fact that i_doc is a complex structure. That means that CONTENT_TEXT and CONTENT_HEX are both internal tables contained in the cell of another internal table (i_doc). So for that you would need another work area (header line). Field-symbols <wa_line> type SOLI. append initial line to <wa_doc>-CONTENT_TEXT assigning <wa_line>. <wa_line>-line = 'TEST LINE 1'. If you aren't used to this internal tables without header lines, you might want to have a look on SDN or the On-Line for this subject. In the On-Line help look especially at the section on code changes for ABAP OO.
Thank You, How can I send an attachment using above FM 2005-03-22 01:24:51 Kapil Sonavane Business Card [Reply] Thank You Thomas, The way you suggested is working fine. Can you tell me how to send attachment through above FM??
Kapil Shivaji Sonavane
Thank You, How can I send an attachment using above FM 2005-03-22 05:18:07 Thomas Jung Business Card [Reply] You just need to add more than one record to the Docs internal table. All records but the first will be treated as attachments.
Thank You, I want to send Output of the program as an Excel attachment 2005-04-04 03:28:40 Kapil Sonavane Business Card [Reply] Dear Tomas, Thanks a lot,
I tried the to send attachment by adding records to 'docs' internal table but it is a text file attachment. How to send report output in a excel file as an attachment. Kapil
Thank You, I want to send Output of the program as an Excel attachment 2005-04-04 11:03:08 Thomas Jung Business Card [Reply]
I'm not exactly sure what you are looking for here. You can add text or binary attachments to the email throught the 'docs' internal table. You add text based content via content_text and binary based content via content_hex. Is your question more specific about how to get report output to excel? That's really another topic all together. The best solution really depends upon how your report is being generated and what connection there is to the email program. Please supply a few more details on what you are wanting to do.
"DOCUMENTS" is not a internal table! 2005-02-01 01:06:55 James Lee Business Card [Reply] Dear Thomas,
DATA: documents_line like line of documents.
"DOCUMENTS" is not a internal table! 2005-02-01 01:09:02 James Lee Business Card [Reply] Dear Thomas,
I got a error after execute the function. "DOCUMENTS" is not a internal table. It's in row 52 --> DATA: documents_line like line of documents. What I can do? We are using SAP 4.7
"DOCUMENTS" is not a internal table! 2005-02-01 01:46:19 Thomas Jung Business Card [Reply] I would be willing to bet that you defined your input parameter DOCUMENTS as the row type and not a table type. The structure I showed in the WebLog then has to be used to create a table type. Notice the difference in the name of what I used in the screen shot for the structure and what is used in the interface of the function module itself.
Great work !! 2005-01-26 04:30:57 tafkap P. Business Card [Reply] Thanks you very much for this function.
One question, when i receive a e-mail, the body of email's text is not empty fill, i've just a file joined with the text of my message. I suppose that i must use a parameter ?
Great work !! 2005-01-28 05:05:18 Thomas Jung Business Card [Reply]
I can't say that I have experienced this before. What did you set you document type to? (documents_line-type). Perhaps you can post your sample code here. Sorry I could be more help up front. Hopefully with a code sample we can figure it out.
Thanks 2004-09-10 15:28:50 Dennis Gaule Business Card [Reply] Thanks Thomas. This information is going to be very helpful!
Dennis Gaule SAP BW
This action might not be possible to undo. Are you sure you want to continue?
We've moved you to where you read on your other device.
Get the full title to continue listening from where you left off, or restart the preview.