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 WFtask (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 WFtask is a GUID.

The static method GET_EVENT_OBJECT creates an object of class Z_WD_WF_EVENT_CLASS with a GUID, which will be passed to the WFtask data binding (Step 4). The other methods are necessary to the use of the WF task with the class.



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.

2015-04-03 15_34_57-Workflow Builder - Display 'z_wd_wf'.png

4. asynchronous task

The class Z_WD_WF_CLASS with the OPEN_WD method is used for object method of the WF task. These WFtask must be asynchronous because the WF task is terminated by an event.

2015-04-03 15_36_06-Standard Task_ Display.png

A container must be created for the Event class Z_WD_WF_EVENT_CLASS.

2015-04-03 15_42_05-Standard Task_ Display.png

This allows the two events APPROVE and REJECT are entered in the tab “Terminating events”.

2015-04-03 15_41_24-Standard Task_ Display.png

Data binding passes the Object Class XX passed with the GUID.


Furthermore, the Web Dynpro application is required:

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)


Result


By clicking on the button “APPROVE” the event “APPROVE” is fired, thereby terminating the WF task.


Thereafter, the entry in the instance linkages is no longer included.

Other suggestions?

To report this post you need to login first.

6 Comments

You must be Logged on to comment or reply to a post.

  1. Ronen Weisz

    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?  

    (0) 
    1. Johannes Konings Post author

      The event object must be instantiated for each new WFtask (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?

      (0) 
  2. Karri Kemppi

    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

    (0) 
      1. phanikumar yennamani

        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  ??

        (0) 
        1. Johannes Konings Post author

          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).

          (0) 

Leave a Reply