2024 Jun 08 11:39 AM - edited 2024 Jun 08 11:40 AM
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
Request clarification before answering.
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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 Workflows
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.
Repeat Step | If 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. |
Cancel | If 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 workflow | If 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. |
Continue | The 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.
"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 ). |
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.
For a generic recommendation what to do with existing workflows in S4 please see here.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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:
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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.
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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.
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.
User | Count |
---|---|
96 | |
11 | |
9 | |
9 | |
7 | |
5 | |
4 | |
4 | |
4 | |
4 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.