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: 
rrajgor
Participant
There are many standard workflow scenarios available but some business requirements can not be satisfied by standard workflows. So it is important to develop something from scratch as per the business requirements. This blog post is mainly focus on Sales Order Approval process. In most of the case, there is only one Manager/Approver who is going to approve the SO. This can be manged by the SD Functional by some configuration and with same configuration you can also manage multi-level Manager/Approver too. But what if the business requirement is as bellow,

  • While creating or changing SO, a workflow should be triggered to a Manager/Approver.

  • There are 4 Level of Manager/Approver who is going to approve SO one by one.

  • Sales Order is considered to be approved if the Final Approver level has approved it.

    • Final Approver can be Level 1 , Level 2 , Level 3 or Level 4 based on the SO amount fit to the range provided to each level.

    • i.e. Level 1 ( 0 to 1,000 )

    • i.e. Level 2 ( 1,000 to 10,000 )

    • i.e. Level 3 ( 10,000 to 1,00,000 )

    • i.e. Level 4 ( 1,00,000 and above )

    • Now if SO amount is 65,000, Level 3 is the Final Approver.



  • If Sales Order is in Credit Block, workflow should be triggered to a Manager/Approver to release it from Credit Block.


 

Before starting step by step implementation, let us see the basic prerequisites.

Custom Table: In SE11 -> ZSO_APPROVAL( Screenshot - 1 )



Table Maintenance : ZSO_APPROVAL. I have changed the screen, Utility->Table Maintenance Generator->Environment->Modification->Maintenance Screen.

( Screenshot - 2 )



Here, it is not necessary to maintain 4 entries. If there is only one entry like, MinValue = 1.00 and Max_Value BLANK(Means anything) than Level 1 will be the Final Approver. In case there is no entry found in this custom table for a particular sales order, so that sales order will be skipped from this Level-Approval Process and treated as per SAP Standard flow.

There is no Level-Approver to manage Credit Block. Only one approver is going to release sales order from Credit Block based on sales order amount.

Class : Create a Class in SE24, ZSO_APPROVAL_EXIT with event 'TRIGGER'. This class also have some interfaces,methods and attributes that will be covered after this workflow creation steps.

( Screenshot - 3 )



Function Module : Create a Function Module in SE37 as bellow. There is no importing or exporting or changing parameters.

( Screenshot - 4 )


Implementation Steps:


Step 1: Create workflow and register Events + Binding.

Goto SWDD and create new workflow. After 'Abbreviations' and 'Description' in 'Basic Data' tab, you can register events in 'Start Event' tab. Here, we have two events of standard BO "BUS2032" and one custom event of class "ZSO_APPROVAL_EXIT".

( Screenshot - 5 )



Create following workflow container elements before binding them to the events.

  • Workflow Container.

    • ITAB stores HTML Content

    • ISO  stores VBELN

    • IUSER stores userid while workflow triggered for Credit Release.(VKM1)

    • BUS2032 is BO which will be used for Credit Release.(VKM1)




( Screenshot - 6 )



Binding will be same for "CREATED" and "CHANGED" events. Required container element will receive some value whenever a sales order is created or changed.

( Screenshot - 7 )



Binding for "TRIGGER" event of class ZSO_APPROVAL_EXIT. This event will be triggered manually if SO is in Credit Block in FORM USEREXIT_SAVE_DOCUMENT of MV45AFZZ UserExit or from some BADI.

( Screenshot - 8 )



  • Note :  &EVENT_NAME& will be used to identify the triggered workflow, i.g. workflow is triggered by VA01(Created) or VA02(Changed) or VKM1(Trigger).


Step 2: Common task that will generate SO Details with List of Materials in HTML format.

Bellow is an sample output of this task that generate HTML content filled with required Sales Order details including list of materials.

( Screenshot - 9 )



Create an activity with custom task. ( Screenshot - 10 )



Fill required field and select Object Category "ABAP Class" and in Object Type "ZSO_APPROVAL_EXIT" with Method "MATERIALS". So each time this method get called whenever any of the workflow triggered. Dont forget to make this task as "BACKGROUND PROCESSING".

( Screenshot - 11 )



In Container tab declare ITAB with typed ZSOLISTI1_TT( which is Table Type of SOLISTI1 ). So whole HTML content will be stored in this Container Element and this ITAB is going to placed in UserDecision Task for both workflow.

( Screenshot - 12 )



It is declared with TableType so Multiline is by default checked but in Parameter Settings check Export checkbox. Because we are receiving HTML in it.

( Screenshot - 13 )



Binding for this Task is simple. Just you have to pass &ISO& to MATERIALS method and it will return &ITAB&. ( ISO is Sales Order number )

( Screenshot - 14 )



Step 3: Condition to identify workflow. (Condition 000008)

Create a condition step to identify which workflow should trigger. If event_name is "CREATED" or "CHANGED" it will trigger Sales Order Approval workflow and if event_name is "TRIGGER" it will trigger workflow to release Sales Order from Credit Block.

( Screenshot - 15 )



Step 4: Let us assume EVENT_NAME is 'CREATED' or 'CHANGED'.  That means workflow triggered from VA01 or VA02. ( User Decision 000041 )

Create User Decision step with "Approve" and "Reject" buttons. Pass &ISO& in parameter 1 so it can be used in Title " Approval for Sales Order : &1" . &1 is our Sales Order Number. Here agent assignment is dynamic. You can skip this part for now.

( Screenshot - 16 )



In Control tab, create new task and do not forget to make "General Task" in Task Properties.

( Screenshot - 17 )



Assign ZSO_APPROVAL_EXIT in Program Exits tab. So after taking decision, it will call that class and here we can perform operation based on the decision.

( Screenshot - 18 )



Double click on Task "TS90000046" in control tab. Fill all fields as bellow.

( Screenshot - 19 )



In container tab, create an element with "MLIST" with same type as we created "ITAB" . Here the lines of ITAB will be stored in "MLIST".

( Screenshot - 20 )



Now simply declare "&MLIST[]&" in Description tab. Whole HTML content will be displayed in User Decision BODY.

( Screenshot - 21 )



Binding for Task "TS90000046" , assign ITAB to MLIST. ( Screenshot - 22)



Goto decision tab, create new rule and double click it. Fill required field and in Rule Definition select "Agent Determination: Function to be Executed" and Function Module "ZRULE_FM". (Need to create ZRULE_FM first before activating this agent assignment). Do not forget to check "Terminate If Rule Resolution Without Result.

So if there is no agent received for a particular SO, it will not trigger / dump workflow.

( Screenshot - 23 )



In container of this Rule, create element with type VBELN and make it Import. ( Screenshot - 24)



Binding is again simple for this, just pass ISO. ( ISO is sales order number ) ( Screenshot - 25 )



As we did binding above for the Rule, there is an element ISO passed to SO_N. This SO_N will be received in ZRULE_FM  in wa_container-value. Based on that VBELN we can get agent from our Custom Table. Append that userid retrieved from ZSO_APPROVAL to actor_tab.

( Code - 1 )
  DATA : wa_actor     TYPE swhactor,
wa_container TYPE swcont.
DATA : lv_lines TYPE i.

READ TABLE ac_container INTO wa_container.
IF wa_container-value IS NOT INITIAL.

*** Sales Order Number(VBELN) will be received in [wa_container-value]
*** Based on that you can find Sales Order Amount and than pass it to ZSO_APPROVAL table
*** So you will get list of approvers.

wa_actor-otype = 'US'.
wa_actor-objid = wa_soapp-userid.
APPEND wa_actor TO actor_tab.

ENDIF.

DELETE ADJACENT DUPLICATES FROM actor_tab COMPARING objid.
DESCRIBE TABLE actor_tab LINES lv_lines.
IF lv_lines EQ 0.
RAISE nobody_found.
ENDIF.

 

Step 5: Finally, Sales Order Approval acknowledgement will be sent to the workflow initiator.

This email will be received by a workflow initiator with decision taken by the approver and sales order details.

( Screenshot - 26 )



Create Send Mail step to send mail to the initiator. Here BUS2032 container element have enough data regarding particular Sales Order. So you can use it as bellow.

( Screenshot - 27 )



Step 6: Let us assume EVENT_NAME is 'TRIGGER' . This workflow trigger when SO is in Credit Block. So associated approver can release it without going into VKM1.

Create Activity and create a task to Instantiate BUS2032 BO by using sales order number.This step is necessary because to get the complete details of a particular SO .

( Screenshot - 28 )



Double-click on task. Fill required field and do not forget to check Background Processing.

( Screenshot - 29 )



When you perform above step, it will auto generate Object Key, Object type, and Object instance.

( Screenshot - 30 )



Pass ISO to Objectkey and BUS2032 ( as  it is - string) to Objecttype. So it will instantiate BO implicitly and return it to &BUS2032& Container Element.

( Screenshot - 31 )



Step 7: User Decision for Credit Release.

Create User Decision with "Release" and "Block" buttons and pass Parameter1 with &ISO&. When we trigger the event manually from UserExit by calling ZSO_APPROVAL_EXIT-SET_PARAM method with &ISO& and &IUSER&.

( Screenshot - 32 )



Create new task "TS****"  and do not forget to make "General Task" in Task Properties.

( Screenshot - 33 )



Assign ZSO_APPROVAL_EXIT in Program Exits tab. So after taking decision, it will call that class and here we can perform operation based on the decision.

( Screenshot - 34 )



Binding is simple, pass &ITAB& to &MLIST& so HTML content can be displayed on Decision Body. Avoid Extended and Decision_note, i forgot to remove this auto generated entries.

( Screenshot - 35 )



Step 8: Sales Order Credit Block Acknowledgement sent to the workflow initiator.

Create Send Mail step to send mail to the initiator. Here BUS2032 container element have enough data regarding particular Sales Order. So you can use it as bellow.

( Screenshot - 36 )



 

Reference Code.

Class ZSO_APPROVAL_EXIT - Interfaces, following interfaces must be implemented.

( Screenshot - 37 )



Class ZSO_APPROVAL_EXIT - Methods, there are only 3 methods used in this blog.

  1. IF_SWF_IFS_WORKITEM_EXIT~EVENT_RAISED

    • This method is used to handle Decision Taken by the Approver.



  2. SET_PARAMS

    • This method is used to trigger event when so is in Credit Block.



  3. MATERIALS.

    • This method is used to get Sales Order details and List of materials.




( Screenshot - 38 )



Event_Raised - Method ( Code - 2 )
IF im_event_name EQ 'AFT_EXEC'.
CHECK ls_wihead-wi_stat NE swfco_wi_status_error
AND ls_wihead-wi_stat NE swfco_wi_status_excpcaught
AND ls_wihead-wi_stat NE swfco_wi_status_excphandlr.

l_wf_cont = im_workitem_context->get_wf_container( ).
l_wi_cont = im_workitem_context->get_wi_container( ).

CALL METHOD l_wf_cont->get
EXPORTING
name = 'EVENT_NAME'
IMPORTING
value = _wf.

CALL METHOD l_wi_cont->get
EXPORTING
name = '_WI_RESULT'
IMPORTING
value = _res.

***IF A Persion has clicked on Approve button, _res = 0001. and _wf will be received from Workflow.
IF _res EQ '0001' AND ( _wf EQ 'CREATED' OR _wf EQ 'CHANGED' ).

***Code to change SO status in JEST standard table.
***You can use 'I_CHANGE_STATUS' FM
***After getting approved by the approver, it will trigger workflow to next approver level as maitained in ZSO_APPROVAL Table.
***Manually Trigger workflow to next level approver.
***
***
WAIT UP TO 1 SECONDS.
CALL FUNCTION 'SAP_WAPI_CREATE_EVENT'
EXPORTING
object_type = object_type "BUS2032
object_key = object_key "SALES ORDER NUMBER
event = event. "CHANGED


***
***
ELSEIF _res EQ '0001' AND _wf EQ 'TRIGGER'.

CALL METHOD l_wf_cont->get
EXPORTING
name = 'ISO'
IMPORTING
value = so_n.


CALL FUNCTION 'SD_ORDER_CREDIT_RELEASE'
EXPORTING
vbeln = so_n.

***
ENDIF.

Materials - Method - to generate HTML Content. ( Code - 3 )
*** ITAB and ISO are imporing parameters.
*** By using ISO that is SO Number you can get details of Materials
*** Append each HTML Line to ITAB [ ITAB TYPE ZSOLISTI1_TT ] Create TableType of SOLISTI1.
DATA : wa_contents TYPE solisti1.

wa_contents-line = |<html><head><style>|.APPEND wa_contents TO itab.
wa_contents-line = '.t1 {font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;border-collapse: collapse;width: 80%;}'.APPEND wa_contents TO itab.
wa_contents-line = '.t1 td, .t1 th {border: 1px solid #000;padding: 2px;}'.APPEND wa_contents TO itab.
wa_contents-line = '.t2{font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;border-collapse: collapse;width: 100%;}'.APPEND wa_contents TO itab.
wa_contents-line = '.t2 td, .t2 th {border: 1px solid #504F4F;padding: 5px;}'.APPEND wa_contents TO itab.
wa_contents-line = '.t2 tbody tr:nth-child(even){background-color: #f2f2f2;}'.APPEND wa_contents TO itab.
wa_contents-line = '.t2 tbody tr:hover {background-color: #ddd;}'.APPEND wa_contents TO itab.
wa_contents-line = '.t2h1 {padding-top: 12px;padding-bottom: 12px;text-align: left;background-color: #9BC2E6;color: #504F4F;}'.APPEND wa_contents TO itab.
wa_contents-line = '.t2h2 {padding-top: 12px;padding-bottom: 12px;text-align: left;background-color: #A6A6A6;color: #504F4F;text-align:center;}'.APPEND wa_contents TO itab.
wa_contents-line = |</style></head><body>|.APPEND wa_contents TO itab.
wa_contents-line = |<table border="1" class="t1"><tbody>|.APPEND wa_contents TO itab.
wa_contents-line = |<tr><td>Person Responsible</td><td>{ ename }</td></tr>|.APPEND wa_contents TO itab.
wa_contents-line = |<tr><td>Sales Organization</td><td>{ wtab-vkorg }</td></tr>|.APPEND wa_contents TO itab.
wa_contents-line = |<tr><td>Distribution Channel</td><td>{ wtab-vtweg }</td></tr>|.APPEND wa_contents TO itab.
wa_contents-line = |<tr><td>Division</td><td>{ wtab-spart }</td></tr>|.APPEND wa_contents TO itab.
wa_contents-line = |<tr><td>Sold to Party</td><td>{ wtab-name1 }</td></tr>|.APPEND wa_contents TO itab.
wa_contents-line = |<tr><td>Inco-Terms</td><td>{ wtab-inco1 }</td></tr>|.APPEND wa_contents TO itab.
wa_contents-line = |</tbody></table><table border="1" class="t2" style="margin-top:10px;"><tbody>|.APPEND wa_contents TO itab.
wa_contents-line = |<tr><th class="t2h1">List of Material:</th></tr>|.APPEND wa_contents TO itab.
wa_contents-line = |<tr><th class="t2h2">Material No</th><th class="t2h2">Material Description</th>|.APPEND wa_contents TO itab.
wa_contents-line = |<th class="t2h2">Order Quantity</th>|.APPEND wa_contents TO itab.
wa_contents-line = |<th class="t2h2">UOM</th>|.APPEND wa_contents TO itab.
wa_contents-line = |<th class="t2h2">Pending Quantity</th>|.APPEND wa_contents TO itab.
wa_contents-line = |<th class="t2h2">Basic Price</th>|.APPEND wa_contents TO itab.
wa_contents-line = |<th class="t2h2">Amount</th>|.APPEND wa_contents TO itab.
wa_contents-line = |<th class="t2h2">Currency</th></tr>|.APPEND wa_contents TO itab.

LOOP AT imat INTO wmat.

***GET MATERIALS DETAILS***

ENDLOOP.
wa_contents-line = |</tfoot></table></body></html>|.APPEND wa_contents TO itab.

SET_PARAMS - Method . ( Code - 4 )
  METHOD set_params.

DATA: lv_objtype TYPE sibftypeid,
lv_event TYPE sibfevent,
lv_objkey TYPE sibfinstid,
lr_event_parameters TYPE REF TO if_swf_ifs_parameter_container,
lv_param_name TYPE swfdname,
lv_id TYPE char10.

DATA : wa TYPE zmatlist.

lv_objtype = 'ZSO_APPROVAL_EXIT'. " your class name
lv_event = 'TRIGGER'. " event name.

CALL METHOD cl_swf_evt_event=>get_event_container
EXPORTING
im_objcateg = cl_swf_evt_event=>mc_objcateg_cl
im_objtype = lv_objtype
im_event = lv_event
RECEIVING
re_reference = lr_event_parameters.

lv_param_name = 'ISO'. " parameter name of the event
lv_id = iso.

TRY.
CALL METHOD lr_event_parameters->set
EXPORTING
name = lv_param_name
value = lv_id.

CATCH cx_swf_cnt_cont_access_denied .
CATCH cx_swf_cnt_elem_access_denied .
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_elem_def_invalid .
CATCH cx_swf_cnt_container .
ENDTRY.

lv_param_name = 'IUSER'. " parameter name of the event
lv_id = iuser.

TRY.
CALL METHOD lr_event_parameters->set
EXPORTING
name = lv_param_name
value = lv_id.

CATCH cx_swf_cnt_cont_access_denied .
CATCH cx_swf_cnt_elem_access_denied .
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_elem_def_invalid .
CATCH cx_swf_cnt_container .
ENDTRY.

TRY.
CALL METHOD cl_swf_evt_event=>raise
EXPORTING
im_objcateg = cl_swf_evt_event=>mc_objcateg_cl
im_objtype = lv_objtype
im_event = lv_event
im_objkey = lv_objkey
im_event_container = lr_event_parameters.
CATCH cx_swf_evt_invalid_objtype .
CATCH cx_swf_evt_invalid_event .
ENDTRY.

COMMIT WORK.
ENDMETHOD.

Bellow code can be placed in MV45AFZZ userexit or you can use BADI to call SET_PARAM method to trigger workflow.

( Code - 5 )
*** TRIGGER WORKFLOW FROM MV45AFZZ.
*** LV_USER is an Approver who is going to take decision whether SO should be removed from Credit Block or Not.

*** MANUALLY TRIGGER WORKFLOW FOR VKM1
DATA obj TYPE REF TO zso_approval_exit.
data lv_user_t type uname.

CREATE OBJECT obj.
lv_user_t = |US{ lv_user }|.
obj->set_params( EXPORTING iso = vbak-vbeln iuser = lv_user_t ).
*** MANUALLY TRIGGER WORKFLOW FOR VKM1


*** CODE TO REMOVE OLD/PENDING WORKFLOW TRIGGERD FOR THE SAME SO.
lv_srch = |%{ lv_vbeln }|. "CONCATE % WITH SO NUMBER

SELECT wi_id wi_aagent wi_cd wi_ct FROM swwwihead INTO TABLE wtab
WHERE wi_text LIKE lv_srch AND
( wi_stat EQ 'READY' OR wi_stat EQ 'STARTED' OR wi_stat EQ 'SELECTED' ) AND
top_task EQ 'WS90000016'.

LOOP AT wtab.
IF wtab-wi_cd LT sy-datum.
CALL FUNCTION 'SAP_WAPI_ADM_WORKFLOW_CANCEL'
EXPORTING
workitem_id = wtab-wi_id
actual_agent = wtab-wi_aagent.
ELSEIF wtab-wi_cd EQ sy-datum AND wtab-wi_ct LE time.
CALL FUNCTION 'SAP_WAPI_ADM_WORKFLOW_CANCEL'
EXPORTING
workitem_id = wtab-wi_id
actual_agent = wtab-wi_aagent.
ENDIF.

ENDLOOP.

 

What you learned from this blog post?

  • You can create a workflow with standard BO events and custom class event by following this prototype. It is also possible to merge multiple workitem in a single workflow .

  • Dynamic agent assignment using Function Module.

  • You can design your own stylish content in mail body using CSS + HTML.

  • Further, you can also add validation in Outbound Delivery and Invoice as well to restrict it without final level approvel.


( Screenshot - 39 )



 

Do connect me if there is any issue or if i missed some points.

 

Thanks & Regards,

Rajesh Kalyanji Rajgor

 
29 Comments
Thank You Mr. Rajesh. It is very helpful and informative blog.
Deipac
Explorer
Good one Rajesh. Very informative.
former_member615840
Discoverer

Thanks for the post rajesh. I was excepting this blog for workflow. It's very detailed and useful.

mmcisme1
Active Contributor
Very nice!  I love the pictures they really help me understand what you are trying to say.  Step by step is always good too!  This one is going to be bookmarked for later - when I need it.
Hi Rajesh,

 

very nice blog. I am new to work flow so here my requirement is to have 3 level of workflow to be triggered when sales order is blocked. ( to release the sales order , wf to be triggered with multiple approvers) . Could you please let me know how i can modify this and use .

 

Like sequence of steps to be used , highly appreciate your response.

 

Regards,

Vikram.
rrajgor
Participant
0 Kudos
Hi Vikram, i received your requirement and i have sent you a sample code+steps based on your requirement. Try that and connect me for if there is any doubt,
abhinav_bansode
Discoverer
Its very Informative very nicely explained all the steps good work
sourav2117
Participant
Hi ,

It is very informative , but i am facing with the parameters of the different methods available in class ZSO_APPROVAL  can you please share the importing  and parameter screenshots .

I am trying to use table in ZRULE_FM SAP is not allowing me ,it is giving me error table is outdated.

Please provide the screen shot of parameters.it was a nice blog  .

Thank you for sharing .

If possible you share screenshot at soouravraisr@gmail.com.

 

 
rrajgor
Participant
Hi Sourav,

Thanks so much for your comment. Here are the screenshots for each method,



Make sure you materials method is static, because it will be called by ClassName so.




 

And about the Zrule_FM, it is a Function Module, not a table,  please see ( Screenshot – 4 ).

 
0 Kudos
Hi Rajesh,

 

Thanks a lot for the support, It worked for me. Appreciate your concern.

 

I would say you are a workflow wizard, One click, you have all the solution. great going Keep up the good work.

 

thanks again ,

 

R/Vikram
Hi, Rajesh. Your blog post is very helpful! Thank you so much!

However, we have the multi-level approval. If the workflow is approved by Level 1, it will be sent to the 2nd level for approval. Can I also get the sample code or similar instructions for this?

Looking forward to your response! If possible, you may send the code to my e-mail: aalpantoja@gmail.com.

 
rrajgor
Participant
0 Kudos
Hi Angela, sure I will mail you sample code.
tanuja_wa
Explorer
Hi, Rajesh.

Thank you for detailed documentation.

However, we also have requirement for the multi-level approval. If the workflow is approved by Level 1, it will be sent to the 2nd level for approval. Can I also get the sample code or similar instructions for this?

Looking forward to your response! If possible, you may send the code to my e-mail: siyawa7@gmail.com.

 

Regards,

Siya
rrajgor
Participant
Hello Siya,

Sure, i will send you a sample code.
tanuja_wa
Explorer
0 Kudos
Thank you
0 Kudos
Hi, Rajesh.

Thank you so much for sharing such detailed documentation with us.

I would like to ask if you can share also the multi-level approval. Can I also get the sample code or similar instructions for this?

Looking forward to your response! If possible, you may send the code to my e-mail: mohamad.baradhi@gmail.com

 

Best regards,

Mohamad
rrajgor
Participant
0 Kudos
Sample Code :
*** Avoid SalesOffice field.
************************ USER-EXIT MV45AFZZ START ***********************************
DATA : obj TYPE REF TO zso_approval_exit,
lv_user_t TYPE uname,
it_app TYPE TABLE OF zso_approval,
wa TYPE zso_approval,
lv_user TYPE uname,
so_value TYPE vbrk-netwr,
soff VALUE 'X'.

CREATE OBJECT obj.

IF ( ( vbuk-cmgst EQ 'B' or vbuk-cmgst EQ 'C' ) AND ( sy-tcode EQ 'VA01' OR sy-tcode EQ 'VA02' ) AND _check EQ 'X' ).


CLEAR : wa,lv_stat,lv_user.
REFRESH: it_app.

CALL FUNCTION 'CONVERT_TO_LOCAL_CURRENCY'
EXPORTING
date = sy-datum
foreign_amount = vbak-netwr
foreign_currency = vbak-waerk
local_currency = 'INR'
rate = vbkd-kursk
read_tcurr = ''
IMPORTING
local_amount = so_value.

SELECT COUNT( * ) FROM zso_approval WHERE cr_seg EQ vbak-kkber AND
sales_org EQ vbak-vkorg AND
dist_channel EQ vbak-vtweg AND
division EQ vbak-spart.
IF sy-subrc EQ 0 .
SELECT * INTO TABLE it_app FROM zso_approval
WHERE sales_org EQ vbak-vkorg AND
dist_channel EQ vbak-vtweg AND
division EQ vbak-spart AND
soffice EQ vbak-vkbur.
IF sy-subrc NE 0.
REFRESH : it_app.
CLEAR : soff.
SELECT * INTO TABLE it_app FROM zso_approval
WHERE sales_org EQ vbak-vkorg AND
dist_channel EQ vbak-vtweg AND
division EQ vbak-spart AND
soffice EQ ''.
ENDIF.
LOOP AT it_app INTO wa.
IF wa-min_limit2 NE 0 AND wa-max_limit2 NE 0.
IF so_value BETWEEN wa-min_limit2 AND wa-max_limit2.
lv_user = wa-userid2.
EXIT.
ENDIF.
ELSEIF wa-min_limit2 EQ 0 AND wa-max_limit2 NE 0.
IF so_value LE wa-max_limit2.
lv_user = wa-userid2.
EXIT.
ENDIF.
ELSEIF wa-min_limit2 NE 0 AND wa-max_limit2 EQ 0.
IF so_value GE wa-min_limit2.
lv_user = wa-userid2.
EXIT.
ENDIF.
ENDIF.
CLEAR wa.
ENDLOOP.
REFRESH: it_app.

IF lv_user IS NOT INITIAL.

lv_user_t = |US{ lv_user }|.
obj->set_params( EXPORTING iso = vbak-vbeln iuser = lv_user_t evt_name = 'VKM1' ).

ENDIF.
ENDIF.
ENDIF.

IF ( sy-tcode EQ 'VA01' OR sy-tcode EQ 'VA02' ) AND _check EQ 'X'.
obj->set_params( EXPORTING iso = vbak-vbeln iuser = '' evt_name = 'OTHER' ).
ENDIF.
************************ USER-EXIT MV45AFZZ END ***********************************

************************ ZRULE_FM START ***********************************

DATA : it_app TYPE TABLE OF zso_approval.
DATA : wa TYPE zso_approval,
wa2 TYPE zso_approval.

DATA : wa_actor TYPE swhactor,
wa_container TYPE swcont.
DATA : status TYPE STANDARD TABLE OF jstat,
active TYPE tj30-estat.
DATA: lv_lines TYPE i.
DATA : _objnr TYPE jest-objnr.
DATA : lv_stat(1),
lv_user TYPE uname.
DATA : chdt(8),_check(1).
READ TABLE ac_container INTO wa_container.
IF wa_container-value IS NOT INITIAL.

SELECT SINGLE kursk FROM vbkd INTO @DATA(kursk) WHERE vbeln EQ @wa_container-value.
SELECT SINGLE vkorg,netwr,waerk,vtweg,spart,erdat,vkbur FROM vbak INTO ( @DATA(sorg),@DATA(so_value),@DATA(waerk),@DATA(dist),@DATA(div),@DATA(xerdat),@DATA(soffice) )
WHERE vbeln EQ @wa_container-value.

*** To bypass old SO, - in TVARVC table we are going to maitain date, that means SO Approval process is applicable on those SO with having creation date GE <CHDT>.
SELECT SINGLE low FROM tvarvc INTO chdt WHERE name EQ 'ZSO_APPROVAL'.
IF xerdat GE chdt.
_check = 'X'.
ENDIF.
IF _check EQ 'X'.
CALL FUNCTION 'CONVERT_TO_LOCAL_CURRENCY'
EXPORTING
date = sy-datum
foreign_amount = so_value
foreign_currency = waerk
local_currency = 'INR'
rate = kursk
read_tcurr = ''
IMPORTING
local_amount = so_value.
SELECT COUNT( * ) FROM zso_approval WHERE sales_org EQ sorg.
IF sy-subrc EQ 0.
SELECT * INTO TABLE it_app FROM zso_approval
WHERE sales_org EQ sorg AND
dist_channel EQ dist AND
division EQ div AND
soffice EQ soffice.
IF sy-subrc NE 0.
SELECT * INTO TABLE it_app FROM zso_approval
WHERE sales_org EQ sorg AND
dist_channel EQ dist AND
division EQ div AND
soffice EQ ''.
ENDIF.
SORT it_app BY lvl_stat ASCENDING.
SELECT SINGLE objnr FROM vbak INTO _objnr WHERE vbeln = wa_container-value.
IF sy-subrc EQ 0.
SELECT stat inact FROM jest INTO TABLE status
WHERE objnr = _objnr
AND stat NE 'E0003'
AND stat NE 'I0002'
AND inact EQ space.

IF status IS NOT INITIAL.
active = status[ inact = space ].
ELSE.
active = 'E0001'.
ENDIF.
DATA lx TYPE i VALUE 0.
CASE active.
WHEN 'E0002'.
lx = 1.
WHEN 'E0004'.
lx = 2.
WHEN 'E0005'.
lx = 3.
WHEN 'E0006'.
lx = 4.
ENDCASE.

SORT it_app BY lvl_stat ASCENDING.
LOOP AT it_app INTO wa.
IF wa-min_limit NE 0 AND wa-max_limit NE 0.
IF so_value BETWEEN wa-min_limit AND wa-max_limit.

lv_stat = wa-lvl_stat.
lv_user = wa-userid.
EXIT.
ENDIF.
ELSEIF wa-min_limit EQ 0 AND wa-max_limit NE 0.
IF so_value LE wa-max_limit.

lv_stat = wa-lvl_stat.
lv_user = wa-userid.
EXIT.
ENDIF.
ELSEIF wa-min_limit NE 0 AND wa-max_limit EQ 0.
IF so_value GE wa-min_limit.

lv_stat = wa-lvl_stat.
lv_user = wa-userid.
EXIT.
ENDIF.
ENDIF.
CLEAR wa.
ENDLOOP.
IF lx LT lv_stat.
CASE lx.
WHEN 0.
READ TABLE it_app INTO wa2 WITH KEY lvl_stat = lx + 1.
wa_actor-otype = 'US'.
wa_actor-objid = wa2-userid.
APPEND wa_actor TO actor_tab.
WHEN 1.
READ TABLE it_app INTO wa2 WITH KEY lvl_stat = lx + 1.
wa_actor-otype = 'US'.
wa_actor-objid = wa2-userid.
APPEND wa_actor TO actor_tab.
WHEN 2.
READ TABLE it_app INTO wa2 WITH KEY lvl_stat = lx + 1.
wa_actor-otype = 'US'.
wa_actor-objid = wa2-userid.
APPEND wa_actor TO actor_tab.
WHEN 3.
READ TABLE it_app INTO wa2 WITH KEY lvl_stat = lx + 1.
wa_actor-otype = 'US'.
wa_actor-objid = wa2-userid.
APPEND wa_actor TO actor_tab.
WHEN 4.
ENDCASE.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
ENDIF.

DELETE ADJACENT DUPLICATES FROM actor_tab COMPARING objid.
DESCRIBE TABLE actor_tab LINES lv_lines.
IF lv_lines EQ 0.
RAISE nobody_found.
ENDIF.

************************ ZRULE_FM END ***********************************
ajay_mukundan2
Explorer
0 Kudos
Thanks for the blog, neatly written! @rrajgor

Could you let me know whether the custom workflow was integrated with any SAP Fiori app? like My Inbox or SAP Fiori Approve Requests app? If so, could you share some insights on the approach.
rrajgor
Participant
0 Kudos
Thanks a lot @ajay.mukundan2 , i have configured approval to the fiori inbox. I will share it with you....
markus_deuter
Active Participant
0 Kudos
Hi Rajesh,
I only can say one word to your phantastic blog : wow !

There are so many helpful, technical features inside.

Best regards,
Markus
gandi238
Member
0 Kudos
Hi Rajesh,

Please can you share the steps for the Fiori workflow approval set up for sales orders.

 

Thanks

Gandi
rrajgor
Participant
0 Kudos
Thanks a lot Markus.
rrajgor
Participant
0 Kudos

Hello Gandi Govender,

 

First goto SPRO->SAP NetWeaver->SAP Gateway Service Enablement->Content->Workflow Settings and run [ Maintain Task Names and Decision Options ]

next, create new entries with your workflow ID and Task ID and other relavent fields.

and finally in Decision Keys-> you have to add Approve and Reject buttons .

Please find attached images.

                                                                     SPRO Screen

Add workflowID + TaskID

List of buttons, i.e (Approve/Reject)

 

abapkiranp
Discoverer
Hi Rajesh,

I am new to workflow. I got requirement like below. Would you please help me how to do the requirement. If you need any info from my side, i would provide.

 

Sales Order Approval Workflow, ECN, Price Change, Schedule line date change

The business Prerequisites of the workflow should be specified.

  1. Based on sales document types, Sales Area Workflow should trigger

  2. Approval based on value for 5 level.

  3. Special case based on customer wise workflow should trigger for approval.

  4. Sales Order changes (ECN Changes, Schedule dates in sales order.

rrajgor
Participant
Hello Kiran,

Can you please mail me your business requirement?
Hi Rajesh,

Please can you share code for the event TRIGGER of class ZSO_APPROVAL_EXIT. I have a similar requirement. Also, can you explain the below comment in the code.

"***Code to change SO status in JEST standard table.
***You can use 'I_CHANGE_STATUS' FM
***After getting approved by the approver, it will trigger workflow to next approver level as maitained in ZSO_APPROVAL Table.
***Manually Trigger workflow to next level approver.
***
*** "
alawathugoda
Explorer
0 Kudos

Its superb article & easy to understand , Can some one tell me the available standard workflows for credit block sales order with single approver.

rrajgor
Participant
0 Kudos
lv_user_t = |US{ lv_user }|.
obj->set_params( EXPORTING iso = vbak-vbeln iuser = lv_user_t ).

You can use I_CHANGE_STATUS FM manage the standard configuration managed in SPRO and proceed to the next level. But this status can also be managed in user-exit as well.
0 Kudos
Thank you for such detailed article. In class code declaration of the variables is missing. Request you to kindly share the complete code.
Labels in this area