Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
mateuszadamus
Active Contributor

Introduction


Hi and welcome to my next SAP Community blog post.

Recently I had an opportunity to work on SAP Flexible Workflow for Purchase Order approvals.

Flexible Workflow is a Fiori integrated solution for creation of document approval processes. Its main strength, ease of use, comes from little or zero need for programming interference. Basic scenarios can be created using configuration only. If you have a more complex situation, you can use one of the available BAdIs – for example the MMPUR_WORKFLOW_AGENTS_V2 BAdI, which allows for customer specific approval step agent determination logic.

Approval Step Agent Determination Options

Options for approval step agent determination

This blog post first appeared on the Int4 blog.

The issue of missing approval level


At the first glance, the BAdI seems like a perfect solution for implementation of a complex agent determination logic. It consists of a single method called GET_APPROVERS, which is populated with basic information about the document, for which the scenario was triggered.

Agent Determination BADI Parameters

GET_APPROVERS method parameters


  • As you can see, there is the BusinessObject parameter, which will tell you whether it’s a Purchase Order, Purchase Requisition or other document.

  • There are document and item numbers in the PurchasingDocument and PurchasingDocumentItem parameters respectively.

  • There is the WorkflowScenario, which will provide you with the ID of the scenario, for which the determination has been triggered.



 

More information about the BAdI and its parameters can be found in the SAP OSS note #2646400. In the same note you can also read that the information about the current step (level) is available in the BAdI from the 2008 release only and only for Purchase Requisition and Central Purchase Requisition documents.

Why is the level information so important?


To be able to correctly determine the agents for the approval step, you need to know for which level the BAdI was executed. In a static scenario, where all approval steps are triggered, you can achieve this by simply counting the previous approvers, contained in the PreviousApproverList parameter. However, in case of two or more non-obligatory approval steps, or an obligatory step after a non-obligatory one, the PreviousApproverList count will give incorrect results. Consider the example as shown in the image below:

Flexible Workflow Scenario Comparison

Comparison of two Flexible Workflow scenarios

Scenario B shows that the level calculated based on PreviousApproverList will be untrue (3 instead of 4). This may cause assignment of an incorrect agent and faulty workflow processing.

Approval level calculation


Fortunately, you can calculate the current approval level with the information that is available in the system during the BAdI execution.

This information consists of two parts:


  • XML with the details of Flexible Workflow and its flow so far – it contains information about the steps (ACTIVITY) that have been processed (COMPLETED) or skipped (SKIPPED) before the BAdI execution.



Flexible Workflow Process Flow XML

Flexible Workflow process flow XML


  • Workflow log – it contains information about the steps that were skipped just before the BAdI execution.



Skipped Steps in Workflow Log

Information about skipped steps in Workflow log

You can use PurchasingDocument and WorkflowScenario parameters to obtain the first part. Method GET_WORKFLOW_INSTANCES of class CL_SWF_FLEX_DEF_FACTORY will return a list of Flexible Workflow instances executed for the document. The last instance executed (the one with the highest WORKFLOW_ID) is what you should be looking for. You then need to parse its XML and count the steps.

The second part is more tricky. Theoretically, there is the standard function module SWW_WI_LOG_GET_BUFFER that you can use to read the Workflow log. However, this function removes messages of the same type that were triggered in the same second, which makes it useless in this case.

Workflow Log Buffer Function

Function to read Workflow log buffer

To avoid this, you can use a FIELD-SYMBOL and direct Workflow log buffer assignment, which will give you access to all messages stored in the buffer.

Below you can find the whole logic with the method interface:

Step Count Method Interface

The parameters of the approval level calculation method


METHOD calculate_current_level.
DATA:
lv_xml_steps TYPE i,
lv_log_steps TYPE i,
lv_scenario_id TYPE swd_wfd_id,
lv_object_id TYPE sibfboriid,
ls_message TYPE swf_t100ms.

FIELD-SYMBOLS:
<lt_log_buffer> TYPE swlloghist_t.

rv_step = 0.

lv_object_id = iv_purchase_order.
lv_scenario_id = iv_workflow_scenario.

DATA(lo_wf_inst) = cl_swf_flex_def_factory=>wf_inst_handler( ).
DATA(lt_instances) = lo_wf_inst->get_workflow_instances(
EXPORTING
iv_scenario_id = lv_scenario_id
iv_appl_obj_id = lv_object_id
iv_is_draft = abap_false
iv_context = || ).

SORT lt_instances BY workflow_id DESCENDING.
READ TABLE lt_instances REFERENCE INTO DATA(ld_instance) INDEX 1.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_swf_flex_ifs_exception.
ENDIF.

DATA(lo_ixml) = cl_ixml=>create( ).
DATA(lo_stream_factory) = lo_ixml->create_stream_factory( ).
DATA(lo_document) = lo_ixml->create_document( ).

" parse xml-data into dom
IF lo_ixml->create_parser(
document = lo_document
stream_factory = lo_stream_factory
istream = lo_stream_factory->create_istream_xstring( string = ld_instance->xmlresource ) )->parse( ) <> 0.

RAISE EXCEPTION TYPE cx_swf_flex_ifs_exception.
ENDIF.

" iterate dom and count steps
DATA(lo_process_flows_itr) = lo_document->create_iterator( ).
lo_process_flows_itr->set_filter( lo_document->create_filter_name_ns( name = 'processFlow' ) ).

DATA(lo_process_flow) = lo_process_flows_itr->get_next( ).
WHILE lo_process_flow IS BOUND.
DATA(lo_activities_itr) = lo_process_flow->create_iterator( ).
lo_activities_itr->set_filter( lo_document->create_filter_name_ns( name = 'activity' ) ).

DATA(lo_activity) = lo_activities_itr->get_next( ).
WHILE lo_activity IS BOUND.
DATA(lo_attributes) = lo_activity->get_attributes( ).
DATA(lo_runtime_status) = lo_attributes->get_named_item_ns( name = 'runtimeStatus' ).
DATA(lv_runtime_status) = lo_runtime_status->get_value( ).

CASE lv_runtime_status.
WHEN 'COMPLETED' OR 'SKIPPED'.
lv_xml_steps = lv_xml_steps + 1.
WHEN 'CANCELLED'. " Edit 2023-08-11
lv_xml_steps = 0. " If step was cancelled, then a new WF was created, steps should be counted from the start
WHEN OTHERS.
" do nothing
ENDCASE.

lo_activity = lo_activities_itr->get_next( ).
ENDWHILE.

lo_process_flow = lo_process_flows_itr->get_next( ).
ENDWHILE.

" get message from WF log's buffer
ASSIGN ('(SAPLSWWL)LOG_BUFFER[]') TO <lt_log_buffer>.
IF sy-subrc = 0.

" count how many steps have been skipped
" because of preconditions
LOOP AT <lt_log_buffer> TRANSPORTING NO FIELDS
WHERE wi_id = ld_instance->workflow_id
AND meth_user = sy-uname
AND workarea = 'SWF_FLEX_RUN'
AND message = '005'.

lv_log_steps = lv_log_steps + 1.
ENDLOOP.
ENDIF.

rv_step = lv_xml_steps + lv_log_steps.

" There were &1 (&2/&3) steps before Agent Determination BAdI
MESSAGE s007(ym) WITH rv_step lv_xml_steps lv_log_steps INTO DATA(lv_message).
MOVE-CORRESPONDING sy TO ls_message.

CALL FUNCTION 'SWW_WI_LOG_WRITE_SUCCESS'
EXPORTING
do_commit = abap_false
fb_name = 'Agent Determination BAdI'
wi_id = ld_instance->workflow_id
log_message = ls_message.

" add 1 for current step
rv_step = rv_step + 1.
ENDMETHOD.



Summary


Even though the current approval level isn’t always necessary for agent determination, there are cases where the determination would be flawed without it. Fortunately, it is possible to calculate it with the use of the Workflow log and its processing history.

Edit 2023-08-11


I've noticed that it's possible to have more than one WF execution assigned to a single PO. This happens when a WF is cancelled before it can be finished, e.g.: the PO is changed when it is in the process of approval.

In this case the WF instance is divided into so called blocks, where each of the blocks is a separate execution of the WF. Only the last execution is active, but the information about activities of the previous executions is contained in the XML and influences the step counter.

Hence the additional check on LV_RUNTIME_STATUS = CANCELLED, which clears the step counter just before activities from the next WF block are processed.
21 Comments
Hi Mateusz Adamus,

 

Thanks for sharing..

my question is where the method "Calculate_current_level" called? and where should we place this method? is it on the BAdI MMPUR_WORKFLOW_AGENTS_V2 implementation class?

 

Regards
mateuszadamus
Active Contributor
0 Kudos
Hi fabednego

Yes. The CALCULATE_CURRENT_LEVEL method should be called from MMPUR_WORKFLOW_AGENTS_V2 GET_APPROVERS method.

 

Kind regards,

Mateusz
Hi mateuszadamus,

 

Thanks for sharing. I can get insight for my current cases in workflow and I hope SAP will provide step info in PO Workflow as well as in PR Flexible workflow.

However, I wonder where can I view the Flexible Workflow process flow XML? could you please advise tcode to view that?

Regards,

Fanuel
mateuszadamus
Active Contributor
0 Kudos
Hi Fanuel,

 

I do not know the transaction. What I did is debugged the code and checked the XML in the debugger.

 

Kind regards,

Mateusz
0 Kudos
Hello Mateusz,

 

I have implemented the PR flexible workflow. Rejection at every step will initiate  the workflow from the beginning ( just like restart of PR approval when a valid change is made).

I have implemented your logic to calculate the step but however it is not considering rejection . Step count is incremented even when rejection occurs.

 

Can you please let me know if we can identify the rejection step and reset the counter ?

Also please tell me how you debugged he code for xml.

 

Thanks in advance

SK
mateuszadamus
Active Contributor
0 Kudos
Hi Subhashini,

Please keep in mind that my logic was for Flexible Workflow for PO. PR is probably similar, but might have some differences.

I debugged the XML by first getting a WF instance and then analyzing the WF XML contained in the ld_instance->xmlresource variable.

As to the identification of the rejection step, in the XML of a PO WF each step has information about the result. I've marked it with a red line in the image below. You should look for something similar in the XML of you PR WF and act on it.


Kind regards,

Mateusz
0 Kudos
Thank Mateusz,

 

Debugging solution is very useful.

PR Workflow also have same attributes as PO workflow for outcome

However , during runtime ( after rejection in Fiori Inbox) , attribute outcomeID is not filled.

But if I have debug the code ( in dummy se38 program for the same workflow instance) , the outcomeID is filled.

 

Im not sure if im missing any  preconfigure the fills the outcomeid data? Please advice.

Below is xml data.

mateuszadamus
Active Contributor
0 Kudos
Hi Subhashini,

I can see the "outcomeId" node with a "REJECTED" value in the XML. Why do you say it's missing?

Another thing, why do you need the BAdI to execute, if an approver rejects a PR? Shouldn't the PR creator make changes to the PR and submit it for an approval again?

 

Kind regards,

Mateusz
0 Kudos
Hi Mateusz,

I can see the "outcomeId" node with a "REJECTED" value in the XML. Why do you say it's missing?

Attribute "outcomeid" is available in the xml , but value is empty during the runtime process (  approver clicks Reject button from the Fiori inbox). Workflow should  restart from the beginning.

WE can see the value in the  my dummy program when i ran the program later  ( after runtime process is complete) .

 

Another thing, why do you need the BAdI to execute, if an approver rejects a PR? Shouldn't the PR creator make changes to the PR and submit it for an approval again?

PR flexible workflow have limitation. After PR is rejected, the status on the PR changes to "Release Refused" , as workflow is completed. In this status ,PR cannot be edited for resubmission.

In order to avoid this , I have to restart the workflow at every step when PR is rejected. However BAdi agent determination is not considering the workflow restart from step 1. Its incrementing the step count and agents are determined wrongly.

Thanks,

Subha

 
0 Kudos
Another question , where should the method "Calculate_current_level"  be implemented ? which class?

I implemented in same Badi as Agent assignment and called inside the method "GET_APPROVERS".


Is that correct?

mateuszadamus
Active Contributor
0 Kudos
Hi Subhashini,

I do not know why the value is empty when rejected. Is it also empty when user Approves the PR and the approval process is moved to the next step? Maybe the rejection does not update the XML because the whole approval process is finished.

Yes, the CALCULATE_CURRENT_LEVEL should be executed from the GET_APPROVERS BAdI. You're doing it correctly.

 

Kind regards,

Mateusz
0 Kudos
Hi Mateusz,

 

Yes , its strange ,even Approve status ( "RELEASED") is not showing in "outcomeId"  during runtime.

However again , the value is available when I  run my Z program separately .

 

I dont understand if "runtimeStatus" is updating , why its not updating "outcomeid" during runtime.

Wondering if there is any preconfiguration need to be completed.

 

Thanks,

Subhashini

 

 
mateuszadamus
Active Contributor
0 Kudos
Hi,

I cannot answer that. Would have to go through your whole customizing.

But have you done all the necessary steps from the SAP guidelines?

https://help.sap.com/viewer/af9ef57f504840d2b81be8667206d485/2021.000/en-US/91ab576eaf58475f8934d2dc...

 

Kind regards,

Mateusz
marcpara
Explorer

Hi mateuszadamus,

Thanks for the blog, it's interesting.

How do you do to debug the flexible workflow and to go through the BADI MMPUR_WORKFLOW_AGENTS_V2 ? I would like to use this BADI for the purchase request workflow.

Thanks.

Marc

mateuszadamus
Active Contributor
0 Kudos
Thank you marcpara

As to the debug, I just set an External Breakpoint in the implementation of the BAdI and it stops when the BAdI is called.

Kind regards,

Mateusz
Hello mateuszadamus

This is an excellent blog .

However , we have implemeted it but I figured out that there is a limitation .

 

Limitation is when the workflow is retrigged after a change of the PR request ..in This case we cannot calculate anymore exactly the current level .

 

Any hint ?
mateuszadamus
Active Contributor
0 Kudos
Hi lamiafarhat

Thanks, glad you like it.

What is the issue when retriggering?
If I remember correctly, in my tests (for Purchase Order though) the number of steps after retriggering was calculated from the beginning. Were you able to debug the BAdI and check the WF XML after retriggering?

Kind regards,
Mateusz
0 Kudos
Hello mateuszadamus

For PR it seems that the number of steps is not re-set after re-trigger of the workflow ..

I will check the XML and see 🙂

Kind Regards

Lamia
mateuszadamus
Active Contributor
0 Kudos
Hi lamiafarhat

In this case I would analyze the XML that you get from PR workflow instance and check if steps from current and previous execution can be distinguish. But I suppose you're doing that already. 🙂

Kind regards,
Mateusz
Clucking
Participant
Hi Mateusz Adamus,

I know this is an old one - but you just saved my week...

Thanks for taking your time when writing this excellent blog!

I work with scenario for supplier invoice approval using flexible workflow in a 2021 version and the step info will first be available in 2022.

Best regards,

Claus.
mateuszadamus
Active Contributor
Hi claus.lcking

Glad it helped you.

It's a pity that such a vital information is still not available in the BAdI.

Kind regards,
Mateusz
Labels in this area