- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
The credit for this post is given to Hima Chalimadugu. Thanks to her, I am just spreading the knowledge 😄
3.1Layered Approach
The approach designed to handle dynamic approvals is layered into 3 concentric layers as shown in the attached diagram.
3.1.1 Layer 1 - Template
This layer acts an assistant to note down what the requirements are. It makes sure that the requirements are not vague and are specific to whatever level the workflow demands.
i) Components of Template Layer
•Table to capture requirements
•UI actions on this table, for a better UI
ii)Description
An external table is required to keep capturing all requirements. Though workflow can handle it initially, an external table would be needed if these requirements keep changing. Otherwise, any requirement which comes will directly go and change the whole workflow. This in turn changes business rules or other components which take care of smooth transitions. This table has few mandatory columns to identify exact time at which an approval is to be generated. The column structure of the table is something which keeps changing for customer to customer. But anyway, keeping worst case in mind, there wouldn't be more than 20 columns to capture these requirements. A generic structure of this table has been provided in the table below.
Type of TaskStage of TaskType of ApprovalApproverRole of approver (w.r.t requestor)Order Escalation Needed??Dependent FieldCondition 1Condition 2Condition N
Type of task — Requested Item, Catalog Task, Change Request….so on
Stage — Waiting for approval, Awaiting User Revision…..any stage of the workflow
Type of Approval — Auto Approve, Parallel Approval…..based on requirement. (This logic is coded in the next layer. Hence type of approval should be known before-hand. (Not dynamic) )
Approver — Can be a direct reference to user table
Role — Manager, Vice President or Group Director of the requestor
Order — can be any integer value which differentiates each level of approval. ( The design of this field is responsible for all iterations. Hence it is better to maintain a constant difference between consequent approvals.)
The other fields are all dependent on how versatile an approval generation should be.
3.1.2Layer 2 — Approval Log
There are various needs which are all satisfied by yet another table that acts like a log. Layer 2 takes inputs from layer 1 and creates log entries which are used by layer 3 at any later point. This way, all approvals which are created are kept track of and handled separately. For each task created, an entry is made in approver log and is updated whenever approvals change.
iii) Components of Approval Log
•Table to keep track of approvals created
iv)Description
Though we have sysapproval_approver table as a related list in Task table, an increase in number of approvals would result in a huge list of approvers without any other details. Also, all information we get from an approval record is the approval comments and approver. Hence, the approach uses a middle layer to see when an approval should be created ( from template) and creates them beforehand. This way only one approval activity is used in workflow to generate any number of approvals for one stage of an item. However, all details of the approvals are captured in the log. In short, we avoid creating N number of approvals and there by running a set of business rules N times and create N log entries from only one approval which is sent to different approvers.
A generic structure of this table has been provided in the table below.
TaskApproverOrderState of ApprovalStage of TaskComments Captured from approvalDescription of Approval
Task — Every task for which dynamic approval is created
Approver — Name of the user
Order — order of the approval
State of Approval — 'Not yet requested', 'Approved', 'Rejected' in sync with state of sysapproval_approver record.
Stage of task — the stage for which an approval is needed
Comments — Entries made in approval comments
Description — Optional…if approval needs a little more description
3.1.3Layer 3 — Workflow
The reason for placing workflow deep inside all the layers is that workflow should be stable enough to handle all types of requirements and conditions which keep coming. If it was exposed directly to requirements, there might be not just few but many re-design and re-works happening in the workflow. Also, workflow can be used to bring in other components which cannot be coupled elsewhere. Activities in workflow take their own time to execute and there will always be a maximum count of activities, after which the workflow cancels by itself. If approval-user activity is taken, it has very limited options directly available. Hence advanced script of approval-user is used to take inputs from layer2 directly. Hence, only one approval-user activity is called every time. Our workflow uses two scratchpad variables to store minimum order and maximum order. This approval-user is called from minimum order till maximum order. That is how, need for different approvals has been eliminated.
v) Components of Workflow Layer
•Approval — User
•Run Scripts to glide approver log
•IF condition to iterate all levels
vi)Description
Approval — user is an activity which is usually used to create an approval for one or many approvers. We use the advanced script to push our own approvers depending on the order of the approval for a particular task. So this activity runs for all orders logged for a task. i.e if there are 3 records logged in approval log for task RITM0019024, then this approval — user runs for order 10, 20 and 30 sequentially. For order 10, it generates 1 approval record, for order 20, sends this same record to the corresponding approver and so on. After 30, final state of the approval determines the flow.
Scripts are anyways needed to capture order and to get the approval type and approver name.
IF condition is where we increment the order by a constant difference and iterator until maximum order is reached.
3.1.4Bridges joining layers
There are two bridges which join these three layers.
The first one between layers 1 and 2 is a business rule which captures template, makes entries in the log for all tasks created or updated. This is maximum point to which requirements can reach. Beyond this, there will not be any rework coming from requirement changes. This will pass on inputs from layer1 to layer 2
The second bridge is nothing but the run-script which joins workflow and approval log. This bridge is always stable and not changed. Its job is to glide approval log and iterate over approval-user. This will pass on over inputs from layer2 to layer 3 which are finally implemented.
Apart from these 4 or 5 components, there is one business rule which synchronizes approval log and sysapproval_approver tables. Whenever a dynamic approval record's state changes in sysapprova_approver, the log entry is updated with comments and state.
3.2 How generic can this approach be
This approach is designed in such a way that a small customization in the component properties would be enough to make it work for any type of task like catalog request or change request. The layers would still remain the same, but number of run-scripts would increase or decrease depending on the table. It is the maximum for 'Requested Item' table as Service-Now had its own restrictions.
Customizations are to be done only to table names wherever needed. However, layer 1 and layer 2 remain the same for all types of tasks. In Workflow layer, as table on which this workflow runs is different, only properties should be changed.
4.Implementation — Scenario 1
4.1Problem Statement
Based on the price of the requested item, approvals should be directed to requestor's level1 manager, level2 manager, level3 manager…….level 9 manager. Out of which level3, level 5 and level7 managers' approvals need to be auto approved. A next level approval should be generated only if the present approval is approved.
4.2 Implementing 'dynamic approval' design for this scenario
Customizations are to be made for this design to function according to requirements. But the foundation remains the same.
Customizations for scenario 1:
Layer 1: u_dynamic_approval table
Screenshot 1: Design of the form
Screenshot 2: Customized the form by creating all required select boxes to capture requirements
Screenshot 3: Implementing up to four levels first. Out of this, Area Manager approval should be auto approved.
Layer 2: u_list_of_approver table
The table is designed to capture the task number, approver, order, stage of task which needs approval and state of approval. These are populated by a business rule biz_sc_intialApproverList, which runs before insertion of a task. No one should be able to edit this table, as the information should be constant.
Layer 3: Wf_specialItemApproval with a subflow Wf_ItemApproval
Few utilities can be customized and removed if not necessary. The activities which are vital are
Run Script: This script acts like a bridge between layer 2 and the inner most layer. It fetches data from u_list_of_approver and stores it in temporary scratchpad variables. The script is given below.
Script:
var minorder = 10;
var maxorder = 10;
var gr = new GlideRecord("u_list_of_approver");
gr.addQuery("u_task", current.sys_id);
gr.addQuery('u_stage', current.stage);
gr.query();
while (gr.next()) {
workflow.debug(minorder +':'+ maxorder +'in glide:' + gr.u_order);
if(gr.u_order > maxorder ){
maxorder = gr.u_order;
workflow.debug('maxorder changed to: ' + maxorder);
}
if(gr.u_order < minorder ){
minorder = gr.u_order;
workflow.debug('minorder changed to: ' + minorder);
}
}
workflow.scratchpad.currentorder = minorder;
workflow.scratchpad.actualorder = maxorder;
****************************End of run script*************************************
Subflow: Wf_ItemApproval is needed only if the approval needs to be a re-usable component for many other workflows. Or it can be replaced by an approval-user activity. This approval-user activity should have an advanced script which populates approver from the list_of_approver records.
At present, as the subflow is on requested item and as we do not have an effective way of passing variables from main workflow to its subflow directly, there is an additional glide in this approval-user. This additional script is not required for any table where 'Workflow Inputs' can be used. Ex: sc_request, change_request…etc.
Approval Script implemented for approval-user is given below.
********************************************************************************
Script:
var pastorder = 10;
var reqorder = 10;
var order = 0;
// Checking for approvals that have been completed
var gr = new GlideRecord("u_list_of_approver");
gr.addQuery("u_task", current.sys_id );
gr.addQuery("u_state", 'Approved');
gr.addQuery('u_stage', current.stage);
gr.query();
while (gr.next()) {
if(gr.u_order > order){
pastorder = gr.u_order;
}
}
// Checking for the next approval in queue
var gr = new GlideRecord("u_list_of_approver");
gr.addQuery("u_task", current.sys_id );
gr.addQuery("u_state", 'Not Yet Requested');
gr.addQuery('u_stage', current.stage);
gr.query();
while (gr.next()) {
if(gr.u_order < reqorder){
reqorder = gr.u_order;
}
}
/* Validating if past order and required order are correct…Indirectly checking if u_list_of_approvers hasn't been changed */
if(reqorder == (pastorder + 10) || reqorder == 10 ){
var gr = new GlideRecord("u_list_of_approver");
gr.addQuery("u_task", current.sys_id );
gr.addQuery('u_stage', current.stage);
gr.addQuery("u_order", reqorder );
gr.query();
while (gr.next()){
// Pushing the approver dynamically
answer.push(gr.u_approver);
}
}
else {
}
End of Approval Script
IF condition: checks the result of the subflow. 'Approved', 'Rejected' or 'Sent Back' ('send back' option is out-of-scope for this document.)
IF condition to check if approvals are needed any more: This script controls the iterations.
**************************************************************************
Script:
// This script needs to set answer to 'yes' or 'no' to indicate the state of the activity.
//
// For example,
//
workflow.scratchpad.currentorder = workflow.scratchpad.currentorder + 10 ;
answer = ifScript();
//
function ifScript() {
if ( workflow.scratchpad.currentorder <= workflow.scratchpad.actualorder ) {
return 'yes';
}
else {
return 'no';
}
}
**********************************************************************************
Business Rule: Biz_sc_initialApproverList
Connects: u_dynamic_approval & u_list_of_approver
Table: Sc_req_item
Script:
if(current.operation() == 'insert'){
fnAddApprovers();
}
function fnAddApprovers(){
var gr = new GlideRecord("u_dynamic_approval");
gr.addQuery("u_catalog_item", current.cat_item);
gr.query();
while(gr.next()) {
// There are entries made in dynamic approval template for this catalog item.
// Hence add one by one to the log.
var gr1 = new GlideRecord("u_list_of_approver");
gr1.initialize();
gr1.u_stage = current.stage;
gr1.u_task = current.sys_id;
gr1.u_state = 'Not Yet Requested';
gr1.u_order = gr.u_order;
/************ The way an approver/ approval group is given to list of approvers is
************* something which needs customization********************************/
if(gr.u_type_of_approval == 'Role from Hierarchy'){
if( gr.u_hierarchical_role == 'Group Manager'){
gr1.u_approver = current.request.requested_for.u_team.manager;
}
else if( gr.u_hierarchical_role == 'Area Manager'){
gr1.u_approver = current.request.requested_for.u_team.u_area.u_manager;
}
else if( gr.u_hierarchical_role == 'Department Manager'){
gr1.u_approver = current.request.requested_for.u_team.u_area.u_department.u_manager;
}
else {
gr1.u_approver = current.request.requested_for.manager;
}
}
else if(gr.u_type_of_approval == 'Auto Approval'){
gr1.u_approver = current.request.requested_for.manager;
/* var approval = new GlideRecord('sysapproval_approver');
approval.initialize();
approval.sysapproval = current.sys_id;
approval.state = 'not_required';
approval.approver = current.request.requested_for.manager;
approval.due_date = current.due_date;
approval.insertWithReferences();*/
gr1.u_state = 'Auto Approved';
}
/* else if(gr.u_type_of_approval == 'Fetch from SRF'){
var value=gr.u_variable.name;
gs.addInfoMessage('value is '+value);
gs.addInfoMessage('variable '+current.variable_pool.value);
var objRecord = new GlideRecord('sc_item_option_mtom');
objRecord.addQuery('request_item.sys_id',current.sys_id);
objRecord.addQuery('sc_item_option.item_option_new.name','slt_cbs_shortName');
objRecord.query();
gs.addInfoMessage('row count:'+objRecord.getRowCount());
while(objRecord.next()){
gs.addInfoMessage(objRecord.sc_item_option.value);
}
}
*/else {
}
/*Before insertion, all that has to be checked is if our item falls in the price range */
if(gr.u_price_required == true ){
if(current.price >= gr.u_minimum_price){
gr1.insert();
return ;
}
}
else {
// If price_required fields is unchecked, insert the approval anyway.
gr1.insert ();
return ;
}
}
}
***********************End of Biz_sc_initialApproverList********************************
Business Rule: Biz_sc_updateApproverList
Connects: sysapproval_approver & u_list_of_approver
Table: Sc_req_item
Run: Before Update
Script:
var gr = new GlideRecord("u_list_of_approver");
gr.addQuery("u_task", current.sysapproval);
gr.addQuery("u_approver",current.approver);
gr.query();
if (gr.next()) {
gr.u_state = current.state; // cascading state of approval to list_of_approver
gr.update();
}
else {
}
***********************End of Biz_sc_updateApproverList********************************
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.