Workflow and WebDynpro with asynchronous tasks
Normally for the SAP Business Workflow with the transaction SBWP classic Dynpros are used for user interaction. Now I had the requirement to combine SAP Business Workflow with WebDynpro (WDA) to substitute the classical Dynpro.
The reason was the WebDynpro advantage over classical Dynpro.
For that i had first to create two classes:
1. WF-Class (Z_WD_WF_CLASS)
Create a class which is used in the wf task (In this case Z_WD_WF_CLASS).This class requires the interface IF_WORKFLOW, but an implementation is not required.
This class starts the WebDynpro application using the WF–task (Step 4.).
On the method OPEN_WD of class Z_WD_WF_CLASS the WebDynpro application is started. The WI_ID is passed as a URL parameter.
class Z_WD_WF_CLASS definition
public
final
create public .
public section.
interfaces BI_OBJECT .
interfaces BI_PERSISTENT .
interfaces IF_WORKFLOW .
class-methods OPEN_WD
importing
!_WORKITEM type SIBFLPOR
returning
value(_WI_OBJECT_ID) type ref to Z_WD_WF_CLASS .
protected section.
private section.
ENDCLASS.
CLASS Z_WD_WF_CLASS IMPLEMENTATION.
METHOD open_wd.
DATA lt_parameters TYPE tihttpnvp.
DATA ls_parameters LIKE LINE OF lt_parameters.
ls_parameters-name = 'WI_ID'.
ls_parameters-value = _workitem-instid+20. "Name muss so sein
APPEND ls_parameters TO lt_parameters.
CALL FUNCTION 'WDY_EXECUTE_IN_BROWSER'
EXPORTING
* PROTOCOL =
application = 'Z_WD_WF_FORM'
parameters = lt_parameters
* TRY_TO_USE_SAPGUI_THEME = ''
EXCEPTIONS
invalid_application = 1
browser_not_started = 2
action_cancelled = 3
OTHERS = 4.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
ENDMETHOD.
ENDCLASS.
2. WF-Event-Class (Z_WD_WF_EVENT_CLASS)
This class also requires the interface IF_WORKFLOW and has two events (APPROVE and REJECT), which can be fired on the WebDynpro application. The link to the WF–task is a GUID.
class Z_WD_WF_EVENT_CLASS definition
public
final
create public .
public section.
interfaces BI_OBJECT .
interfaces BI_PERSISTENT .
interfaces IF_WORKFLOW .
events APPROVE .
events REJECT .
methods CONSTRUCTOR
importing
!I_GUID_16 type GUID_16 .
class-methods GET_EVENT_OBJECT
returning
value(RO_EVENT_OBJECT) type ref to Z_WD_WF_EVENT_CLASS .
protected section.
private section.
data M_GUID_16 type GUID_16 .
data M_SIBFLPOR type SIBFLPOR .
ENDCLASS.
CLASS Z_WD_WF_EVENT_CLASS IMPLEMENTATION.
method BI_OBJECT~DEFAULT_ATTRIBUTE_VALUE.
endmethod.
method BI_OBJECT~EXECUTE_DEFAULT_METHOD.
endmethod.
method BI_OBJECT~RELEASE.
endmethod.
METHOD bi_persistent~find_by_lpor.
DATA lo_wd_wf_event TYPE REF TO z_wd_wf_event_class.
DATA l_guid_16 TYPE guid_16.
l_guid_16 = lpor-instid.
IF l_guid_16 IS INITIAL.
RETURN.
ENDIF.
CREATE OBJECT lo_wd_wf_event
EXPORTING
i_guid_16 = l_guid_16.
.
result = lo_wd_wf_event.
ENDMETHOD.
METHOD bi_persistent~lpor.
result = m_sibflpor.
ENDMETHOD.
method BI_PERSISTENT~REFRESH.
endmethod.
METHOD constructor.
m_sibflpor-catid = 'CL'.
m_sibflpor-typeid = 'Z_WD_WF_EVENT_CLASS'.
m_sibflpor-instid = i_guid_16.
m_guid_16 = i_guid_16.
ENDMETHOD.
METHOD get_event_object.
DATA lo_system_uuid TYPE REF TO if_system_uuid.
DATA l_guid_16 LIKE m_guid_16.
lo_system_uuid = cl_uuid_factory=>create_system_uuid( ).
l_guid_16 = lo_system_uuid->create_uuid_x16( ).
CREATE OBJECT ro_event_object
EXPORTING
i_guid_16 = l_guid_16.
ENDMETHOD.
ENDCLASS.
Now I can use the classes in the workflow:
3. Workflow
Create any workflow. For this example, I created the following workflow.
4. asynchronous task
A container must be created for the Event class Z_WD_WF_EVENT_CLASS.
5. WebDynpro
WDDOINIT in the Componentencontroller:
In WDDOINIT is the ID of the workitem that has been passed through the URL parameters queried.
The workitem ID is passed to the Fuba SAP_WAPI_READ_CONTAINER. Thus, the container contents of Z_WD_WF_EVENT_CLASS of the workitem can be determined.
The object of the container is passed to the WebDynpro.
METHOD wddoinit .
DATA lo_nd_wf_info TYPE REF TO if_wd_context_node.
DATA lo_el_wf_info TYPE REF TO if_wd_context_element.
DATA ls_wf_info TYPE wd_this->element_wf_info.
ls_wf_info-wi_id = cl_wd_runtime_services=>get_url_parameter( name = 'WI_ID' ).
*
lo_nd_wf_info = wd_context->get_child_node( name = wd_this->wdctx_wf_info ).
lo_el_wf_info = lo_nd_wf_info->get_element( ).
lo_el_wf_info->set_static_attributes(
static_attributes = ls_wf_info ).
*********************************************************
* GET EVENT
*************************************************************
DATA l_workitem_id TYPE swr_struct-workitemid.
DATA l_return_code TYPE sy-subrc.
DATA l_ifs_xml_container TYPE xstring.
DATA l_ifs_xml_container_schema TYPE xstring.
DATA l_task_id TYPE swr_struct-task.
DATA lo_container TYPE REF TO if_swf_cnt_container.
DATA lt_simple_container TYPE STANDARD TABLE OF swr_cont.
DATA lt_message_lines TYPE STANDARD TABLE OF swr_messag.
DATA lt_message_struct TYPE STANDARD TABLE OF swr_mstruc.
DATA lt_subcontainer_bor_objects TYPE STANDARD TABLE OF swr_cont.
DATA lt_subcontainer_all_objects TYPE STANDARD TABLE OF swr_cont.
* DATA lo_wf_event TYPE REF TO /kibf/nacl_wf_event.
DATA ls_sibflpor TYPE sibflpor.
DATA l_guid_16 TYPE guid_16.
l_workitem_id = ls_wf_info-wi_id.
CALL FUNCTION 'SAP_WAPI_READ_CONTAINER'
EXPORTING
workitem_id = l_workitem_id
language = sy-langu
user = sy-uname
buffered_access = 'X'
IMPORTING
return_code = l_return_code
ifs_xml_container = l_ifs_xml_container
ifs_xml_container_schema = l_ifs_xml_container_schema
TABLES
simple_container = lt_simple_container
message_lines = lt_message_lines
message_struct = lt_message_struct
subcontainer_bor_objects = lt_subcontainer_bor_objects
subcontainer_all_objects = lt_subcontainer_all_objects.
cl_swf_cnt_factory=>create_task_container(
EXPORTING
im_task_id = 'TSXXXXXXXXX' "ID of the WF task
IMPORTING
ex_task_container = lo_container
).
TRY.
CALL METHOD cl_swf_ifs_conversion_base=>if_swf_ifs_conversion~import_from_ifs_xml
EXPORTING
ifs_xml_stream = l_ifs_xml_container
target_container = lo_container
* import_param = 'X'
* export_param = 'X'
* local_elements = 'X'
* no_system_elements = SPACE
* IMPORTING
* num_elements_imported =
* error_handle =
.
ENDTRY.
TRY.
CALL METHOD lo_container->if_swf_ifs_parameter_container~get
EXPORTING
name = 'Z_WD_WF_EVENT_CLASS'
IMPORTING
value = ls_sibflpor
* unit = <fs_unit>
returncode = l_return_code.
l_guid_16 = ls_sibflpor-instid.
CREATE OBJECT wd_this->mo_wd_wf_event_class
EXPORTING
i_guid_16 = l_guid_16.
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.
ENDMETHOD.
V_MAIN->ONACTIONAPPROVE
When the user selects an action, the event object is used and fires the appropriate event (in this example APPROVE).
METHOD onactionapprove .
DATA ls_sibflpor TYPE sibflpor.
ls_sibflpor = wd_comp_controller->mo_wd_wf_event_class->bi_persistent~lpor( ).
TRY.
cl_swf_evt_event=>raise(
EXPORTING
im_objcateg = ls_sibflpor-catid
im_objtype = ls_sibflpor-typeid
im_event = 'APPROVE'
im_objkey = ls_sibflpor-instid
* im_event_container =
).
CATCH cx_swf_evt_invalid_objtype .
CATCH cx_swf_evt_invalid_event .
ENDTRY.
COMMIT WORK.
ENDMETHOD.
In the instance linkages (transaction SWEINST) you can see the link between GUID (Object Key) and workitem ID (Receiver Key)
Can you clarify the steps of the "get event" and their purpose? I think this can be done in an easier way: for example catid, typeid should be known and maybe transfer the instid as an importing parameter.
Same question about the binding to the task. shouldn't this be a direct binding?
The event object must be instantiated for each new WF-task (because I need an ID for each workitem ID). I have found no way out of it to get a static method and directly through data binding.
Is this possible otherwise?
I think you should add some kind of background story for this (why/when you might want to implement something like this etc.) and also add some explanations to the code and screenshots (what is the purpose of the code, why are you doing the binding like this etc.). Now I assume that this will be really difficult to follow for developers with limited experience (who might find this really useful & helpful otherwise)
Kind regards,
Karri
Thank you for your advice, I have reworked it. I hope it is now easier to understand.
Document is really good and very helpful.
Please provide some explanation and screen shots for below.
1. what structure used for ls_wf_info and node , attributes created in web dyn pro
component.
2. MO_WD_WF_EVENT_CLASS ??
1. ls_wf_info must have a field for the workitem id and can have some more fields which you can use in the workflow.
2. MO_WD_WF_EVENT_CLASS is the object of class Z_WD_WF_EVENT_CLASS (chapter 2).