Name
global.ChangePolicyApprovalActivitySNC
Description
Provides the functionality for the Change Approval Policy activity. To modify behavior, use the provided ChangePolicyApprovalActivity extension to override functions defined within this Script Include.
Script
var ChangePolicyApprovalActivitySNC = Class.create();
ChangePolicyApprovalActivitySNC.prototype = Object.extendsObject(global.WFActivityHandler, {
APPROVE: "approve",
APPROVED: "approved",
CANCELLED: "cancelled",
DUPLICATE: "duplicate",
FINISHED: "finished",
MANDATORY: "mandatory",
NOT_REQUIRED: "not_required",
NOT_REQUESTED: "not requested",
REJECTED: "rejected",
REJECT: "reject",
REQUESTED: "requested",
SKIPPED: "skipped",
SYSAPPROVAL_APPROVER: "sysapproval_approver",
SYSAPPROVAL_GROUP: "sysapproval_group",
WAITING: "waiting",
initialize: function(_gr, _gs, _activity, _context, _workflow) {
global.WFActivityHandler.prototype.initialize.call(this);
this._gr = _gr || current;
this._gs = _gs || gs;
this._activity = _activity || activity;
this._activity.result = null;
this._context = _context || context;
this._workflow = _workflow || workflow;
this.setDatesFlag = false;
this.approvalUtils = new global.WorkflowApprovalUtils();
this.pendingStates = [this.NOT_REQUESTED, this.NOT_REQUIRED, this.REQUESTED];
this.autoApprovalType = {};
this.autoApprovalType[this.APPROVE] = this.APPROVED;
this.autoApprovalType[this.REJECT] = this.REJECTED;
this.log = new GSLog("com.snc.change_management.policy.approval.log", this.type).setLog4J();
},
onExecute: function() {
this._setDueDate("");
var chgPolAppGR = this._activity.vars.approval_policy.getRefRecord();
if (!chgPolAppGR.isValidRecord()) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[onExecute] Change Approval Policy record not found. Applying default behavior and rejecting this Change Request");
this._defaultReject(this._activity, this._gr, this._gs.getMessage("Change Approval Policy not found. Change Request has been rejected ({0}).", [this._activity.activity.name.toString()]));
return;
}
if (parseInt(chgPolAppGR.getValue("active")) !== 1) { // if not active
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[onExecute] Change Approval Policy record is inactive. Applying default behavior and rejecting this Change Request");
this._defaultReject(this._activity, this._gr, this._gs.getMessage("{0} is inactive. Change Request has been rejected ({1}).", [chgPolAppGR.getDisplayValue(), this._activity.activity.name.toString()]));
return;
}
// Wait for workflow to be canceled by "Cancel Workflows Upon Cancellation" business rule on task table
if (this._gr.state.changesTo(this._activity.vars.canceled_state)) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[onExecute] Change Request state has changed to Cancel. Applying default behavior and waiting for workflow to be cancelled");
this._activity.state = this.WAITING;
return;
}
var chgAppPolicy = new global.ChangePolicy(chgPolAppGR);
var decisions = chgAppPolicy.evaluatePolicy(this._gr, this.runScript(this._activity.vars.policy_input) || {});
if (!decisions || decisions.chg_approval_def.length === 0 || decisions.sys_decision_question.length === 0) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[onExecute] No Decisions matched this Change Request from the Change Approval Policy. Applying default behaviour and skipping this Change Approval Policy");
this._defaultSkip(this._activity, this._gr, true, this._gs.getMessage("No Decisions matched. {0} has been skipped ({1}).", [chgPolAppGR.getDisplayValue(), this._activity.activity.name.toString()]));
return;
}
var userApprovals = [];
var groupApprovals = [];
var autoApproval = false;
if ("first" === chgPolAppGR.getValue("execution")) {
// sys_decision_question's are ordered. Update collection to include first matched Decision only
decisions.sys_decision_question = decisions.sys_decision_question.slice(0,1);
// Update collection to include the first matched answer only
decisions.chg_approval_def = [decisions.sys_decision_question[0].answer];
}
var chgAppDefGR = new GlideRecord("chg_approval_def");
chgAppDefGR.addQuery("sys_id", decisions.chg_approval_def);
chgAppDefGR.query();
while (chgAppDefGR.next()) {
var chgAppDef = new global.ChangeApprovalDef(chgAppDefGR);
var approvalObjArr = chgAppDef.getApprovals(this._gr);
if (approvalObjArr.length === 0)
continue;
if (this.autoApprovalType[approvalObjArr[0].approval_action])
autoApproval = true;
if ("group" === approvalObjArr[0].target_type)
groupApprovals = groupApprovals.concat(approvalObjArr);
else if ("user" === approvalObjArr[0].target_type)
userApprovals = userApprovals.concat(approvalObjArr);
}
if (userApprovals.length === 0 && groupApprovals.length === 0) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[onExecute] No Approvals could be generated from matched Decisions. Applying default behaviour and skipping this Change Approval Policy");
this._defaultSkip(this._activity, this._gr, true, this._gs.getMessage("No approvals were generated from matched Decisions. {0} has been skipped ({1}).", [chgPolAppGR.getDisplayValue(), this._activity.activity.name.toString()]));
return;
}
// Log each approval in chg_policy_applied
for (var j = 0; j < decisions.sys_decision_question.length; j++)
this.logChgPolApplied(this._gr.getUniqueValue(), this._activity.vars.approval_policy, decisions.sys_decision_question[j].sys_id, decisions.sys_decision_question[j].answer);
var userApprovalDefs = this._createUserApprovals(userApprovals, autoApproval);
var groupApprovalDefs = this._createGroupApprovals(groupApprovals, autoApproval);
if (userApprovalDefs && Object.keys(userApprovalDefs).length === 0 && groupApprovalDefs && Object.keys(groupApprovalDefs).length === 0) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[onExecute] No Approvals were generated. Applying default behaviour and skipping this Change Approval Policy");
this._defaultSkip(this._activity, this._gr, false, this._gs.getMessage("No approvals were generated from matched Decisions. {0} has been skipped ({1}).", [chgPolAppGR.getDisplayValue(), this._activity.activity.name.toString()]));
return;
}
// Save the list of Change Approvals that have been generated
this._activity.scratchpad.user_approvals = userApprovalDefs;
this._activity.scratchpad.group_approvals = groupApprovalDefs;
this._activity.state = this.WAITING;
// Check if the activity should finish
this._onUpdate();
},
onDetermineApprovalState: function() {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[onDetermineApprovalState] Checking approval state");
this._onUpdate();
},
// The task has changed, see if we need to update our approval state
onUpdate: function() {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[onUpdate] Checking approval state");
// Evaluate the finish_condition, if true set the state and result to finished.
if (this._finishActivity())
return;
this._onUpdate();
},
onCancel: function() {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[onCancel] Cancelling Change Approval Policy activity");
var changeSysId = this._gr.getUniqueValue();
this._setPendingUserApprovalsByIds("", changeSysId, this.CANCELLED);
this._setPendingGroupApprovalsByIds("", changeSysId, this.CANCELLED);
this._activity.state = this.CANCELLED;
this._activity.result = this.CANCELLED;
},
logChgPolApplied: function(chgReqId, chgPolId, decQueId, decAnsId) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[logChgPolApplied] Log for Change: " + chgReqId + ", Policy: " + chgPolId + ", Question: " + decQueId + ", Answer: " + decAnsId);
var chgPolAppGR = new GlideRecord("chg_policy_applied");
chgPolAppGR.setValue("change_request", chgReqId);
chgPolAppGR.setValue("chg_policy", chgPolId);
chgPolAppGR.setValue("sys_decision_question", decQueId);
chgPolAppGR.setValue("chg_policy_action", decAnsId);
chgPolAppGR.setValue("activity_name", this._activity.activity.name.toString());
return chgPolAppGR.insert();
},
// override this to provide the proper stage state
getFinalStageState: function() {
return new WFApprovalStages().getApprovalState(this._gr, this._gr.stage, this._activity);
},
_onUpdate: function() {
var state = this._determineOverallState();
if (state && (state !== this.REQUESTED)) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_onUpdate] State has changed to " + state + ". Marking remaining approvals as no longer required");
// Set all pending approvals to not required.
this.approvalUtils.setPendingUserApprovalsByIds(Object.keys(this._activity.scratchpad.user_approvals), this.NOT_REQUIRED);
this.approvalUtils.setPendingGroupApprovalsByIds(Object.keys(this._activity.scratchpad.group_approvals), this.NOT_REQUIRED);
var chgPolAppGR = this._activity.vars.approval_policy.getRefRecord();
if (chgPolAppGR.isValidRecord()) {
var comment = "";
if (state === this.APPROVED)
comment = this._gs.getMessage("Change Request has been approved by {0} ({1}).", [chgPolAppGR.getDisplayValue(), this._activity.activity.name.toString()]);
else if (state === this.REJECTED)
comment = this._gs.getMessage("Change Request has been rejected by {0} ({1}).", [chgPolAppGR.getDisplayValue(), this._activity.activity.name.toString()]);
if (comment)
this.approvalUtils.addApprovalHistoryGR(this._gr, comment);
}
this._activity.state = this.FINISHED;
this._activity.result = state;
} else if ((state === this.REQUESTED) && (this._activity.result !== state)) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_onUpdate] Activity is in the waiting state as approvals are requested");
// changed back to requested state (unapprove can do this)
this._activity.state = this.WAITING;
this._activity.result = "";
}
},
// Determine the overall state of the approvals (approved, rejected or "")
_determineOverallState: function() {
var userState = this._determineUserApprovalState();
var groupState = this._refreshGroupApprovals(userState);
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_determineOverallState] userState: " + userState + ", groupState: " + groupState);
if (this.REJECTED === userState || this.REJECTED === groupState)
return this.REJECTED;
else if (this.MANDATORY === userState || this.MANDATORY === groupState)
return this.REQUESTED;
else if (this.APPROVED === userState || this.APPROVED === groupState)
return this.APPROVED;
else if (userState === groupState)
return userState;
else
return this.REQUESTED;
},
// Refreshes the overall state of group approvals
_refreshGroupApprovals: function(userApprovalState) {
var groupApprovalDefs = this._activity.scratchpad.group_approvals;
var groupApprovalIds = Object.keys(groupApprovalDefs);
var appGroupGR = new GlideRecord("sysapproval_group");
appGroupGR.addQuery("sys_id", groupApprovalIds);
appGroupGR.query();
// Case where approvals have been deleted and cannot be found, default skip
if (!appGroupGR.hasNext()) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_refreshGroupApprovals] Group approvals were not found. Skipping group approval check.");
return this.SKIPPED;
}
// Identify individual group approval states
var rejectedChange = false;
var haveApproval = false;
while (appGroupGR.next()) {
var approvalId = appGroupGR.getUniqueValue();
var currentState = this._refreshGroupApprovalState(appGroupGR, groupApprovalDefs[approvalId].wait_for, groupApprovalDefs[approvalId].percentage);
groupApprovalDefs[approvalId].approval_state = currentState;
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_refreshGroupApprovals] Group approval: " + appGroupGR.getValue("number") + "|" + approvalId +
", state: " + currentState +
", mandatory: " + groupApprovalDefs[approvalId].mandatory);
// Hard reject. If any group rejects, it's out of here. We need to make all other groups no longer required after this point.
if (currentState === this.REJECTED)
rejectedChange = true;
// A group has approved but we have to parse everything to determine the change is approved.
if (currentState === this.APPROVED)
haveApproval = true;
}
// Now we have determined the individual group approval states so we can decide on what to do overall
var approvalRequired = false;
var mandatoryApproval = false;
for (var i = 0; i < groupApprovalIds.length; i++) {
var gaId = groupApprovalIds[i];
if (groupApprovalDefs[gaId].approval_state === this.REQUESTED) {
// We only mark approvals as "no longer required" when we are ready to proceed with the policy.
// Both user and group approvals no longer have an overall state of Requested/Mandatory
if (rejectedChange
|| (haveApproval
&& groupApprovalDefs[gaId].mandatory !== "true"
&& userApprovalState !== this.REQUESTED
&& userApprovalState !== this.MANDATORY)) {
this.approvalUtils.setPendingGroupApprovalsByIds(gaId, this.NOT_REQUIRED);
groupApprovalDefs[gaId].approval_state = this.NOT_REQUIRED;
}
//If we're still waiting for a mandatory approval, haveApproval is false
if (groupApprovalDefs[gaId].mandatory === "true") {
haveApproval = false;
mandatoryApproval = true;
}
// If we have a requested group approval which has made it this far, approval is required
approvalRequired = true;
}
}
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_refreshGroupApprovals] State:\n" + JSON.stringify(groupApprovalDefs, null, 3) + "\n[/_refrehGroupApprovals]");
// If the change has been rejected
if (rejectedChange)
return this.REJECTED;
// If we already have approval
if (haveApproval)
return this.APPROVED;
// If approval is not required
if (!approvalRequired)
return this.NOT_REQUIRED;
// If we are waiting for a mandatory approval
if (mandatoryApproval)
return this.MANDATORY;
//Otherwise it's requested
return this.REQUESTED;
},
// Determine approved/rejected states for the given approvals within a group and update state if required.
_refreshGroupApprovalState: function(appGroupGR, waitFor, percentageRequired) {
function updateGroupState(state, user) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_refreshGroupApprovalState] Updating Group approval : " + appGroupGR.getValue("number") + "|" + appGroupGR.getUniqueValue() +
", prevState: " + appGroupGR.getValue("approval") +
", newState: " + state);
appGroupGR.setValue("approval", state);
if (state !== this.REQUESTED && user)
appGroupGR.setValue("approval_user", user);
else
appGroupGR.setValue("approval_user", "");
if (!appGroupGR.update())
return null;
//Approval History Messaging
var commentParams = [appGroupGR.assignment_group.getDisplayValue(),
user ? this.approvalUtils.getUserName(user) : "",
this._activity.activity.name.toString()
];
if (state === this.APPROVED)
this.approvalUtils.addApprovalHistoryGR(this._gr, user ?
this._gs.getMessage("Group approval for {0} approved by user {1} ({2}).", commentParams) :
this._gs.getMessage("Group approval for {0} approved by all users ({2}).", commentParams));
if (state === this.REJECTED)
this.approvalUtils.addApprovalHistoryGR(this._gr, user ?
this._gs.getMessage("Group approval for {0} rejected by user {1} ({2}).", commentParams) :
this._gs.getMessage("Group approval for {0} rejected by all users ({2}).", commentParams));
return state;
}
var currentState = appGroupGR.getValue("approval");
// Unless the currentState is requested we don't need to do anything.
// We just return duplicate, cancelled, not_requested, not_required, approved, rejected
if (currentState !== this.REQUESTED)
return currentState;
var ret = this.approvalUtils.getUserGroupApprovalCounts(appGroupGR.getUniqueValue());
// Case where approvals have been deleted and cannot be found, default skip
if (ret.counts["total"] === 0) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_refreshGroupApprovalState] Group approval: " + appGroupGR.getValue("number") + "|" + appGroupGR.getUniqueValue() +
", state: Skipped" +
", member approvals were not found");
return this.SKIPPED;
}
//Compensate for unneeded approval records.
ret.counts["total"] -= ret.counts[this.NOT_REQUIRED];
ret.counts['total'] -= ret.counts[this.DUPLICATE];
ret.counts["total"] -= ret.counts[this.CANCELLED];
// No valid approvals
if (ret.counts["total"] === 0) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_refreshGroupApprovalState] Group approval: " + appGroupGR.getValue("number") + "|" + appGroupGR.getUniqueValue() +
", state: " + currentState +
", member approvals are not_required, duplicate or cancelled");
return currentState;
}
// Hard rejection
if (ret.counts[this.REJECTED] > 0)
return updateGroupState.call(this, this.REJECTED, ret.approvalIDs[this.REJECTED][0]);
// Percentage of users approval
if (waitFor === "percent" && this._percentageApproved(ret.counts, percentageRequired))
return updateGroupState.call(this, this.APPROVED);
// First in group approval
var percentageFirstResponse = (waitFor === "percent" && (!percentageRequired || percentageRequired <= 0));
if ((waitFor === "first" || percentageFirstResponse) && ret.counts[this.APPROVED] > 0) {
if (updateGroupState.call(this, this.APPROVED, ret.approvalIDs[this.APPROVED][0]))
this.approvalUtils.setPendingGroupApprovalsByIds(appGroupGR.getUniqueValue(), this.NOT_REQUIRED);
return this.APPROVED;
}
// All in group approval
var percentageAllResponse = (waitFor === "percent" && percentageRequired && percentageRequired >= 100);
if ((waitFor === "all" || percentageAllResponse) && ret.counts[this.APPROVED] === ret.counts["total"])
return updateGroupState.call(this, this.APPROVED);
return currentState; // No state change if none of the above are fulfilled
},
_percentageApproved: function(approvalCounts, percentageRequired) {
// Delegate to first response check
if (!percentageRequired || percentageRequired <= 0)
return false;
// Delegate to all response check
if (percentageRequired >= 100)
return false;
var total = parseInt(approvalCounts["total"]);
var approved = parseInt(approvalCounts[this.APPROVED]);
var calculatedPercentage = (approved / total) * 100;
if (calculatedPercentage < percentageRequired)
return false;
return true;
},
_determineUserApprovalState: function() {
var state = this.REQUESTED;
var userApprovalDefs = this._activity.scratchpad.user_approvals;
var userApprovalIds = Object.keys(userApprovalDefs);
var userApprovalGR = new GlideRecord("sysapproval_approver");
var mandatory = false;
userApprovalGR.addQuery("sys_id", userApprovalIds);
userApprovalGR.query();
// Case where approvals have been deleted and cannot be found, default skip
if (!userApprovalGR.hasNext()) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_determineUserApprovalState] User approval not found. Skipping user approval check.");
return this.SKIPPED;
}
while (userApprovalGR.next()) {
var approvalId = userApprovalGR.getUniqueValue();
userApprovalDefs[approvalId].approval_state = userApprovalGR.getValue("state");
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_determineUserApprovalState] User approval: " + approvalId +
", state: " + userApprovalDefs[approvalId].approval_state +
", mandatory: " + userApprovalDefs[approvalId].mandatory);
if (this.REJECTED === userApprovalDefs[approvalId].approval_state) {
state = this.REJECTED;
break;
} else if (this.APPROVED === userApprovalDefs[approvalId].approval_state)
state = this.APPROVED;
else if (this.SKIPPED === userApprovalDefs[approvalId].approval_state)
state = this.SKIPPED;
else if ("true" === userApprovalDefs[approvalId].mandatory && this.REQUESTED === userApprovalDefs[approvalId].approval_state)
mandatory = true;
}
if (mandatory && state !== this.REJECTED)
state = this.MANDATORY;
return state;
},
_createUserApprovals: function(userApprovals, autoApproval) {
var userApprovalDefs = {};
var existingIds = this._getExistingUserApprovals();
for (var i = 0; i < userApprovals.length; i++) {
var state = this.REQUESTED;
var approvalId = "";
// If there is an auto-approval and this is not a mandatory approval, ensure state is set to NOT_REQUIRED
if (autoApproval && "true" !== userApprovals[i].mandatory)
state = this.NOT_REQUIRED;
// Auto-approval state is known
if (this.autoApprovalType[userApprovals[i].approval_action])
state = this.autoApprovalType[userApprovals[i].approval_action];
if (existingIds && existingIds[userApprovals[i].target_id]) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_createUserApprovals] Found an existing approval for user: " + userApprovals[i].target_id);
approvalId = existingIds[userApprovals[i].target_id];
this._setApprovalState(approvalId, state, this.SYSAPPROVAL_APPROVER);
} else {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_createUserApprovals] Create a new approval for user: " + userApprovals[i].target_id + ", state: " + state);
approvalId = this._createNewUserApproval(userApprovals[i].target_id, state);
}
if (approvalId) {
userApprovals[i].approval_state = state;
userApprovals[i].sysapproval_approver = approvalId;
userApprovalDefs[approvalId] = userApprovals[i];
}
}
return userApprovalDefs;
},
_createNewUserApproval: function(userId, state, approvalOrder) {
var userAppGR = new GlideRecord("sysapproval_approver");
userAppGR.initialize();
// When auto approving/rejecting an approval need to ensure unnecessary update events to the workflow do not occur
if (this.APPROVED === state || this.REJECTED === state)
userAppGR.setWorkflow(false);
var table = this._gr.getRecordClassName();
var guid = this._gr.getUniqueValue();
if (table != null && this.approvalUtils.isTask(table)) {
userAppGR.sysapproval = this._gr.sys_id;
userAppGR.sysapproval.setRefRecord(this._gr);
}
userAppGR.document_id = guid;
userAppGR.document_id.setRefRecord(this._gr);
userAppGR.source_table = table;
userAppGR.approver = userId;
var approvalColumn = this._activity.vars.approval_column;
userAppGR.approval_column = approvalColumn ? this.js(approvalColumn) : "approval";
var approvalHistory = this._activity.vars.approval_history;
userAppGR.approval_journal_column = approvalHistory ? this.js(approvalHistory) : "approval_history";
userAppGR.wf_activity = this._activity.activity.sys_id;
userAppGR.state = state;
if (approvalOrder)
userAppGR.order = approvalOrder;
userAppGR.expected_start.setValue(this.expected_start);
userAppGR.due_date.setValue(this.due_date);
return userAppGR.insert();
},
_getExistingUserApprovals: function() {
var ids = {};
var gr = new GlideRecord("sysapproval_approver");
gr.initialize();
// We work with the change_request table only, so we can depend on sysapproval field.
gr.addQuery("sysapproval", this._gr.sys_id);
gr.addQuery("wf_activity", this._activity.activity.sys_id);
gr.addQuery("state", "!=", this.CANCELLED);
gr.addNullQuery("group");
gr.query();
while (gr.next())
ids[gr.approver.toString()] = gr.sys_id.toString();
return ids;
},
_createGroupApprovals: function(groupApprovals, autoApproval) {
var groupApprovalDefs = {};
// Avoid recreating approvals for group members that already have them from this activity
var existingIds = this._getExistingGroupApprovals();
// Avoid creating more than one group approval for the same group
var createdGroupApprovals = {};
for (var i = 0; i < groupApprovals.length; i++) {
var state = this.REQUESTED;
var groupAppGR = null;
var approvalId = "";
// If there is an auto-approval and this is not a mandatory approval, ensure state is set to NOT_REQUIRED
if (autoApproval && "true" !== groupApprovals[i].mandatory)
state = this.NOT_REQUIRED;
if (existingIds && existingIds[groupApprovals[i].target_id]) {
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_createUserApprovals] Found an existing approval for group: " + groupApprovals[i].target_id);
approvalId = existingIds[groupApprovals[i].target_id];
this._setApprovalState(approvalId, state, this.SYSAPPROVAL_GROUP);
} else {
// Check if we've already created an approval for this group
if (createdGroupApprovals[groupApprovals[i].target_id])
continue;
groupAppGR = this._createNewGroupApproval(groupApprovals[i].target_id, state);
if (!groupAppGR)
continue;
// Flag that we have created an approval for the group
createdGroupApprovals[groupApprovals[i].target_id] = true;
// Create associated user approvals because the SNC - Create user approvals for group
// business rule does not run when the state is No longer required; auto-approval scenario.
if (this.NOT_REQUIRED === state)
this.approvalUtils.createUserApprovals(groupAppGR);
approvalId = groupAppGR.getUniqueValue();
}
if (approvalId) {
groupApprovals[i].approval_state = state;
groupApprovals[i].sysapproval_group = approvalId;
groupApprovalDefs[approvalId] = groupApprovals[i];
}
}
return groupApprovalDefs;
},
_createNewGroupApproval: function(groupId, state, approvalOrder) {
// make sure there are users of the group before creating it
var ids = this.approvalUtils.getMembersOfGroup(groupId);
if (ids.length == 0)
return "";
var groupAppGR = new GlideRecord("sysapproval_group");
groupAppGR.assignment_group = groupId;
groupAppGR.parent = this._gr.sys_id;
groupAppGR.parent.setRefRecord(this._gr);
groupAppGR.wf_activity = this._activity.activity.sys_id;
groupAppGR.approval = state;
if (this.NOT_REQUIRED === state)
groupAppGR.setWorkflow(false);
if (approvalOrder)
groupAppGR.order = approvalOrder;
groupAppGR.expected_start.setValue(this.expected_start);
groupAppGR.due_date.setValue(this.due_date);
if (!groupAppGR.insert())
return null;
if (this.log.atLevel(GSLog.DEBUG))
this.log.debug("[_createNewGroupApproval] Created Group Approval: " + groupAppGR.getValue("number") + "|" + groupAppGR.getUniqueValue() +
", Change: " + this._gr.getValue("number") + "|" + this._gr.getUniqueValue() +
", Group: " + groupAppGR.assignment_group.getRefRecord().getDisplayValue() + "|" + groupId +
", State: " + state +
", Workflow Activity: " + groupAppGR.getValue("wf_activity"));
return groupAppGR;
},
_getExistingGroupApprovals: function() {
var ids = {};
var groupAppGR = new GlideRecord("sysapproval_group");
groupAppGR.initialize();
groupAppGR.addQuery("parent", this._gr.sys_id);
groupAppGR.addQuery("wf_activity", this._activity.activity.sys_id);
groupAppGR.addQuery("approval", "!=", this.CANCELLED);
groupAppGR.query();
while (groupAppGR.next())
ids[groupAppGR.assignment_group.toString()] = groupAppGR.sys_id.toString();
return ids;
},
/**
* Set the state of the approval to the specified state
*/
_setApprovalState: function(approvalId, state, table) {
if (this._workflow.scratchpad.maintainStateFlag == true)
return;
var appGR = new GlideRecord(table);
if (!appGR.get(approvalId))
return;
appGR.setValue(this.SYSAPPROVAL_GROUP === appGR.getRecordClassName() ? "approval" : "state", state);
if (this.setDatesFlag) {
appGR.expected_start.setValue(this.expected_start);
appGR.due_date.setValue(this.due_date);
}
appGR.update();
},
_setDueDate: function(startAt) {
this.expected_start = new GlideDateTime();
if (startAt)
this.expected_start.setValue(startAt);
var wd = new WorkflowDuration();
wd.setActivity(this);
wd.setStartDateTime(startAt);
wd.setWorkflow(this._context.schedule, this._context.timezone);
wd.calculate(this._activity.vars.__var_record__);
this.due_date = new GlideDateTime(wd.getEndDateTime());
this.duration = wd.getTotalSeconds() * 1000;
},
_setPendingUserApprovalsByIds: function(approvalSysIds, changeSysId, state) {
var gr = new GlideRecord("sysapproval_approver");
if (approvalSysIds)
gr.addQuery("sys_id", approvalSysIds);
gr.addQuery("sysapproval", changeSysId);
gr.addQuery("document_id", changeSysId);
gr.addQuery("wf_activity", this._activity.activity.sys_id + "");
gr.addQuery("state", this.pendingStates);
gr.setValue("state", state);
gr.updateMultiple();
},
_setPendingGroupApprovalsByIds: function(approvalSysIds, changeSysId, state) {
var gr = new GlideRecord("sysapproval_group");
if (approvalSysIds)
gr.addQuery("sys_id", approvalSysIds);
gr.addQuery("parent", changeSysId);
gr.addQuery("wf_activity", this._activity.activity.sys_id + "");
gr.addQuery("approval", this.pendingStates);
gr.setValue("approval", state);
gr.updateMultiple();
},
_defaultSkip: function(providedActivity, gr, logchgPolApplied, message) {
providedActivity.state = this.FINISHED;
providedActivity.result = this.SKIPPED;
if (logchgPolApplied)
this.logChgPolApplied(gr.getUniqueValue(), providedActivity.vars.approval_policy, "", "");
this.approvalUtils.addApprovalHistoryGR(gr, message);
},
_defaultReject: function(providedActivity, gr, message) {
providedActivity.state = this.FINISHED;
providedActivity.result = this.REJECTED;
this.approvalUtils.addApprovalHistoryGR(gr, message);
},
_finishActivity: function() {
var condition = "" + this._activity.vars.finish_condition;
if (!condition)
return false;
// Provide change_request, finish_condition, matchAll, caseSensitive
var stop = SNC.Filter.checkRecord(this._gr, condition, true, false);
if (stop) {
this._activity.state = this.FINISHED;
this._activity.result = this.FINISHED;
}
return stop;
},
type: 'ChangePolicyApprovalActivitySNC'
});
Sys ID
948fdf26535123008ef67c2c0fc587ca