Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 

Introduction

Indirect Procurement is the sourcing of all goods and services for a business which are commonly bought for consumption by internal business units or by individual users. This process explains the shopping cart approval process taking place in the system prior to generation of a Purchase Order for the Supplier.

Approval Process in the workflow will be triggered for every creation of shopping carts. This is usually on the basis of the steps defined in Process Schema.

This document explains how to create the steps dynamically while creating the SC. Each step will create a Work item in the Approval Process Overview tab. It will be done without defining the step explicitly in Process Schema.

Since this involves many steps we will use an example to explain the concept of dynamic creation. This example will be cited throughout the document.

Example:

For instance, during Shopping cart creation, manager approval is required when the shopping cart total value is above the spend limit of the requester.  The number of level of approvals depends upon the total shopping cart value.

In the above example, we need to obtain the manager of the requestor/on-behalf of the Shopping cart and add it as a workitem in the Approval process overview tab in each item of the Shopping cart. If the spend limit of the manager is lesser than the total value of the shopping cart, SC has to get approval from his manager also. New manager will be added as the second work item. This means that, SC has to first get approval from Manager 1 and then from Manager 2. Only then PO will be created. This flow will continue till we obtain the manager whose spend limit crosses the total value of the shopping cart.

 

This brings in the requirement of creation of steps/process levels dynamically as we do not know beforehand how many workitems / managers need to be added in Approval Process.

Process levels are created dynamically in the BADI /SAPSRM/BD_WF_PROCESS_CONFIG.

Step 1:
Inside our BADI Implementation, we have the GUID of the shopping cart from IS_DOCUMENT.

Step 2:
We have to determine the managers and create Process Levels and then send it to CS_PROCESS_LEVEL.

Step 3:
Once levels are created, the BADI for Agent Determination is called as many times as the number of levels in CS_PROCESS_LEVEL.

Step 1: Obtaining the Managers:

Citing our example again , we refer two Z-tables for Manager Approval:

  • Job Band Table – Spend Limit of each job band
  • Manager Table – Hierarchy of the Requestor and Managers along with the job band.

Logic:

System will identify the “requester”\”On-behalf” of user of the shopping cart and  gets his Band from “Manager table” table. The spend limit for the given job band will be identified from “Job Band Table”.

The total Shopping cart value will be compared with Spend Limit of the given job band.  If the SC value is less than Spend limit, no approval will be triggered.  System will do auto approval and SC will be converted to PO.

If the SC value is greater than the “identified Job band spend limit” the system triggers a work flow for approval.

First approver will be the manager user ID of “requester” or “On behalf of “user from the “Manager table”.

System compares the total shopping cart value with approval limit of Manager identified. If approval limit is less than the total shopping cart value, system will get next level Manager from “Manager table” table. This process will continue till the shopping cart value is less than the approval limit value of the derived manager.

Finally we will get a list of approvers in an internal table with 2 columns:

  1. Level
  2. Manager Id

Step 2: Creation of Process Levels

/SAPSRM/BD_WF_PROCESS_CONFIG was implemented with name– Z_SC_PROCESS_LEVEL. Z_SC_PROCESS_LEVEL is triggered by passing the same Resp Resolver Name as that of the first step in the Process Schema.

The existing Process Schema is shown below;

Our BADI Implementation:

If you notice the Resp Resolver name passed is Z_SC_PURCHASER. This is done to trigger the Process Config BADI when the whole Approval Process is triggered.

We already have Step 10 and Step 999. Step 10 is the first step. We need to insert our dynamically created steps after Step 10.

In our case, Approvers list is same for all the items as the approval is determined for the total value of the shopping cart.

Loop at approvers for each item (two loops). Inside the loop, for each approver create process level and obtain level GUID.

Process Level can be created by passing the below values:

      level_seqnr = 20 initially and then +10.
      level_type  = 'A'.
      evaluation_id = 'ZEV_SC_MGR_REVIEW'.
      resolver_name = 'Z_SC_MANAGER'.
      template_id = 'WS40000017'.
      approval_task_id = 'TS40007953'.
      decision_type = '1'.
      doc_changeable = ''.

This configuration is similar to the values maintained in the Process Schema and it creates as many numbers of levels as the number of
approvers.

We are also passing an Evaluation ID and Responsible Resolver Name. Evaluation Id (or Event Id in BRF) is for evaluating the SC DATA
and returns a unique result. This Boolean value will determine if the BADI has to be triggered or not. Expression provided encapsulated within the Event ID can be standard or custom or combination of both. This is used for the evaluation of the SC data.

Depending on the nature of evaluation and the availability of suitable standard BRF expressions, Expression type for SC could be 0CF001 or 0FB001.

Each Process Level created will in turn call the Agent Determination BADI through the Responsible Resolver Name mentioned above.

BRF

Event Id and Expression are created in BRF (Business Rule Framework). BRF events form a connection between the SAP SRM system and the BRF. They are the central entry point for using the BRF. You must assign an event to each process schema, so that the system can determine the process schema to be used for a specific approval process. You must also assign an event to each process level, so that the system can determine for each process level whether or not the process level needs to be performed. Each event  contains a BRF expression.

To do so, go to BRF transaction.

Event created is - ZEV_SC_MGR_REVIEW. It contains the Expression Z_SC_MGR_REVIEW which evaluates whether the Process level has to be executed or not.

Expression
Also created in BRF transaction is defined as a SAP Formula Interpreter OFB001. According to our requirement we can create custom or combination of custom and standard expressions as well. For example conditions can be maintained in a custom FM and can be attached to the Expression.

In our case, we have written an expression which will return a Boolean value. If the total value of the Shopping cart exceeds zero, expression will return 0, thereafter the process level will be executed. Else, it will be exited and the Process level will not be executed.

This sounds meaningful as we determine Agents/managers if the total shopping cart value exceeds the spend limit of the owner of the
Shopping cart.

Coming back to our BADI Implementation, Z_SC_PROCESS_LEVEL; with the data passed into process level, we create a new process level;

        CALL METHOD io_level_service->create_process_level
          CHANGING
            cs_process_level = cs_process_level.

Newly created cs_process_level is updated in 2 tables,

  1. Agent table – Agent on which we are looping
  2. Item table – Item on which we are looping

Both the tables will be linked through the same GUID of the cs_process_level.

Once the 2 tables are ready for each agent and the item, the internal tables are passed to /SAPSRM/WF_CORE_ADHOC_SAVE function module. The agent table and item table are saved in DDIC, so that it can be retrieved later in the agent determination.

Please refer the related code below:

BADI Implementation:                                             Z_SC_PROCESS_LEVEL_IM-GET_APPROVERS_LIST


Z_SC_PROCESS_LEVEL_IM-GET_APPROVERS_LIST:


method GET_APPROVER_LIST.


*Method to determine dynamic steps based on Total value of Shopping cart.
  TYPES : BEGIN OF ty_approvers_list,
            level TYPE swc_index,
*            userid TYPE sysid,
            userid TYPE uname,
            username TYPE zusername,
          END OF ty_approvers_list.


  TYPES ty_level_to_agent_map TYPE /sapsrm/d_wf_016 .
  TYPES:tty_level_to_agent_map TYPE STANDARD TABLE OF /sapsrm/d_wf_016 .
  TYPES ty_level_to_item_map TYPE /sapsrm/d_wf_015 .
  TYPES tty_level_to_item_map TYPE STANDARD TABLE OF /sapsrm/d_wf_015.


  DATA lt_level_to_agent_map TYPE tty_level_to_agent_map.
  DATA l_wa_level_to_agent_map TYPE ty_level_to_agent_map.
  DATA lr_level_to_agent_map TYPE REF TO ty_level_to_agent_map.
  DATA lt_agent_list TYPE /sapsrm/t_wf_agent_id.
  DATA l_wa_level_to_item_map  TYPE ty_level_to_item_map.
  DATA lt_level_to_item_map  TYPE tty_level_to_item_map.
  DATA l_wa_bd_wf_level TYPE /sapsrm/s_bd_wf_level.



  DATA:l_guid           TYPE bbp_guid,
       l_onbehalf       TYPE abap_bool,
       l_doc_owner_guid TYPE guid,
       l_ob_employee    TYPE REF TO if_bbp_es_employee,
       l_doc_owner      TYPE xubname,
       l_employee       TYPE sysid,
       l_wa_header      TYPE bbp_pds_sc_header_d,
       l_level          TYPE /sapsrm/wf_process_level_seqnr,
       lt_item          TYPE STANDARD TABLE OF bbp_pds_sc_item_d,
       l_wa_item        TYPE bbp_pds_sc_item_d,
       l_co_code        TYPE be_ewerk,
       lt_approvers     TYPE /SAPSRM/T_BD_WF_APPROVER,
       gt_approvers_list TYPE STANDARD TABLE OF ty_approvers_list,
       g_wa_approvers_list TYPE ty_approvers_list.


  CLEAR: lt_level_to_agent_map,
         l_wa_level_to_agent_map,
         lr_level_to_agent_map,
         lt_agent_list,
         l_wa_level_to_item_map,
         lt_level_to_item_map,
         l_wa_bd_wf_level,
         l_guid,
         l_onbehalf,
         l_doc_owner_guid,
         l_ob_employee,
         l_doc_owner,
         l_employee,
         l_wa_header,
         l_level,
         lt_item,
         l_wa_item,
         l_co_code,
         lt_approvers,
         gt_approvers_list,
         g_wa_approvers_list.



  CONSTANTS: c_x TYPE c VALUE 'X'.



*Process present Step
  CALL METHOD io_level_service->create_process_level
    CHANGING
      cs_process_level = cs_process_level.



* Determine on-behalf userid when SC is created on-behalf.
* SC hdr guid
  l_guid = is_document-document_guid.



* By default, populating the doc owner as Sy-uname.
  l_employee = sy-uname.



* check if SC is created as on-behalf
  CALL METHOD /sapsrm/cl_pdo_sc_doc_helper=>created_on_behalf_of
    EXPORTING
      iv_header_guid      = l_guid
      iv_user             = sy-uname
    IMPORTING
      rv_bought_on_behalf = l_onbehalf.



  IF l_onbehalf EQ c_x.



*  get document owner guid
    CALL METHOD /sapsrm/cl_pdo_sc_doc_helper=>get_document_owner
      EXPORTING
        iv_header_guid    = l_guid
      RECEIVING
        rv_document_owner = l_doc_owner_guid.



* get the doc owner username
    l_ob_employee = cl_bbp_es_enterprise=>get_employee_by_guid( l_doc_owner_guid ).


    l_doc_owner = l_ob_employee->get_username( ).


    IF l_doc_owner IS NOT INITIAL.
      l_employee = l_doc_owner.
    ENDIF.



  ENDIF.



  CALL FUNCTION 'BBP_PD_SC_GETDETAIL'
    EXPORTING
      I_GUID          = is_document-document_guid
      I_WITH_ITEMDATA = c_x
    IMPORTING
      E_HEADER        = l_wa_header
    TABLES
      E_ITEM          = lt_item.



  DELETE  lt_item WHERE del_ind = c_x.
  CHECK  lt_item IS NOT INITIAL.


  READ TABLE lt_item INTO l_wa_item INDEX 1.



  IF NOT l_wa_item IS INITIAL.
    l_co_code = l_wa_item-be_co_code.
  ENDIF.



** Obtain the list of approvers for the requestor/doc owner.
  IF NOT l_wa_header IS INITIAL
  AND NOT l_wa_item IS INITIAL
  AND  NOT l_employee IS INITIAL.



* <Logic for obtaining the approvers from the Z-tables (Job band and Manager tables) based on the logic mentioned above into a temporary table - gt_approvers_list.>



* Once the list of approvers are obtained,
  SORT gt_approvers_list BY level userid.



  DELETE ADJACENT DUPLICATES FROM gt_approvers_list COMPARING userid.



* Looping at the items.
* Map the process level guid to each item.
* Map the process level guid to each agent.
* Both the mapped tables (item and agent list) is sent to the ADHOC_SAVE FM.
* This data will be retrieved later in the Agent Determination (BD_WF_RESP_RESOLVER) BADI.



  LOOP AT lt_item INTO l_wa_item.



* Approvers list is same for all the items as the approval is
* determined for the total value of the shopping cart.


    LOOP AT gt_approvers_list INTO g_wa_approvers_list.



* Create process level and obtain level GUID
      IF l_level IS INITIAL.
        l_level = '020'.
      ENDIF.



      l_wa_bd_wf_level-level_seqnr = l_level.
      l_wa_bd_wf_level-level_type  = 'A'.
      l_wa_bd_wf_level-evaluation_id = 'ZEV_SC_MGR_REVIEW'.
      l_wa_bd_wf_level-resolver_name = 'Z_SC_MANAGER'.
      l_wa_bd_wf_level-template_id = 'WS40000017'.
      l_wa_bd_wf_level-approval_task_id = 'TS40007953'.
      l_wa_bd_wf_level-decision_type = '1'.
      l_wa_bd_wf_level-doc_changeable = ''.


      cs_process_level =  l_wa_bd_wf_level.
      CLEAR :l_wa_bd_wf_level.



* If the agent is already mapped in the agent table,
* mapping the item to the same guid.
      READ TABLE lt_level_to_agent_map INTO l_wa_level_to_agent_map
                                       WITH KEY agent_id = g_wa_approvers_list-userid.
      IF sy-subrc = 0.
        l_wa_level_to_item_map-level_guid = l_wa_level_to_agent_map-level_guid.
        l_wa_level_to_item_map-item_guid = l_wa_item-guid.
        APPEND l_wa_level_to_item_map TO lt_level_to_item_map.
        CLEAR l_wa_level_to_item_map.
        CONTINUE.



* Adding the agent to the agent table and mapping it to item table.
      ELSE.



* Creating a new process level.
        CALL METHOD io_level_service->create_process_level
          CHANGING
            cs_process_level = cs_process_level.
        l_level = cs_process_level-level_seqnr + '10'.


      ENDIF.



      IF NOT cs_process_level-level_guid IS INITIAL.



* Item table updated.
        l_wa_level_to_item_map-level_guid  = cs_process_level-level_guid.
        l_wa_level_to_item_map-item_guid = l_wa_item-guid.
        APPEND l_wa_level_to_item_map TO lt_level_to_item_map.
        CLEAR: l_wa_level_to_item_map.
* Agent table updated.
        l_wa_level_to_agent_map-level_guid = cs_process_level-level_guid.
        l_wa_level_to_agent_map-agent_id    = g_wa_approvers_list-userid.
        APPEND l_wa_level_to_agent_map TO lt_level_to_agent_map.
        CLEAR l_wa_level_to_agent_map.
      ENDIF.


      CLEAR:g_wa_approvers_list.



    ENDLOOP. "approvers_list
    CLEAR l_wa_item.
  ENDLOOP. "items



* Pass the internal table to the ADHOC_SAVE function module
* The agent table and item table are saved in DDIC, so that
* it can be retrieved later in the agent determination.



  CALL FUNCTION '/SAPSRM/WF_CORE_ADHOC_SAVE'
    TABLES
      it_level_to_item_map  = lt_level_to_item_map
      it_level_to_agent_map = lt_level_to_agent_map.
  CLEAR: lt_level_to_item_map,lt_level_to_agent_map.




endmethod.



Step3: Agent Determination BADI

Hence once process levels starts executing, it calls the Agent Determination BADI with the Resp Resolver saved in the process level as many times as the number of process levels.

It will call the BADI Implementation of /SAPSRM/BD_WF_RESP_RESOLVER with the filter value as Z_SC_MANAGER. This is the same Resp Resolver passed while creating the Process level.

In this Implementation, we do the following steps:

  1. Map each item to the responsibility area in /SAPSRM/IF_EX_WF_RESP_RESOLVER~GET_AREA_TO_ITEM_MAP
  2. Determine the Manager approvers for SC Approval in /SAPSRM/IF_EX_WF_RESP_RESOLVER~GET_APPROVERS_BY_AREA_GUID.

Step a: Map each item to the responsibility area.

The item and agent data table that was updated in the PROCESS_CONFIG Badi is retrieved here through the FM /SAPSRM/WF_CORE_ADHOC_READ. Please be aware that the process level information is also present in these tables.

After combining the Item and Agent data into a single table, create area of responsibility for each combination of item and Agent.

While creating a virtual responsibility area, a leading object has to be determined and in our case it is the Agent ID. We are trying to create a work item for each item and then trying to map the agent to the Workitem.

A work item is created for every new Agent ID.

This is done by passing the Agent ID in the table as the Leading object to the Z-class which is used to determine the  approvers: ZCL_WF_AREA_MANAGER_APRROVER.  This class is of type /SAPSRM/CL_WF_AREA.

Please refer the below related code: ZCL_SC_MANAGER_APPROVER-GET_AREA_TO_ITEM_MAP

     

  



method /SAPSRM/IF_EX_WF_RESP_RESOLVER~GET_AREA_TO_ITEM_MAP.



TYPES : BEGIN OF ty_process,
          agent_id TYPE /sapsrm/wf_agent_id,
          item_guid TYPE bbp_guid,
         END OF ty_process.



DATA :  lt_level_to_agent_map   TYPE STANDARD TABLE OF /sapsrm/d_wf_016,
         l_wa_level_to_agent_map TYPE /sapsrm/d_wf_016,
         lr_level_to_agent_map   TYPE REF TO /sapsrm/d_wf_016,
         lt_level_to_item_map    TYPE STANDARD TABLE OF /sapsrm/d_wf_015,
         l_wa_level_to_item_map  TYPE /sapsrm/d_wf_015,
         lr_level_to_item_map    TYPE REF TO /sapsrm/d_wf_015,
         l_ob_area               TYPE REF TO /sapsrm/if_wf_area,
         l_wa_area_to_item_map   TYPE /sapsrm/s_wf_item_to_area,
         gt_process              TYPE STANDARD TABLE OF ty_process,
         g_wa_process            TYPE ty_process.



CLEAR:  lt_level_to_agent_map,
         l_wa_level_to_agent_map,
         lt_level_to_item_map,
         l_wa_level_to_item_map,
         l_wa_area_to_item_map,
         gt_process,
         g_wa_process.



* Input checks
  ASSERT ID /sapsrm/wf_cfg CONDITION ( NOT is_document-document_guid IS INITIAL ).
  ASSERT ID /sapsrm/wf_cfg CONDITION ( NOT is_current_process_level-level_guid IS INITIAL ).



  IF is_document-document_guid IS INITIAL OR
     is_current_process_level-level_guid IS INITIAL.
    RAISE EXCEPTION TYPE /sapsrm/cx_wf_abort.
  ENDIF.



  CHECK is_document IS NOT INITIAL.


*-------------------------------------------------------------------------------------*
* 1) Fetching the item and agent data table that was updated
*in the PROCESS_CONFIG Badi.
* Both tables are mapped with the process levels as well.
*-------------------------------------------------------------------------------------*



  CLEAR: lt_level_to_item_map,lt_level_to_agent_map.



  IF NOT is_current_process_level-level_type EQ /sapsrm/if_wf_process_c=>gc_prc_level_type_completion.



    CALL FUNCTION '/SAPSRM/WF_CORE_ADHOC_READ'
      EXPORTING
        iv_adhoc_process_level_guid = is_current_process_level-level_guid
      TABLES
        et_level_to_item_map        = lt_level_to_item_map
        et_level_to_agent_map       = lt_level_to_agent_map.



  ENDIF.



* Putting the item abd aget data into a single table.



  LOOP AT lt_level_to_item_map REFERENCE INTO lr_level_to_item_map.



    g_wa_process-item_guid = lr_level_to_item_map->item_guid.



    CLEAR l_wa_level_to_item_map.
    READ TABLE lt_level_to_agent_map
         REFERENCE INTO lr_level_to_agent_map
         WITH KEY level_guid = lr_level_to_item_map->level_guid.
    IF sy-subrc = 0.



      g_wa_process-agent_id = lr_level_to_agent_map->agent_id.
      APPEND g_wa_process TO gt_process.
      CLEAR g_wa_process.



    ELSE.
      CLEAR g_wa_process.



    ENDIF.



    CLEAR l_wa_level_to_item_map.
  ENDLOOP.



  SORT gt_process.
  LOOP AT gt_process INTO g_wa_process.



* Creating Area of responsibility for the item and agent.
    AT NEW agent_id.
     l_ob_area = /sapsrm/cl_wf_area=>/sapsrm/if_wf_area~create_instance(
     iv_area_type         = 'ZCL_WF_AREA_MANAGER_APRROVER'
     iv_leading_object_id =  g_wa_process-agent_id
     ).
      l_wa_area_to_item_map-area_guid = l_ob_area->get_guid( ).
    ENDAT.



    l_wa_area_to_item_map-item_guid = g_wa_process-item_guid.
    APPEND l_wa_area_to_item_map TO rt_item_to_area_map.
    CLEAR: g_wa_process.



  ENDLOOP.



endmethod.


Step b: Determine the Manager Approvers

Method GET_APPROVERS_BY_AREA_GUID of the same Implementation class returns all line managers of these organizational  units as responsible agents.

First we obtain the responsibility area reference for given area GUID which was created in GET_AREA_TO_ITEM_MAP.

This is done by the code:

  l_ob_area = /sapsrm/cl_wf_area=>/sapsrm/if_wf_area~get_instance_by_guid(
    iv_area_type ='ZCL_WF_AREA_MANAGER_APRROVER'
    iv_area_guid = is_area-area_guid
    ).

The actual code of retrieving the managers is inside the method GET_RESPONSIBLE_APPROVERS of Z-class  ZCL_WF_AREA_MANAGER_APRROVER. This class is of type /SAPSRM/CL_WF_AREA.

ZCL_WF_AREA_MANAGER_APRROVER

As we already have the Agent Information, we only need to add the Agent into the table RT_APPROVER and return the same. As you can
recall, Agent id was passed as the leading object into the Z-class from GET_AREA_TO_ITEM_MAP.

As each process level is mapped as a Work Item, the Approvers inside RT_APPROVER will be mapped to the Work Item.

Hence, each agent that was retrieved from the Z table will create a work item in each item of the SC and will be mapped to the
Workitem as the hierarchy.

Please refer the below related code: ZCL_WF_AREA_MANAGER_APPROVER-GET_RESPONSIBLE_APPROVERS

 



method /SAPSRM/IF_WF_AREA~GET_RESPONSIBLE_APPROVERS.



  DATA l_wa_approver        TYPE /sapsrm/s_wf_approver.
  DATA l_agent_id TYPE  /sapsrm/wf_agent_id .
  DATA l_area  TYPE /sapsrm/wf_area_guid.



  CLEAR :l_agent_id,
         l_area,
         l_wa_approver.



*-----------------------------------------------------------------------
* Area of responsibility corrsponds to agent id.
*-----------------------------------------------------------------------


  l_agent_id = me->/sapsrm/if_wf_area~get_leading_object_id( ).
  l_area = me->/sapsrm/if_wf_area~get_guid( ) .



  IF l_agent_id IS INITIAL.
    RETURN.
  ENDIF.



  l_wa_approver-approver_ot = 'US'.
  l_wa_approver-approver_id = l_agent_id.
  APPEND l_wa_approver TO rt_approver.
  CLEAR: l_wa_approver.



endmethod.



     

         

Once, these steps are done, if the SC is created with the total value as > 0, it will trigger the Manager Approver Step.

Example:

Shown below is the screenshot of the Approval Process Tab.

First line item is Approver based on the first step in Process Schema.

From Second line item, you can see the approvers in the form of hierarchy created as part of Manager Determination. As per the example, when Step 10 is approved, it will come to Manager Approvers. This will create a work item in MGR1_WD1 and when  MGR1_WD1 approves, a work item is created in MGR2_WD1. Once, MGR2_WD1 approves, Approval Process shows status as Finished.

Please refer the below link for knowing more on BRF Expression and SC workflow:

http://www.sdn.sap.com/irj/scn/go/portal/prtroot/docs/library/uuid/d0fb3445-69a0-2d10-78a1-8681f27f4...

9 Comments