Summary:
A programming exit is an ABAP class that the system executes at a specific time during the processing of a workflow. Programming exits enable you to implement your own enhancements and adaptations.
ABAP classes that you use as programming exits must not use the following elements:
● COMMIT WORK
● ROLLBACK WORK
● Calling of function module DB_COMMIT
● Any type of RFC calls
● Any changes to a system element in the container
This article describes a business scenario where Program Exit can be used in workflow. Whenever a user creates travel expense report from ESS portal and attaches some documents with it, the attachments are linked to BUS2089 instance using Generic Object Services. Later on if an approver wants to see the attachments of the expense report, he/she should open the SAP back-end transaction (launched from portal using SAP GUI HTML). From there approver can view the attachments using GOS dropdown.
This process requires that approver should have access authorization to back-end transaction. At the same time to end user it is inconvenient to launch SAP transaction to view attachments of expense. Client requested a functionality where attachments of the expense report are directly available as work item attachment.
Reader should have basic understanding of SAP Workflow and Generic Object Services.
Author:Parag Parikh
Company: Deloitte Consulting LLP
Created on: 24th March 2012
Author Bio
Parag Parikh is an SAP ABAP, SAP workflow consultant with 4.5 years of experience. He has extensively worked on SAP ABAP and SAP workflow. Parag also has functional skills for SAP FS-CD solution. He has worked on many SAP modules including SAP FI/CO, SD, MM, QM, PP, PLM-RM, SRM, HCM, ESS/MSS and EH&S. Parag is working with Deloitte Consulting LLP as Workflow Consultant.
Requirements in detail
1) Files attached to any travel expense report created by employee are linked to instance of Business Object BUS2089 using Generic Object Services
2) The approver should be able to view the attachments when he receives the work item in his/her inbox (SBWP) or UWL
3) The approver can approve/reject or send the expense report back to employee for revision. While revising the expense report, employee may add/remove some attachments. Hence it is necessary that every time a work item is created for travel expense approval step, we copy the attachments of BUS2089 instance to work item.
Creating Programming Exit Class
1) Create class ZHRXMCL_TRIP_APP_PROG_EXT in SE24. Use interface IF_SWF_IFS_WORKFLOW_EXIT.
2) Interface IF_SWF_IFS_WORKITEM_EXIT contains method EVENT_RAISED. SAP webflow engine calls this method when events like work item created, ready etc. are fired by workflow environment. Hence this method can be used to identify work item related events.
3) In the forwarded declaration for the class, declare type pool SWRCO. This type pool contains necessary data declaration to use program exit.
Declare additional attributes as shown below in the class.Attrbiute WI_CONTEXT provides us the context of the work item for which program exit is trigerred.
4) Below is the sample code to be written in method EVENT_RAISED. We first check if the event triggered was 'CREATED'. If yes, we go ahead and call method AFTER_CREATION to handle event.
*--After creation, call out method
*--Set current work item context
me->wi_context = im_workitem_context.
IF im_event_name <> swrco_event_after_creation.
EXIT.
ENDIF.
*--Call after creation method
me->after_creation( ).
5) Files attached to any travel expense report created by employee are linked to instance of Business Object BUS2089 using Generic Object Services. Hence in the method AFTER_CREATION we should first try to read the GOS Attachment and then copy these attachments to work item.
Restriction on program exit for workflow is that, within program exit methods we can not have call to an RFC FM. SAP FM available for reading and copying GOS attachments are RFC FM or internally calls RFC FM. Example is FM BDS_BUSINESSDOCUMENT_GET_INFO. If we try to use SAP provided classes for GOS, again we need to investigate and make sure that the classes internally do not call RFC, which again makes this task laborious.
To overcome above restriction, one of the workaround is to create a custom event when work item is created. In response to this event, we can call a custom task which copies the attachment of travel expense to the approval work item. This will create a loose coupling between work item created event and process of copying GOS attachments to work item.
Shown below is the sample code for method AFTER_CREATION where we trigger the custom event.
*--Local Variables
DATA:l_workitem_id TYPE sww_wiid ,
l_value1 TYPE string ,
l_value2 TYPE string .
DATA:l_pernr TYPE persno ,
l_reinr TYPE reinr .
DATA: l_object_key TYPE swr_struct-object_key,
lt_container TYPE STANDARD TABLE OF swr_cont,
ls_container TYPE swr_cont.
*--Local reference to work item container
DATA:lrf_container TYPE REF TO if_swf_ifs_parameter_container.
*--Get work item ID
CALL METHOD wi_context->get_workitem_id
RECEIVING
re_workitem = l_workitem_id.
*--Get work item container
CALL METHOD wi_context->get_wi_container
RECEIVING
re_container = lrf_container.
*--Get Trip Number from container element
TRY.
CALL METHOD lrf_container->get
EXPORTING
name = 'TripNumber'
IMPORTING
value = l_value1.
* unit =
* returncode =
CATCH cx_swf_cnt_elem_not_found .
CATCH cx_swf_cnt_elem_type_conflict .
CATCH cx_swf_cnt_unit_type_conflict .
CATCH cx_swf_cnt_container .
ENDTRY.
*--Get employee number from container element
TRY.
CALL METHOD lrf_container->get
EXPORTING
name = 'EmployeeNumber'
IMPORTING
value = l_value2.
* unit =
* returncode =
CATCH cx_swf_cnt_elem_not_found .
CATCH cx_swf_cnt_elem_type_conflict .
CATCH cx_swf_cnt_unit_type_conflict .
CATCH cx_swf_cnt_container .
ENDTRY.
*--Call FM to get relationships if any of trip and copy attachments to work item
l_pernr = l_value2.
l_reinr = l_value1.
*--Create event to attach files
l_object_key = l_reinr.
ls_container-element = 'PersonnelNumber'.
ls_container-value = l_pernr.
APPEND ls_container TO lt_container.
ls_container-element = 'TripNumber'.
ls_container-value = l_reinr.
APPEND ls_container TO lt_container.
ls_container-element = 'WorkItem'.
ls_container-value = l_workitem_id.
APPEND ls_container TO lt_container.
CALL FUNCTION 'SAP_WAPI_CREATE_EVENT'
EXPORTING
object_type = 'ZHRXMTRIP'
object_key = l_object_key
event = 'SentForApproval'
TABLES
input_container = lt_container.
6) Shown below is the structure of custom business object ZHRXMTRIP and its event SentForApproval. We also see that event has parameters personnel number, trip number and approval work item number. These parameters can be used to first read GOS attachment and then copy them to approval work item.
Custom Business Object ZHRXMTRIP
Parameters for event SentForApproval
7) Define a custom method in any SE24 class that you are using for workflow task (class with interface IF_WORKFLOW) and add logic below to archive desired functionality. In order to modularize the code, we see that FM ZHREXP_ATTACHMENTS_TO_WIis defined. This FM contains desired functionality to copy and attach files to workitem.
*--Attach the BUS2089 attachments to work item
CALL FUNCTION 'ZHREXP_ATTACHMENTS_TO_WI'
EXPORTING
im_workitemid = i_workitem
im_pernr = i_pernr
im_reinr = i_reinr
EXCEPTIONS
no_attachment_found = 1
error_reading_attachment = 2
error_instantiating_attachment = 3
OTHERS = 4.
IF sy-subrc <> 0.
IF sy-subrc = 1.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
RAISING no_attachment_found .
ELSEIF sy-subrc = 2.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
RAISING error_reading_attachment .
ELSEIF sy-subrc = 3.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
RAISING error_instantiating_attachment.
ENDIF.
ENDIF.
😎 Below is the code for FM ZHREXP_ATTACHMENTS_TO_WI
FUNCTIONzhrexp_attachments_to_wi.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" VALUE(IM_WORKITEMID) TYPE SWW_WIID OPTIONAL
*" VALUE(IM_PERNR) TYPE PERNR
*" VALUE(IM_REINR) TYPE REINR
*" TABLES
*" T_ATTA STRUCTURE SWR_ATT_ID OPTIONAL
*" EXCEPTIONS
*" NO_ATTACHMENT_FOUND
*" ERROR_READING_ATTACHMENT
*" ERROR_INSTANTIATING_ATTACHMENT
*"---------------------------------------------------------------------
DATA BEGIN OF ls_docid_structure.
INCLUDE STRUCTURE soentryi1.
DATA END OF ls_docid_structure.
DATA: ls_lpor TYPE sibflporb,
lt_conn TYPE TABLE OF bdn_con,
ls_conn TYPE bdn_con,
ls_atta TYPE swr_att_id,
ls_key TYPE soodk,
ls_text TYPE soli,
ls_comment TYPE bcss_dbpc,
lt_hex_cont TYPE TABLE OF solix,
ls_hex_cont TYPE solix,
lrf_document TYPE REF TO cl_document_bcs,
lrf_bcs_exception TYPE REF TO cx_root,
ls_att_header TYPE swr_att_header.
DATA: l_att_txt TYPE string,
l_text TYPE string,
l_file_ext TYPE char3,
l_document_id TYPE sofolenti1-doc_id,
l_binfile TYPE xstring.