Skip to Content
Technical Articles

Sales Order – Level Approval Workflow and release from Credit Block 2 in 1

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

 

17 Comments
You must be Logged on to comment or reply to a post.
  • 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.

    • 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,

      • 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 ,

    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.

     

     

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

       

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

     

  • 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

  • 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

  • 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 ***********************************