cancel
Showing results for 
Search instead for 
Did you mean: 

Dynamic parallel processing in SAP S/4HANA flexible workflow

SumitKundu
Active Participant
0 Kudos

Hi,

We have an existing custom classic workflow for purchase order where the approval is based on multiple cost centers of line items. It uses dynamic parallel processing (par-for-each) using multiline element and a sub workflow 

In SAP S/4HANA in-premise flexible workflow, is it possible to achieve dynamic parallel processing if we create a custom workflow scenario? Would appreciate if any content or blog post is available. 

Best regards,

Sumit 

 

Accepted Solutions (0)

Answers (4)

Answers (4)

KjetilKilhavn
Active Contributor

My conclusion is that flexible workflow doesn't support the kind of dynamic parallell processing that you have in classic workflows, but you can have parallell processing for multiple approvers of a step in flexible workflow.

I see that you try to set a table of amount values, but that shoudn't work, you have to set a single amount in each task.
One of my conclusions is that the context is missing in parallel processing in flexible workflow - all the work items are "identical" except for the agent. So let's assume you have cost centers CC1 and CC2 in your case, when you get to your custom code there is no way to figure out whether the work item is for the owner of CC1 or the owner of CC2. The best you can hope for, if the agent is available (I didn't go this far) is to work your way back from the agent to the cost center and then determine the correct amount based on cost center(s) that person is responsible for.

This is very different from the classic workflow dynamic parallel processing where you would set a "driver element" and that element would be available in the container of your parallel work items (or workflows) to identify the context.

SumitKundu
Active Participant
0 Kudos
Exactly @KjetilKilhavn. I also came to this conclusion. I checked you already had asked this similar question already. I checked the workflow log and the agent is not set at task container it shows. is not set unfortunately ,
SBach
Product and Topic Expert
Product and Topic Expert

Hi Sumit,

we always recommend to use the standard flexible workflow (here for PO). In this it seems to be possible to define approvers per accounting object: https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/af9ef57f504840d2b81be8667206d485/c1acc9fa4c2041be952...But I am not an expert in this. Please ask the application colleagues as well for a solution.

There is a limited implementation in flexible workflow for par-for-each / multi-instance provided by abap platform colleagues. You can choose to get a workitem for each recipient. If you have for example 1 costcenter owner as agent you get 1 workitems, if you have 2 you get 2, etc..Screenshot configuration in Manage WorkflowsScreenshot configuration in Manage Workflows

Task Exception Handling

For those tasks we are also offering the mapping of actions to a task outcome in the exception handling section of the task definition. The below table lists all possible actions and how the flexible workflow engine executes those actions.

Action ResultRuntime Behavior
Repeat StepIf one of the instances of a multi-instance task has an outcome, which is mapped to this action result, all other task instances created out of this multi-instance task will be canceled (if not already processed successfully).
The complete multi-instance task will be inserted behind this task definition and might result in multiple task instances again. 
CancelIf one of the multi-instance tasks has an outcome, which is mapped to this action result, all other task instances created for this multi-instance task definition will be canceled (if not already processed successfully)
and then the workflow gets canceled as well.
Restart workflowIf one of the multi-instance tasks has an outcome, which is mapped to this action result, all other task instances created for this multi-instance task definition will be canceled (if not already processed successfully)
and then the workflow starts from the beginning. All already executed tasks will still exist in the history of the workflow instance.
ContinueThe workflow instance waits until all instances of this multi-instance task got completed and then continues the workflow instance with the next task. It can be seen as a parallel split and join.

The required action is a task, which gets inserted in-between the last executed task instance and the future tasks (which might get adjusted based on the action result).

We offer an API to add task instance during the execution of a workflow instance. Either when a workflow or step callback is called on your runtime callback class or via our flexible workflow APIs.

Step callback
"inserts a task after the current activity/task
DATA(lo_factory) = io_activity->if_swf_flex_ifs_run_component~get_factory( ).
DATA(lo_activity) = lo_factory->create_activity( iv_step_id = '<stepId>' iv_agent_rule_id = '<agent_rule>' iv_is_multi_instance = abap_true ).
io_activity->if_swf_flex_ifs_run_component~insert_component_after( lo_activity ).
Flexible Workflow API
CLASS lcl_wf_modifier DEFINITION FINAL.
  PUBLIC SECTION.
    INTERFACES if_swf_flex_ifs_inst_modifier.
ENDCLASS.
 
CLASS lcl_wf_modifier IMPLEMENTATION.
 
  METHOD if_swf_flex_ifs_inst_modifier~execute.
    DATA(lt_activity) = io_workflow->get_all_activities( ).
    READ TABLE lt_activity INTO DATA(lo_first_activity) INDEX 1.
    DATA(lo_factory) = lo_first_activity->if_swf_flex_ifs_run_component~get_factory( ).
    DATA(lo_new_activity) = lo_factory->create_activity(
        iv_step_id = 'approveLeaveRequest'
        iv_is_multi_instance = abap_true
    ).
    lo_new_activity->assign_to(
        it_principal = VALUE #( ( objid = 'MILLER01' otype = swfco_org_user )
                                ( objid = 'MILLER02' otype = swfco_org_user ) )
    ).
    lo_first_activity->if_swf_flex_ifs_run_component~insert_component_after( lo_new_activity ).
  ENDMETHOD.
 
ENDCLASS.
 
[...]
 
 
DATA(lo_factory) = io_activity->if_swf_flex_ifs_run_component~get_factory( ).
DATA(lo_runtime_handler) = lo_factory->get_runtime_handler( ).
       
DATA(lo_modifier) = NEW lcl_wf_modifier( ).
lo_runtime_handler->modify_workflow_instance(
    iv_leading_object = <my_leading_object>
    io_modifier = lo_modifier
).

There is also a limited possibility to have more configurable parallel processing with the feature "run with previous". This needs to be activated on scenario level. Would need to explore if it is actual possible to add steps dynamically via coding with this feature. Could also be relevant which release you work on. 

Screenshot 2024-06-10 142708.jpg

For a generic recommendation what to do with existing workflows in S4 please see here.

SumitKundu
Active Participant
0 Kudos
Thanks for the detailed response. I have tried multiple task instances using 'All of the recipients' needs to complete the instance. But each workitem needs to have different cost center and amounts if possible. Is there any way in step callback to update each instance task container with such values as individual cost center?
SBach
Product and Topic Expert
Product and Topic Expert
0 Kudos
There are 3 callback methods in the runtime controll class for steps: IF_SWF_FLEX_IFS_RUN_APPL_STEP~BEFORE_CREATION_CALLBACK IF_SWF_FLEX_IFS_RUN_APPL_STEP~ON_CREATION_CALLBACK IF_SWF_FLEX_IFS_RUN_APPL_STEP~AFTER_COMPLETION_CALLBACK
SumitKundu
Active Participant
0 Kudos
I have used IF_SWF_FLEX_IFS_RUN_APPL_STEP~ON_CREATION_CALLBACK to see if I can update task container or not. But can't find a suitable api to put different values to task container.
SumitKundu
Active Participant
0 Kudos

Hi @SBach 

Actually the task container I get from io_context has 2 instances for 2 approvers selected at flexible workflow. These instances are private and can't access them, also the workitem id seems not committed to DB yet (checked in table SWWWIHEAD when debugging the callback method).

Please check below screenshots from debugging:

lo_container.jpglo_container1.jpgtask instance 2.jpgtask instance1.jpg

I am not sure how to access the multiline parameter container MT_PARAMETER_CONTAINER. It is a private attribute of a local class of CL_SWF_FLEX_ENGINE. 

Best regards,

Sumit

SumitKundu
Active Participant
0 Kudos

Hi @SBach ,

I have used below code in IF_SWF_FLEX_IFS_RUN_APPL_STEP~ON_CREATION_CALLBACK.

 

data itab TYPE STANDARD TABLE OF gnetw.
    APPEND 30 to itab.
    APPEND 40 to itab.
    DATA(lo_container) = io_context->get_task_container( ).
    lo_container->set( EXPORTING name = 'Amount'
                         value = itab ).

 

 But the task container in the workflow log shows same value in both the runtime instances of the task.

 

p619793_1-1718108717925.png

p619793_3-1718108798982.png

Also I used the amount expression in the workitem text, however it did not update the workitem text as seen in the workflow log screenshot above.

Best regards,

Sumit

 

 

 

SBach
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hi Sumit, unfortunately I never did a POC on that and actual there is no time for. Also I guess there is a need to adapt the task ui. Regarding the callback. I found some example coding (but didn´t run it) in our FORMABSENC- Demo Flexworkflow Scenario for the on_creation_callback that may help.

 

 

 

 

 

DATA(lv_task_id) = io_current_activity->get_task_id( ). 
IF lv_task_id = 'TS77408433'. 
" The application object could be fetched 
" DATA(lv_appl_obj) = io_context->get_leading_object_reference( ). 
" The following code will set an approval comment depending on the value of a step property 

DATA(lt_step_properties) = io_current_activity->if_swf_flex_ifs_run_component~get_properties( ). 
IF line_exists( lt_step_properties[ id = cl_swf_flex_def_appl_lra=>cs_prop_milestone-id ] ). 
DATA(lo_formabsenc) = CAST cl_swf_formabsenc( cl_swf_formabsenc=>bi_persistent~find_by_lpor( CONV #( io_context->get_leading_object_reference( ) ) ) ). 
IF lo_formabsenc IS NOT BOUND. RAISE EXCEPTION TYPE cx_swf_flex_ifs_run_exception EXPORTING textid = cx_swf_flex_ifs_run_exception=>leading_object_key_error. 
ENDIF. 
DATA(ls_formabsenc) = lo_formabsenc->get_detail( ). 
CASE lt_step_properties[ id = cl_swf_flex_def_appl_lra=>cs_prop_milestone-id ]-value. "value field contains a key (if a fixed value list is used) or the value itself (if no value list is used) WHEN cl_swf_flex_def_appl_lra=>cs_prop_milestone-value_id_1. ls_formabsenc-approval_comment = 'Stage 1 reached' ##NO_TEXT. WHEN cl_swf_flex_def_appl_lra=>cs_prop_milestone-value_id_2. ls_formabsenc-approval_comment = 'Stage 2 reached' ##NO_TEXT. ENDCASE. 
" Update leading object here 
TRY.
lo_formabsenc->update_background( 
EXPORTING
 is_swxformabs = ls_formabsenc iv_do_commit = abap_false ). 
CATCH cx_bo_error INTO DATA(lx_bo_error). 
RAISE EXCEPTION TYPE cx_swf_flex_ifs_run_exception EXPORTING previous = lx_bo_error. ENDTRY. ENDIF. 
TRY. 
io_context->get_task_container( )->set( 
EXPORTING 
name = 'dummy' 
value = 'A' 
). 
CATCH cx_swf_cnt_cont_access_denied cx_swf_cnt_elem_access_denied cx_swf_cnt_elem_not_found cx_swf_cnt_elem_type_conflict cx_swf_cnt_unit_type_conflict cx_swf_cnt_elem_def_invalid cx_swf_cnt_container INTO DATA(lx_container_write). 
RAISE EXCEPTION TYPE cx_swf_flex_ifs_run_exception EXPORTING previous = lx_container_write. 
ENDTRY.

** --- Simulate MMPUR_WFL_CONTEXT_ENHANCE BAdI to set step priority ----
*    DATA(lt_execution_list) = io_current_activity->get_execution_results( ).
*    DATA(lt_work_items_id) = VALUE swwtwiid( FOR line IN lt_execution_list ( line-wi_id ) ).
*    APPEND io_context->get_wi_id( ) TO lt_work_items_id . "Main workitem priority also needs to be set
*    LOOP AT lt_work_items_id INTO DATA(ls_work_item_id).
*      CALL FUNCTION 'SWW_WI_PRIORITY_CHANGE'
*        EXPORTING
*          priority  = 2
*          wi_id     = ls_work_item_id
*          do_commit = ' '
**       AUTHORIZATION_CHECKED       = ' '
**       PRECONDITIONS_CHECKED       = ' '
**       PROPAGATE_TO_FLOW           = ' '
**       FUNCNAME                    = ' '
*        EXCEPTIONS
*          OTHERS    = 0.
*    ENDLOOP.

 

 

 

 

 

SBach
Product and Topic Expert
Product and Topic Expert
0 Kudos

What also sometimes helps in custom workflow dev is the Programming Exit on activity Level.

*How do the program exits work?
*To change alternatives of the user decision at runtime dynamically (for each *work item), you can define an ABAP class here that supports interface *IF_SWF_IFS_WORKITEM_EXIT.

*At runtime, method IF_SWF_IFS_WORKITEM_EXIT~EVENT_RAISED is called multiple *times. Here you can define source code that is run for different time points *of the work item processing.

*To delete or change the alternatives, your code must be run at the time *point SWFCO_EVENT_BEFORE_DECISION.

*Example: Class CL_SWH_T_DECISION_EXIT from workflow definition WS77400143 "WF_Verify141"

Method IF_SWF_IFS_WORKITEM_EXIT~EVENT_RAISED.
  data lt_decialts type IF_WAPI_WORKITEM_CONTEXT=>swrtdecialts.
  if im_event_name = IF_SWF_IFS_DECISION_EXIT~C_EVTTYP_BEFORE_DECISION.
    call method IM_WORKITEM_CONTEXT->get_decision_alts
       importing et_decialts = lt_decialts.
*   --- hide alternative 0001 and 0002 ---
    delete lt_decialts where altkey <> '0003'.
    call method IM_WORKITEM_CONTEXT->set_decision_alts
       exporting it_decialts = lt_decialts.
  endif.
endmethod.