Custom Area of Responsibility
In process-controlled workflow SAP introduced decision sets (DS) and area of responsibility (AOR) so that responsibility for the items of a document can be distributed among several agents (items are grouped according to the AOR). Mainly, at each approval step, document items are grouped into sets each of which refers to a specific AOR. Item grouping, AOR item assignment and agent resolution tasks are carried out by implementations of /SAPSRM/BD_WF_RESP_RESOLVER BAdI (responsibility resolvers). Standard responsibility resolvers are RR_MANAGER, RR_EMPLOYEE ecc.
HOW IT WORKS
AOR data are stored in /SAPSRM/D_WF_012 table and managed using Persistent Object Service (POS). So, an AOR is represented by an id, a leading object id and an area entity. The persistent class responsible for reading/writing data is /SAPSRM/CL_WF_AREA_BASE. The main AOR class, superclass of all AOR classes /SAPSRM/CL_WF_AREA*, is /SAPSRM/CL_WF_AREA class implementing the /SAPSRM/IF_WF_AREA interface. AOR instances are created and are next retrieved by using method implementations of responsibility resolver BAdI /SAPSRM/BD_WF_RESP_RESOLVER (using persistent classes). More precisely AOR instances are:
- created in /SAPSRM/IF_EX_WF_RESP_RESOLVER~GET_AREA_TO_ITEM_MAP method, next document item guids are assigned to involved AOR
- retrieved in /SAPSRM/IF_EX_WF_RESP_RESOLVER~GET_APPROVERS_BY_AREA_GUID method and asked for agent resolution
Although in /SAPSRM/IF_EX_WF_RESP_RESOLVER~GET_APPROVERS_BY_AREA_GUID method you can return approvers simply filling RT_APPROVER output and ignoring AORs, this is not a good practice. There you must retrieve the AOR instance passed as mandatory input in IS_AREA parameter and ask it for responsible approvers. Even the document key IS_DOCUMENT received as optional input could be useless for agent resolution if document items are grouped into multiple DS belonging to different AORs (Item Based Decision). The purpose of the method is to ask AOR received as input for responsible approvers. AOR instances are built calling the static CREATE_INSTANCE method specifying:
- IV_AREA_TYPE: AOR class name
- IV_LEADING_OBJECT_ID: the leading object of your AOR instance
- IR_EVALUATION_CONTEXT: a generic type ref to data
As said before AOR data are managed using the Persistent Object Service in /SAPSRM/D_WF_12 table; although IV_LEADING_OBJECT_ID is typed as ANY in CREATE_INSTANCE method, it is a 32 char [as you can see in /SAPSRM/D_WF_012 table field and in the INITIALIZE method of /SAPSRM/CL_WF_AREA class]. What about IR_EVALUATION parameter in CREATE_INSTANCE method? Following abap code single-step inside [/SAPSRM/CL_WF_AREA] you can see taht IR_EVALUATION_CONTEXT “is lost” when calling SAVE method. As explained AOR are first retrieved and next asked for agent resolution; in standard AOR implementations [/SAPSRM/CL_WF_AREA* classes] the LEADING_OBJECT_ID code is enough for carrying out agent resolution task (probably this is the reason why the IR_EVALUATION_CONTEXT parameter is ignored). What if an AOR needs more than just a single LEADING_OBJECT_ID code for agent resolution?
CUSTOM AOR DEVELOPMENT
I show you how to take advantage of IR_EVALUATION_CONTEXT parameter for handling additional data for agent resolution in AOR used by standard process controlled workflows. The IR_EVALUATION_CONTEXT parameter, being a type ref to data, can be used to inject additional data of any kind in the AOR instance. Technically the IR_EVALUATION_CONTEXT passed must be:
- mapped to specific custom attributes
- stored using persistent object service in specific database tables
Below, to simplify, i decided to handle a structure (so, a line). First of all we need to create a database table for storing our custom data; the only thing to take care of is to start our table primary key with the client and the AOR id (the same of /SAPSRM/D_WF_012 table). Assume that our evaluation context table is this one, the ZIREVACONTEXT table:
Next we have to create a persistent class for our evaluation context table. This is quite simple; everything’s carried out by the SAPGUI. Simply create a new class specifying the persistence option (Img. 1); next click the persistence button and specify the database table for your evaluation context (Img. 2). Add all table fields as persistent attribute to your persistence class (Img. 3) and configure generator settings as defined for /SAPSRM/CL_WF_AREA_BASE class (Img. 4). You can also keep default configuration simply adding the “Generate Methods for Query” tick (this is important).
Save, activate. The SAPGUI automatically ask you for activation of actor and base classes. If you want to explore deeper the POS just have a look at SAP Help.The last step is to build our AOR class so that it can manage our evaluation context; you could do it two ways:
- as subclass of base responsible area class /SAPSRM/CL_WF_AREA
- implementing the /SAPSRM/IF_WF_AREA interface modeling your code as the one on class /SAPSRM/CL_WF_AREA
I prefer to show you the second one choice so that i have full control of implementation; the first one is a little bit tricky because of some constraints on method redefinition. I created a protected instance generation class named ZCL_WF_AREA_DEMO implementing the /SAPSRM/IF_WF_AREA interface and defined attributes and methods exactly as defined in base class /SAPSRM/CL_WF_AREA. Now my class is able to manage only data of standard table /SAPSRM/D_WF_012. To manage data of my custom table i added two private methods:
- LOAD_EVALUATION_CONTEXT: responsible for reading persistent instance data
- SAVE_EVALUATION_CONTEXT: responsible for saving attribute & persistent data (receive as input the IR_EVALUATION_CONTEXT)
- i added a call to my LOAD_EVALUATION_CONTEXT method at the end of the basic LOAD method
- i added a call to my SAVE_EVALUATION_CONTEXT method at the end of basic SAVE method transferring to it the IR_EVALUATION_CONTEXT parameter
- i implemented /SAPSRM/IF_WF_AREA~GET_RESPONSIBLE_APPROVERS method (in general there you must implement the agent resolution logic based on instance attribute values; in that case to simplify i supposed that each field of my structure contains the agent user id)
Let’s go a little bit inside the code. That’s the LOAD_EVALUATION_CONTEXT method:
data: lo_query_manager type ref to if_os_query_manager, lo_query type ref to if_os_query, lt_area_entity_ref type osreftab, lt_area_entity_query type osreftab, lo_area_entity_ref like line of lt_area_entity_ref, lo_area_entity type ref to /sapsrm/if_wf_area_entity, lv_area_entity_id type /sapsrm/wf_area_entity_id, lo_ps_context type ref to zcl_ps_eval_context. *load evaluation context data from db-persistence layer *-> simply an adjustment of standard load code for our evealuation context *get the newly created objects form the object service and filter those lt_area_entity_ref = zca_ps_eval_context=>agent->if_os_ca_instance~get_created( ). loop at lt_area_entity_ref into lo_area_entity_ref. cast zcl_ps_eval_context( lo_area_entity_ref ). lo_ps_context = cast #( lo_area_entity_ref ). if lo_ps_context->get_id( ) ne me->/sapsrm/if_wf_area~get_guid( ). delete lt_area_entity_ref index sy-tabix. endif. endloop. *get the persistent objects by query and append them to list lo_query_manager = cl_os_system=>get_query_manager( ). lo_query = lo_query_manager->create_query( i_filter = `ID = PAR1` ). lt_area_entity_query = zca_ps_eval_context=>agent->if_os_ca_persistency~get_persistent_by_query( i_query = lo_query i_par1 = me->/sapsrm/if_wf_area~get_guid( ) ). append lines of lt_area_entity_query to lt_area_entity_ref. *assign values to local attributes (IR_EVALUATION_CONTEXT) loop at lt_area_entity_ref into lo_area_entity_ref. * cast object cast zcl_ps_eval_context( lo_area_entity_ref ). lo_ps_context = cast #( lo_area_entity_ref ). * prepare lines me->ir_evaluation_context-mandt = sy-mandt. me->ir_evaluation_context-id = lo_ps_context->get_id( ). me->ir_evaluation_context-field1 = lo_ps_context->get_field1( ). me->ir_evaluation_context-field2 = lo_ps_context->get_field2( ). me->ir_evaluation_context-field3 = lo_ps_context->get_field3( ). endloop.
as you can see it is simply an adapted copy/paste of standard LOAD method based on our persistent-class. Same approach for SAVE_EVALUATION_CONTEXT method:
data: lo_context type ref to zcl_ps_eval_context. field-symbols: <ls_context> type zirevacontext. *evaluation context management if ir_evaluation_context is bound. * cast it in your structure assign ir_evaluation_context->* to <ls_context>. * store attribute using db persistence layer lo_context = zca_ps_eval_context=>agent->create_persistent( me->/sapsrm/if_wf_area~get_guid( ) ). lo_context->set_field1( <ls_context>-field1 ). lo_context->set_field2( <ls_context>-field2 ). lo_context->set_field3( <ls_context>-field3 ). * initialize attribute <ls_context>-mandt = sy-mandt. <ls_context>-id = me->/sapsrm/if_wf_area~get_guid( ). me->ir_evaluation_context = <ls_context>. endif.
To simulate the behavior when used inside a BAdI implementation i wrote a simple report which consist of a few dozen lines.
report zrespareawithevcntx. data: lt_approver type /sapsrm/t_wf_approver, ls_approver type /sapsrm/s_wf_approver, lo_areac type ref to zcl_wf_area_demo, "area created lo_areal type ref to zcl_wf_area_demo, "area loaded lo_context type ref to zirevacontext. parameters: p_f1 type fieldname obligatory, p_f2 type fieldname obligatory, p_f3 type fieldname obligatory. start-of-selection. create data lo_context. * set context values lo_context->mandt = sy-mandt. lo_context->field1 = p_f1. lo_context->field2 = p_f2. lo_context->field3 = p_f3. * build responsible area * must be done in method /SAPSRM/IF_EX_WF_RESP_RESOLVER~GET_AREA_TO_ITEM_MAP * of your responsible resolver lo_areac ?= zcl_wf_area_demo=>/sapsrm/if_wf_area~create_instance( iv_area_type = zcl_wf_area_demo=>gc_area_type iv_leading_object_id = 'LEAD' ir_evaluation_context = lo_context ). * retrieve instance & ask for approvers * must be done in method /SAPSRM/IF_EX_WF_RESP_RESOLVER~GET_APPROVERS_BY_AREA_GUID * of your responsible resolver lo_areal ?= zcl_wf_area_demo=>/sapsrm/if_wf_area~get_instance_by_guid( iv_area_type = zcl_wf_area_demo=>gc_area_type iv_area_guid = lo_areac->/sapsrm/if_wf_area~get_guid( ) ). lt_approver = lo_areal->/sapsrm/if_wf_area~get_responsible_approvers( ). loop at lt_approver into ls_approver. write: / ls_approver-approver_ot, ls_approver-approver_id. endloop.
Comments inside the code should be enough to understand the code. As explained the report simulate calls just for quick testing purposes; if you want to test it during a document approval you must implement your own responsibility resolver [Badi /SAPSRM/BD_WF_RESP_RESOLVER].
I showed how to build up a custom AOR class so that it can be used in agent resolution in standard process controlled workflows. Mainly there are 4 steps to perform:
- Define tables to store AOR custom data
- Create persistent class(es) to handle your data using POS
- Create your AOR class implementing /SAPSRM/IF_WF_AREA interface modeled as /SAPSRM/CL_WF_AREA
- Make appropriate changes to your AOR class so that it can manage in LOAD and SAVE method your custom data
Obviously (this is why in point 1 and 2 i talked about tables and classes) you can also define your AOR with many attributes (structures or internal tables). You just need to handle attributes-database read/write using POS.
You can download code sample there: sap_custom_oar.