But what we found was we were imbeding the same code into multiple programs. Then we were getting phone calls stateing we did not get the e-mail or the fax. The final problem we were having was on the development box and QAS box whenever some one would test a program we would have to be carefull that we did not send the report out.
So we started a project with the following criteria:
- Create a simple interface for e-mailing/faxing reports.
- Allow for resending reports.
- Allow for changing who the report is to be sent to.
- Allow for viewing reports.
- Prevent test reports from being sent to customers.
- Create an archive ability.
First let me show you how the interface program works.
Selection Screen
This is the main screen
Clicking on History pulls up the following screen.
Currently the history screen really only makes sense for Faxes it will allways display emails as being verified.
Clicking on Details will display the following screen.
I hope this gives you an idea of how the program works.
Inorder to do this I created several custom tables.
Main Driver Table ZEMAIL_001
Receipents Table is ZEMAIL_002
Message Table ZEMAIL_003A
FTP Location ZEMAIL_004(Will discuss later why we need this)
Override Email Address for Test Systems ZEMAIL_005
If you have more then one attachment you will populate ZEMAIL_006
Those are the tables I am using to hold the data for this process.
Now then I created a function module for adding records to these tables.
Function Module is called ZADD_EMAIL I simply pass to the function module four tables.
Source Code for Function Module.
function zadd_email. *"---------------------------------------------------------------------- *"*"Local Interface: *" TABLES *" Z002A STRUCTURE ZEMAIL_002 *" Z003A STRUCTURE ZEMAIL_003A *" Z006A STRUCTURE ZEMAIL_006 OPTIONAL *" Z001A STRUCTURE ZEMAIL_001 *"---------------------------------------------------------------------- tables: zemail_001, zemail_002, zemail_003a, zemail_006. loop at z002a. move-corresponding z002a to zemail_002. zemail_002-source_program = z001a-source_program. zemail_002-email_date = z001a-email_date. zemail_002-email_time = z001a-email_time. insert zemail_002. endloop. loop at z003a. move-corresponding z003a to zemail_003a. zemail_003a-source_program = z001a-source_program. zemail_003a-email_date = z001a-email_date. zemail_003a-email_time = z001a-email_time. insert zemail_003a. endloop. loop at z006a. move-corresponding z006a to zemail_006. zemail_006-source_program = z001a-source_program. zemail_006-email_date = z001a-email_date. zemail_006-email_time = z001a-email_time. insert zemail_006. endloop. zemail_001-source_program = z001a-source_program. zemail_001-email_date = z001a-email_date. zemail_001-email_time = z001a-email_time. zemail_001-kunnr = z001a-kunnr. zemail_001-subject = z001a-subject. zemail_001-attachment = z001a-attachment. zemail_001-name_attach = z001a-name_attach. zemail_001-type_attachment = z001a-type_attachment. zemail_001-retain_until = z001a-retain_until. zemail_001-status = 'U'. insert zemail_001. endfunction.Now in your programs you will simply populate the internal tables and send to application server your pdf, text or whatever file you want and call this function module. (Of course you can simply populate the actual tables in your report program but I wanted to learn how to create a function module.
Now then the heart of this whole system the program that creates the email/fax itself.
I call it ZGENERIC_EMAIL
The following is a simple flow chart displaying how the program is working. Sorry for the size of the flow chart still new to blogging.
Data Section
report zgeneric_email. tables: zemail_001, zemail_008. types: begin of type_test, address_type type zemail_005-address_type, address type zemail_005-address, end of type_test. data: global_header type table of zemail_001 with header line, global_address type table of zemail_002 with header line, global_test_add type table of type_test with header line, global_message type table of zemail_003a with header line, global_attachment type table of zemail_006 with header line, global_mult(1) type c, it_message type standard table of solisti1 initial size 0 with header line, t_packing_list like sopcklsti1 occurs 0 with header line, t_receivers like somlreci1 occurs 0 with header line, t_receivers_bk like somlreci1 occurs 0 with header line, t_attachment like solisti1 occurs 0 with header line, ld_sender_address like soextreci1-receiver, ld_sender_address_type like soextreci1-adr_typ, ld_format type so_obj_tp, ld_attdescription type so_obj_nam, w_sent_all(1) type c, global_cnt type i, global_fax(1) type c, global_error(1) type c, local_time_dif type sy-uzeit, global_doc_data like sodocchgi1, ls_fax type sadrfd, bindata type table of solisti1 with header line, bindataotf type table of solisti1 with header line, objhead like solisti1 occurs 1 with header line.The HTML gets confused with < receiver > just remove the space between "< and receiver and >".
field-symbols < receiver > type c. select-options: s_source for global_header-source_program no intervals no-extension, s_date for global_header-email_date no intervals no-extension, s_time for global_header-email_time no intervals no-extension, s_only for global_address-email_address no intervals no-extension, s_type for global_address-com_type no intervals no-extension, s_test for global_address-email_address no intervals no-extension.
The program kept steping on top of itself so I put the following code in place to prevent that from happening I need to replace it with some locking logic but have not had a chance to do that.
local_time_dif = sy-datum - s_time-low. if local_time_dif < 60. wait up to 60 seconds. endif.Next we grab some data.
select single * from zemail_001 into global_header where source_program = s_source-low and email_date = s_date-low and email_time = s_time-low. if s_only-low = ''. select * from zemail_002 into table global_address where source_program = s_source-low and email_date = s_date-low and email_time = s_time-low. else. select * from zemail_002 into table global_address where source_program = s_source-low and email_date = s_date-low and email_time = s_time-low and email_address = s_only-low. endif. select * from zemail_003a into table global_message where source_program = s_source-low and email_date = s_date-low and email_time = s_time-low order by sequence. select * from zemail_006 into table global_attachment where source_program = s_source-low and email_date = s_date-low and email_time = s_time-low order by sequence.The following code is to handle if the attachements are stored off line I will discuss how we archived this later but for now I am going to show you the code
if global_header-stored_offline = 'Y'. if global_header-attachment <> ''. perform run_java. else. select * from zemail_006 into table global_attachment where source_program = s_source-low and email_date = s_date-low and email_time = s_time-low. if sy-subrc = 0. loop at global_attachment. global_header-attachment = global_attachment-attachment. perform run_java. global_mult = 'Y'. update zemail_006 set stored_offline = ' ' where source_program = global_header-source_program and email_date = global_header-email_date and email_time = global_header-email_time. endloop. endif. endif. endif.Now we go and get the attachments and build the message along with getting the receivers
perform get_attachment. * Populate message body text refresh it_message. loop at global_message. it_message = global_message-message. append it_message. endloop. * Build the Receivers file clear t_receivers. refresh t_receivers. t_receivers-receiver = s_test-low. t_receivers-rec_type = 'U'. t_receivers-com_type = 'INT'. t_receivers-notif_ndel = 'X'. if s_test-low <> ''. append t_receivers. endif.This section allow for the overrideing of the e-mail address/fax number if the program is being run on the development machine. My production client is 400
if sy-mandt > 399. loop at global_address. if global_address-com_type <> 'TEL'. t_receivers-receiver = global_address-email_address. t_receivers-rec_type = 'U'. t_receivers-com_type = 'INT'. t_receivers-notif_ndel = 'X'. append t_receivers. clear t_receivers. else. global_fax = 'Y'. endif. endloop. else. clear global_fax. loop at global_address. if global_address-com_type = 'TEL'. global_fax = 'Y'. endif. endloop. select address_type address from zemail_005 into table global_test_add. loop at global_test_add. if global_test_add-address_type = 'E'. t_receivers-receiver = global_test_add-address. t_receivers-rec_type = 'U'. t_receivers-com_type = 'INT'. t_receivers-notif_del = 'X'. append t_receivers. clear t_receivers. endif. endloop. endif.The following update is to prevent any other programs from grabing this same record I created this program so multiple copies could be run at the same time but it's not really needed again I should take this out and move to a SAP locking method but have not done this yet.
update zemail_001 set sent_date = sy-datum sent_user = sy-uname status = 'U' where source_program = s_source-low and email_date = s_date-low and email_time = s_time-low.I have two function calls to transmit the e-mails one for a single attachment and another for multiple attachments
if global_mult = 'N'. t_receivers_bk[] = t_receivers[]. call function 'SO_DOCUMENT_SEND_API1' exporting document_data = global_doc_data put_in_outbox = 'X' tables packing_list = t_packing_list object_header = objhead contents_bin = bindata contents_txt = it_message receivers = t_receivers exceptions too_many_receivers = 1 document_not_sent = 2 document_type_not_exist = 3 operation_no_authorization = 4 parameter_error = 5 x_error = 6 enqueue_error = 7 others = 8. else. t_receivers_bk[] = t_receivers[]. call function 'SO_NEW_DOCUMENT_ATT_SEND_API1' exporting document_data = global_doc_data put_in_outbox = 'X' tables packing_list = t_packing_list object_header = objhead contents_bin = bindata contents_txt = it_message receivers = t_receivers exceptions too_many_receivers = 1 document_not_sent = 2 document_type_not_exist = 3 operation_no_authorization = 4 parameter_error = 5 x_error = 6 enqueue_error = 7 others = 8. endif.I update zemail_008 with the results from the call to the function modules
global_error = sy-subrc. if sy-subrc <> 0. global_error = sy-subrc. else. perform insert_into008. endif.Now then we update the Header file with the fact that the e-mail was transmitted.
update zemail_001 set sent = global_error where source_program = global_header-source_program and email_date = global_header-email_date and email_time = global_header-email_time.The previous code was for e-mail only this part is for faxing.
if global_fax = 'Y'. clear t_receivers. refresh t_receivers. if sy-mandt > 399. loop at global_address. if global_address-com_type = 'TEL'. ls_fax-rec_state = 'US'. ls_fax-rec_fax = global_address-email_address. assign ls_fax toSubroutines get_attachment Local Datacasting. t_receivers-receiver = . t_receivers-rec_type = 'F'. t_receivers-com_type = 'TELFAX'. append t_receivers. clear t_receivers. endif. endloop. else. loop at global_test_add. if global_test_add-address_type = 'F'. ls_fax-rec_state = 'US'. ls_fax-rec_fax = global_test_add-address. assign ls_fax to casting. t_receivers-receiver = . t_receivers-rec_type = 'F'. t_receivers-com_type = 'TELFAX'. append t_receivers. clear t_receivers. endif. endloop. endif. t_receivers_bk[] = t_receivers[]. perform insert_into008. call function 'SO_DOCUMENT_SEND_API1' exporting document_data = global_doc_data put_in_outbox = 'X' tables packing_list = t_packing_list object_header = objhead contents_bin = bindata "contents_bin = bindataotf contents_txt = it_message receivers = t_receivers exceptions too_many_receivers = 1 document_not_sent = 2 document_type_not_exist = 3 operation_no_authorization = 4 parameter_error = 5 x_error = 6 enqueue_error = 7 others = 8. if sy-subrc <> 0. global_error = sy-subrc. endif. update zemail_001 set sent = global_error where source_program = global_header-source_program and email_date = global_header-email_date and email_time = global_header-email_time. endif. commit work.
data: local_file_name(100) type c, global_slen type i, global_key type i value 26101957, mi_handle type i, local_password(20) type c, global_dest like rfcdes-rfcdest, local_string(255) type c, local_start type i, local_count type i, begin of mtab_data occurs 0, line(132) type c, end of mtab_data, tab_lines like sy-tabix, local_bindata type table of solisti1 with header line, local_bindataotf type table of solisti1 with header line, blob_length type i, zemail_004_itab type table of zemail_004 with header line.Prelim work
global_mult = 'N'. clear: local_count, local_start. local_file_name = global_header-attachment.Now we need to check to see if were attaching just a text file or a PDF etc
if global_header-type_attachment <> 'PDF' and global_header-type_attachment <> 'CSV' and global_header-type_attachment <> 'JPG'.Text file
refresh bindata. open dataset local_file_name for input in text mode encoding default ignoring conversion errors. if sy-subrc eq 0. t_packing_list-doc_size = 0. clear t_packing_list. refresh t_packing_list. do. read dataset local_file_name into bindata. if sy-subrc <> 0. exit. endif. append bindata. clear bindata. enddo. close dataset local_file_name. * Fill the document data and get size of attachment clear global_doc_data. read table bindata index global_cnt. global_doc_data-doc_size = ( global_cnt - 1 ) * 80. * Fill the document data. global_doc_data-doc_size = 1. * Populate the subject/generic message attributes global_doc_data-obj_name = 'SAPRPT'. global_doc_data-obj_descr = global_header-subject. * Populate the subject/generic message attributes global_doc_data-obj_name = 'SAPRPT'. global_doc_data-obj_descr = global_header-subject. * Describe the body of the message t_packing_list-transf_bin = space. t_packing_list-head_start = 1. t_packing_list-head_num = 0. t_packing_list-body_start = 1. describe table it_message lines t_packing_list-body_num. t_packing_list-doc_type = 'RAW'. append t_packing_list. t_packing_list-transf_bin = 'X'. t_packing_list-head_start = 1. t_packing_list-head_num = 1. t_packing_list-body_start = 1. describe table bindata lines t_packing_list-body_num. t_packing_list-doc_type = ld_format. t_packing_list-obj_descr = ld_attdescription. t_packing_list-doc_size = t_packing_list-body_num * 80. t_packing_list-obj_name = global_header-name_attach. t_packing_list-doc_type = global_header-type_attachment. append t_packing_list. w_sent_all = 'X'. concatenate global_header-name_attach '.' global_header-type_attachment into objhead. append objhead. endif.When it's not a text file
else. wait up to 5 seconds. clear: bindata, bindataotf. refresh: bindata, bindataotf. select single * from zemail_004 into zemail_004_itab. global_slen = strlen( zemail_004_itab-password ).I choose to use FTP to get the attachment into an internal table in my program I am sure there are other ways of doing this but this is how I choose to do it. First you have to scramble the password.
*-- FTP_CONNECT requires an encrypted password to work call function 'HTTP_SCRAMBLE' exporting source = zemail_004_itab-password sourcelen = global_slen key = global_key importing destination = local_password.Now were ready to connect to the server.
global_dest = zemail_004_itab-address. call function 'FTP_CONNECT' exporting user = zemail_004_itab-userid password = local_password host = global_dest rfc_destination = 'SAPFTPA' importing handle = mi_handle exceptions not_connected = 1 others = 2.Pull the data from the server This section is to handle a single attachment
if local_file_name <> ''. call function 'FTP_SERVER_TO_R3' exporting handle = mi_handle fname = local_file_name importing blob_length = blob_length tables blob = bindata.The following will create the email attachments
* Fill the document data and get size of attachment clear global_doc_data. read table bindata index global_cnt. global_doc_data-doc_size = ( global_cnt - 1 ) * 80. * Fill the document data. global_doc_data-doc_size = 1. * Populate the subject/generic message attributes global_doc_data-obj_name = 'SAPRPT'. global_doc_data-obj_descr = global_header-subject. * Describe the body of the message clear t_packing_list. refresh t_packing_list. t_packing_list-transf_bin = space. t_packing_list-head_start = 1. t_packing_list-head_num = 0. t_packing_list-body_start = 1. describe table it_message lines t_packing_list-body_num. t_packing_list-body_num = 1. t_packing_list-doc_type = 'RAW'. append t_packing_list. t_packing_list-transf_bin = 'X'. t_packing_list-head_start = 1. t_packing_list-head_num = 1. t_packing_list-body_start = 1. describe table bindata lines t_packing_list-body_num. t_packing_list-doc_type = ld_format. t_packing_list-obj_descr = ld_attdescription. t_packing_list-doc_size = t_packing_list-body_num * 80. t_packing_list-obj_name = global_header-name_attach. t_packing_list-doc_type = global_header-type_attachment. append t_packing_list. w_sent_all = 'X'. concatenate global_header-name_attach '.' global_header-type_attachment into objhead. append objhead. call function 'FTP_SERVER_TO_R3' exporting handle = mi_handle fname = local_file_name importing blob_length = blob_length tables blob = bindataotf. * Fill the document data and get size of attachment clear global_doc_data. read table bindataotf index global_cnt. global_doc_data-doc_size = ( global_cnt - 1 ) * 80. * Fill the document data. global_doc_data-doc_size = 1. * Populate the subject/generic message attributes global_doc_data-obj_name = 'SAPRPT'. global_doc_data-obj_descr = global_header-subject. * Describe the body of the message clear t_packing_list. refresh t_packing_list. t_packing_list-transf_bin = space. t_packing_list-head_start = 1. t_packing_list-head_num = 0. t_packing_list-body_start = 1. describe table it_message lines t_packing_list-body_num. t_packing_list-doc_type = 'RAW'. append t_packing_list. t_packing_list-transf_bin = 'X'. t_packing_list-head_start = 1. t_packing_list-head_num = 1. t_packing_list-body_start = 1. describe table bindataotf lines t_packing_list-body_num. t_packing_list-doc_type = ld_format. t_packing_list-obj_descr = ld_attdescription. t_packing_list-doc_size = t_packing_list-body_num * 80. t_packing_list-obj_name = global_header-name_attach. t_packing_list-doc_type = global_header-type_attachment. append t_packing_list. w_sent_all = 'X'. concatenate global_header-name_attach global_header-type_attachment into objhead. append objhead.Multiple Attachments
else. loop at global_attachment. refresh local_bindata. clear local_bindata. * Fill the document data and get size of attachment clear global_doc_data. read table bindata index global_cnt. global_doc_data-doc_size = ( global_cnt - 1 ) * 255. * Fill the document data. global_doc_data-doc_size = 1. * Populate the subject/generic message attributes global_doc_data-obj_langu = sy-langu. global_doc_data-obj_name = 'SAPRPT'. global_doc_data-obj_descr = global_header-subject. global_doc_data-sensitivty = 'F'. * Describe the body of the message clear t_packing_list. if local_count = 0. t_packing_list-transf_bin = space. t_packing_list-head_start = 1. t_packing_list-head_num = 0. t_packing_list-body_num = 1. t_packing_list-body_start = 1. t_packing_list-doc_type = 'RAW'. local_start = 1. append t_packing_list. endif. call function 'FTP_SERVER_TO_R3' exporting handle = mi_handle fname = global_attachment-attachment importing blob_length = blob_length tables blob = local_bindata. describe table local_bindata lines tab_lines. append lines of local_bindata to bindata. objhead = global_attachment-name_attach. append objhead. local_count = local_count + 1. if local_count > 1. global_mult = 'Y'. endif. t_packing_list-transf_bin = 'X'. t_packing_list-head_start = local_count. t_packing_list-head_num = local_count. t_packing_list-body_start = local_start. t_packing_list-body_num = tab_lines. t_packing_list-doc_type = global_attachment-type_attachment. t_packing_list-obj_name = 'ANALAGE'. t_packing_list-obj_descr = global_attachment-description. t_packing_list-doc_size = blob_length + strlen( objhead ). local_start = local_start + tab_lines. append t_packing_list. clear t_packing_list. clear objhead. endloop. endif.Now then we need to disconnect from the server
call function 'FTP_DISCONNECT' exporting handle = mi_handle. * Fill the document data and get size of attachment clear global_doc_data. read table bindata index global_cnt. w_sent_all = 'X'. global_doc_data-doc_size = ( global_cnt - 1 ) * 255. * Fill the document data. global_doc_data-doc_size = 1. * Populate the subject/generic message attributes global_doc_data-obj_langu = sy-langu. global_doc_data-obj_name = 'SAPRPT'. global_doc_data-obj_descr = global_header-subject. global_doc_data-sensitivty = 'F'. endif. endform. " GET_ATTACHMENTUpdate the History file
*&---------------------------------------------------------------------* *& Form INSERT_INTO008 *&---------------------------------------------------------------------* * text *----------------------------------------------------------------------* * --> p1 text * <-- p2 text *----------------------------------------------------------------------* form insert_into008. data: local_sequence type zemail_008-sequence, local_com_type type zemail_002-com_type. clear local_sequence. select sequence from zemail_008 into local_sequence where source_program = global_header-source_program and email_date = global_header-email_date and email_time = global_header-email_time order by sequence descending. exit. endselect. loop at t_receivers_bk. local_sequence = local_sequence + 1. zemail_008-source_program = global_header-source_program. zemail_008-email_date = global_header-email_date. zemail_008-email_time = global_header-email_time. zemail_008-sequence = local_sequence. zemail_008-email_address = t_receivers_bk-receiver. zemail_008-sent_date = sy-datum. zemail_008-sent_time = sy-uzeit. select single com_type from zemail_002 into local_com_type where source_program = global_header-source_program and email_date = global_header-email_date and email_time = global_header-email_time and email_address = zemail_008-email_address. zemail_008-com_type = local_com_type. insert zemail_008. endloop. endform. " INSERT_INTO008Getting the file from the archive
*&---------------------------------------------------------------------* *& Form RUN_JAVA *&---------------------------------------------------------------------* * text *----------------------------------------------------------------------* * --> p1 text * <-- p2 text *----------------------------------------------------------------------* form run_java . data: local_commandline(350) type c, local_hold(350) type c, local_hold2(250) type c, local_file(100) type c, local_position type i. if sy-sysid = 'QAS'. sy-sysid = 'TST'. endif. if sy-sysid = 'PRD'. sy-sysid = 'PROD'. endif. search global_header-attachment for '/usr/sap/transfer/sapout/email/ar/stop_ship/'. if sy-subrc = 0. local_position = sy-fdpos + 44. local_file = global_header-attachment+local_position(100). else. search global_header-attachment for '/usr/sap/transfer/sapout/email/ar/zauto/'. if sy-subrc = 0. local_position = sy-fdpos + 40. local_file = global_header-attachment+local_position(100). else. search global_header-attachment for '/usr/sap/transfer/sapout/email/ap/zchecks/'. if sy-subrc = 0. local_position = sy-fdpos + 42. local_file = global_header-attachment+local_position(100). else. search global_header-attachment for '/usr/sap/transfer/sapout/email/'. if sy-subrc = 0. local_position = sy-fdpos + 31. local_file = global_header-attachment+local_position(100). endif. endif. endif. endif. replace all occurrences of '/' in global_header-attachment with '\' . concatenate '"\\SAP' sy-sysid '\rootbin' global_header-attachment '"' into local_hold2. concatenate '"' local_file '"' into local_hold. concatenate local_hold local_hold2 into local_commandline separated by ' '. call function 'WS_EXECUTE' exporting program = '\\mpfs-portalprod\sap\aws\dist\recvaws.bat' commandline = local_commandline inform = '' exceptions prog_not_found = 1. if sy-subrc = 0. if global_mult <> 'Y'. update zemail_001 set stored_offline = ' ' where source_program = global_header-source_program and email_date = global_header-email_date and email_time = global_header-email_time. endif. endif. endform. " RUN_JAVAI put this routine in but it's very tied into what were doing you would need to handle this on your own. Just as a side note were using the Amazon S3 Cloud to hold the archive.
This is the heart of the system but I have another three programs to add I will be adding those in my next blog session. If you would like the full code to this program without all the breaks please e-mail me and I will be happy to send it to you.