approval-workflows

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Approval Workflows for ServiceNow

ServiceNow 审批工作流

Approval workflows route records through configurable approval chains.
审批工作流可通过可配置的审批链来流转记录。

Approval Architecture

审批架构

Record (change_request, sc_req_item, etc.)
Approval Rules (sysapproval_rule)
Approval Records (sysapproval_approver)
    ↓ Approve/Reject
Record State Updated
Record (change_request, sc_req_item, etc.)
Approval Rules (sysapproval_rule)
Approval Records (sysapproval_approver)
    ↓ Approve/Reject
Record State Updated

Key Tables

核心表

TablePurpose
sysapproval_approver
Individual approval records
sysapproval_group
Group approval configuration
sysapproval_rule
Approval rules
sys_approval_workflow
Approval workflow stages
表名用途
sysapproval_approver
单个审批记录
sysapproval_group
审批组配置
sysapproval_rule
审批规则
sys_approval_workflow
审批工作流阶段

Approval Rules (ES5)

审批规则(ES5)

Create Approval Rule

创建审批规则

javascript
// Create approval rule (ES5 ONLY!)
var rule = new GlideRecord('sysapproval_rule');
rule.initialize();

// Rule identification
rule.setValue('name', 'Change Request Manager Approval');
rule.setValue('table', 'change_request');
rule.setValue('order', 100);
rule.setValue('active', true);

// Conditions - when rule applies
rule.setValue('conditions', 'type=normal^priority<=2');

// Approver type
rule.setValue('approver', 'manager');  // manager, group, user, script
// For user: rule.setValue('approver_user', userSysId);
// For group: rule.setValue('approver_group', groupSysId);

// Approval type
rule.setValue('approval_type', 'and');  // and (all must approve), or (any can approve)

// Wait for previous level
rule.setValue('wait_for', true);

rule.insert();
javascript
// Create approval rule (ES5 ONLY!)
var rule = new GlideRecord('sysapproval_rule');
rule.initialize();

// Rule identification
rule.setValue('name', 'Change Request Manager Approval');
rule.setValue('table', 'change_request');
rule.setValue('order', 100);
rule.setValue('active', true);

// Conditions - when rule applies
rule.setValue('conditions', 'type=normal^priority<=2');

// Approver type
rule.setValue('approver', 'manager');  // manager, group, user, script
// For user: rule.setValue('approver_user', userSysId);
// For group: rule.setValue('approver_group', groupSysId);

// Approval type
rule.setValue('approval_type', 'and');  // and (all must approve), or (any can approve)

// Wait for previous level
rule.setValue('wait_for', true);

rule.insert();

Script-Based Approver Selection

基于脚本选择审批人

javascript
// Approval rule with script (ES5 ONLY!)
var rule = new GlideRecord('sysapproval_rule');
rule.initialize();
rule.setValue('name', 'Cost-Based Approval');
rule.setValue('table', 'sc_req_item');
rule.setValue('approver', 'script');

// Script to determine approvers (ES5 ONLY!)
rule.setValue('script',
    '(function getApprovers(current) {\n' +
    '    var approvers = [];\n' +
    '    var cost = parseFloat(current.getValue("estimated_cost")) || 0;\n' +
    '    \n' +
    '    // Manager approval for all\n' +
    '    var caller = current.requested_for.getRefRecord();\n' +
    '    if (caller.manager) {\n' +
    '        approvers.push(caller.manager.toString());\n' +
    '    }\n' +
    '    \n' +
    '    // Director approval for > $5000\n' +
    '    if (cost > 5000) {\n' +
    '        var director = getDirector(caller);\n' +
    '        if (director) approvers.push(director);\n' +
    '    }\n' +
    '    \n' +
    '    // VP approval for > $25000\n' +
    '    if (cost > 25000) {\n' +
    '        var vp = getVP(caller);\n' +
    '        if (vp) approvers.push(vp);\n' +
    '    }\n' +
    '    \n' +
    '    return approvers;\n' +
    '})(current);'
);

rule.insert();
javascript
// Approval rule with script (ES5 ONLY!)
var rule = new GlideRecord('sysapproval_rule');
rule.initialize();
rule.setValue('name', 'Cost-Based Approval');
rule.setValue('table', 'sc_req_item');
rule.setValue('approver', 'script');

// Script to determine approvers (ES5 ONLY!)
rule.setValue('script',
    '(function getApprovers(current) {\n' +
    '    var approvers = [];\n' +
    '    var cost = parseFloat(current.getValue("estimated_cost")) || 0;\n' +
    '    \n' +
    '    // Manager approval for all\n' +
    '    var caller = current.requested_for.getRefRecord();\n' +
    '    if (caller.manager) {\n' +
    '        approvers.push(caller.manager.toString());\n' +
    '    }\n' +
    '    \n' +
    '    // Director approval for > $5000\n' +
    '    if (cost > 5000) {\n' +
    '        var director = getDirector(caller);\n' +
    '        if (director) approvers.push(director);\n' +
    '    }\n' +
    '    \n' +
    '    // VP approval for > $25000\n' +
    '    if (cost > 25000) {\n' +
    '        var vp = getVP(caller);\n' +
    '        if (vp) approvers.push(vp);\n' +
    '    }\n' +
    '    \n' +
    '    return approvers;\n' +
    '})(current);'
);

rule.insert();

Managing Approvals (ES5)

审批管理(ES5)

Create Approval Manually

手动创建审批

javascript
// Create approval record (ES5 ONLY!)
function createApproval(recordSysId, approverSysId, source) {
    var approval = new GlideRecord('sysapproval_approver');
    approval.initialize();
    approval.setValue('sysapproval', recordSysId);
    approval.setValue('approver', approverSysId);
    approval.setValue('state', 'requested');
    approval.setValue('source_table', source.table || '');

    return approval.insert();
}
javascript
// Create approval record (ES5 ONLY!)
function createApproval(recordSysId, approverSysId, source) {
    var approval = new GlideRecord('sysapproval_approver');
    approval.initialize();
    approval.setValue('sysapproval', recordSysId);
    approval.setValue('approver', approverSysId);
    approval.setValue('state', 'requested');
    approval.setValue('source_table', source.table || '');

    return approval.insert();
}

Process Approval Decision

处理审批决策

javascript
// Approve or reject (ES5 ONLY!)
function processApprovalDecision(approvalSysId, decision, comments) {
    var approval = new GlideRecord('sysapproval_approver');
    if (!approval.get(approvalSysId)) {
        return { success: false, message: 'Approval not found' };
    }

    // Validate current state
    if (approval.getValue('state') !== 'requested') {
        return { success: false, message: 'Approval already processed' };
    }

    // Validate approver
    if (approval.getValue('approver') !== gs.getUserID()) {
        if (!canActOnBehalf(approval.getValue('approver'))) {
            return { success: false, message: 'Not authorized to approve' };
        }
    }

    // Set decision
    approval.setValue('state', decision);  // 'approved' or 'rejected'
    approval.setValue('comments', comments);
    approval.setValue('actual_approver', gs.getUserID());
    approval.update();

    // Update parent record approval status
    updateParentApprovalStatus(approval.getValue('sysapproval'));

    return {
        success: true,
        decision: decision,
        record: approval.sysapproval.getDisplayValue()
    };
}

function canActOnBehalf(originalApproverId) {
    // Check delegation
    var delegation = new GlideRecord('sys_user_delegate');
    delegation.addQuery('user', originalApproverId);
    delegation.addQuery('delegate', gs.getUserID());
    delegation.addQuery('starts', '<=', new GlideDateTime());
    delegation.addQuery('ends', '>=', new GlideDateTime());
    delegation.addQuery('approvals', true);
    delegation.query();
    return delegation.hasNext();
}
javascript
// Approve or reject (ES5 ONLY!)
function processApprovalDecision(approvalSysId, decision, comments) {
    var approval = new GlideRecord('sysapproval_approver');
    if (!approval.get(approvalSysId)) {
        return { success: false, message: 'Approval not found' };
    }

    // Validate current state
    if (approval.getValue('state') !== 'requested') {
        return { success: false, message: 'Approval already processed' };
    }

    // Validate approver
    if (approval.getValue('approver') !== gs.getUserID()) {
        if (!canActOnBehalf(approval.getValue('approver'))) {
            return { success: false, message: 'Not authorized to approve' };
        }
    }

    // Set decision
    approval.setValue('state', decision);  // 'approved' or 'rejected'
    approval.setValue('comments', comments);
    approval.setValue('actual_approver', gs.getUserID());
    approval.update();

    // Update parent record approval status
    updateParentApprovalStatus(approval.getValue('sysapproval'));

    return {
        success: true,
        decision: decision,
        record: approval.sysapproval.getDisplayValue()
    };
}

function canActOnBehalf(originalApproverId) {
    // Check delegation
    var delegation = new GlideRecord('sys_user_delegate');
    delegation.addQuery('user', originalApproverId);
    delegation.addQuery('delegate', gs.getUserID());
    delegation.addQuery('starts', '<=', new GlideDateTime());
    delegation.addQuery('ends', '>=', new GlideDateTime());
    delegation.addQuery('approvals', true);
    delegation.query();
    return delegation.hasNext();
}

Update Parent Record

更新父记录

javascript
// Update approval status on parent record (ES5 ONLY!)
function updateParentApprovalStatus(recordSysId) {
    // Get all approvals for this record
    var approvals = new GlideRecord('sysapproval_approver');
    approvals.addQuery('sysapproval', recordSysId);
    approvals.query();

    var requested = 0;
    var approved = 0;
    var rejected = 0;

    while (approvals.next()) {
        var state = approvals.getValue('state');
        if (state === 'requested') requested++;
        else if (state === 'approved') approved++;
        else if (state === 'rejected') rejected++;
    }

    // Determine overall status
    var overallStatus = 'not requested';

    if (rejected > 0) {
        overallStatus = 'rejected';
    } else if (requested > 0) {
        overallStatus = 'requested';
    } else if (approved > 0) {
        overallStatus = 'approved';
    }

    // Update parent record
    var parent = new GlideRecord('change_request');
    if (parent.get(recordSysId)) {
        parent.setValue('approval', overallStatus);
        parent.update();
    }
}
javascript
// Update approval status on parent record (ES5 ONLY!)
function updateParentApprovalStatus(recordSysId) {
    // Get all approvals for this record
    var approvals = new GlideRecord('sysapproval_approver');
    approvals.addQuery('sysapproval', recordSysId);
    approvals.query();

    var requested = 0;
    var approved = 0;
    var rejected = 0;

    while (approvals.next()) {
        var state = approvals.getValue('state');
        if (state === 'requested') requested++;
        else if (state === 'approved') approved++;
        else if (state === 'rejected') rejected++;
    }

    // Determine overall status
    var overallStatus = 'not requested';

    if (rejected > 0) {
        overallStatus = 'rejected';
    } else if (requested > 0) {
        overallStatus = 'requested';
    } else if (approved > 0) {
        overallStatus = 'approved';
    }

    // Update parent record
    var parent = new GlideRecord('change_request');
    if (parent.get(recordSysId)) {
        parent.setValue('approval', overallStatus);
        parent.update();
    }
}

Group Approvals (ES5)

审批组(ES5)

Configure Group Approval

配置审批组

javascript
// Create group approval configuration (ES5 ONLY!)
var groupApproval = new GlideRecord('sysapproval_group');
groupApproval.initialize();
groupApproval.setValue('parent', recordSysId);
groupApproval.setValue('group', groupSysId);

// Approval requirement
groupApproval.setValue('approval', 'any');  // any, all, specific_count
groupApproval.setValue('specific_count', 2);  // If specific_count

groupApproval.insert();
javascript
// Create group approval configuration (ES5 ONLY!)
var groupApproval = new GlideRecord('sysapproval_group');
groupApproval.initialize();
groupApproval.setValue('parent', recordSysId);
groupApproval.setValue('group', groupSysId);

// Approval requirement
groupApproval.setValue('approval', 'any');  // any, all, specific_count
groupApproval.setValue('specific_count', 2);  // If specific_count

groupApproval.insert();

Group Approval with Minimum

满足最低要求的审批组

javascript
// Check if group approval threshold met (ES5 ONLY!)
function checkGroupApprovalThreshold(groupApprovalSysId) {
    var groupConfig = new GlideRecord('sysapproval_group');
    if (!groupConfig.get(groupApprovalSysId)) {
        return false;
    }

    var approvalType = groupConfig.getValue('approval');
    var groupId = groupConfig.getValue('group');
    var parentId = groupConfig.getValue('parent');

    // Count approvals from group members
    var ga = new GlideAggregate('sysapproval_approver');
    ga.addQuery('sysapproval', parentId);
    ga.addQuery('approver.sys_id', 'IN', getGroupMembers(groupId));
    ga.addQuery('state', 'approved');
    ga.addAggregate('COUNT');
    ga.query();

    var approvedCount = 0;
    if (ga.next()) {
        approvedCount = parseInt(ga.getAggregate('COUNT'), 10);
    }

    // Check based on type
    if (approvalType === 'any') {
        return approvedCount >= 1;
    } else if (approvalType === 'all') {
        var memberCount = getGroupMemberCount(groupId);
        return approvedCount >= memberCount;
    } else if (approvalType === 'specific_count') {
        var required = parseInt(groupConfig.getValue('specific_count'), 10);
        return approvedCount >= required;
    }

    return false;
}
javascript
// Check if group approval threshold met (ES5 ONLY!)
function checkGroupApprovalThreshold(groupApprovalSysId) {
    var groupConfig = new GlideRecord('sysapproval_group');
    if (!groupConfig.get(groupApprovalSysId)) {
        return false;
    }

    var approvalType = groupConfig.getValue('approval');
    var groupId = groupConfig.getValue('group');
    var parentId = groupConfig.getValue('parent');

    // Count approvals from group members
    var ga = new GlideAggregate('sysapproval_approver');
    ga.addQuery('sysapproval', parentId);
    ga.addQuery('approver.sys_id', 'IN', getGroupMembers(groupId));
    ga.addQuery('state', 'approved');
    ga.addAggregate('COUNT');
    ga.query();

    var approvedCount = 0;
    if (ga.next()) {
        approvedCount = parseInt(ga.getAggregate('COUNT'), 10);
    }

    // Check based on type
    if (approvalType === 'any') {
        return approvedCount >= 1;
    } else if (approvalType === 'all') {
        var memberCount = getGroupMemberCount(groupId);
        return approvedCount >= memberCount;
    } else if (approvalType === 'specific_count') {
        var required = parseInt(groupConfig.getValue('specific_count'), 10);
        return approvedCount >= required;
    }

    return false;
}

Approval Delegation (ES5)

审批委托(ES5)

Create Delegation

创建委托

javascript
// Create approval delegation (ES5 ONLY!)
function createDelegation(userId, delegateId, startDate, endDate) {
    var delegation = new GlideRecord('sys_user_delegate');
    delegation.initialize();
    delegation.setValue('user', userId);
    delegation.setValue('delegate', delegateId);
    delegation.setValue('starts', startDate);
    delegation.setValue('ends', endDate);
    delegation.setValue('approvals', true);
    delegation.setValue('assignments', false);

    return delegation.insert();
}
javascript
// Create approval delegation (ES5 ONLY!)
function createDelegation(userId, delegateId, startDate, endDate) {
    var delegation = new GlideRecord('sys_user_delegate');
    delegation.initialize();
    delegation.setValue('user', userId);
    delegation.setValue('delegate', delegateId);
    delegation.setValue('starts', startDate);
    delegation.setValue('ends', endDate);
    delegation.setValue('approvals', true);
    delegation.setValue('assignments', false);

    return delegation.insert();
}

Find Active Delegates

查询活跃委托

javascript
// Get delegates who can approve for a user (ES5 ONLY!)
function getActiveDelegates(userId) {
    var delegates = [];
    var now = new GlideDateTime();

    var delegation = new GlideRecord('sys_user_delegate');
    delegation.addQuery('user', userId);
    delegation.addQuery('approvals', true);
    delegation.addQuery('starts', '<=', now);
    delegation.addQuery('ends', '>=', now);
    delegation.query();

    while (delegation.next()) {
        delegates.push({
            delegate: delegation.delegate.getDisplayValue(),
            delegate_id: delegation.getValue('delegate'),
            ends: delegation.getValue('ends')
        });
    }

    return delegates;
}
javascript
// Get delegates who can approve for a user (ES5 ONLY!)
function getActiveDelegates(userId) {
    var delegates = [];
    var now = new GlideDateTime();

    var delegation = new GlideRecord('sys_user_delegate');
    delegation.addQuery('user', userId);
    delegation.addQuery('approvals', true);
    delegation.addQuery('starts', '<=', now);
    delegation.addQuery('ends', '>=', now);
    delegation.query();

    while (delegation.next()) {
        delegates.push({
            delegate: delegation.delegate.getDisplayValue(),
            delegate_id: delegation.getValue('delegate'),
            ends: delegation.getValue('ends')
        });
    }

    return delegates;
}

Approval Notifications (ES5)

审批通知(ES5)

Send Approval Request

发送审批请求

javascript
// Trigger approval notification (ES5 ONLY!)
function sendApprovalNotification(approvalSysId) {
    var approval = new GlideRecord('sysapproval_approver');
    if (!approval.get(approvalSysId)) return;

    var parent = new GlideRecord(approval.source_table);
    if (parent.get(approval.getValue('sysapproval'))) {
        gs.eventQueue('approval.request', approval, approval.getValue('approver'), '');
    }
}
javascript
// Trigger approval notification (ES5 ONLY!)
function sendApprovalNotification(approvalSysId) {
    var approval = new GlideRecord('sysapproval_approver');
    if (!approval.get(approvalSysId)) return;

    var parent = new GlideRecord(approval.source_table);
    if (parent.get(approval.getValue('sysapproval'))) {
        gs.eventQueue('approval.request', approval, approval.getValue('approver'), '');
    }
}

MCP Tool Integration

MCP 工具集成

Available Tools

可用工具

ToolPurpose
snow_query_table
Query approvals
snow_find_artifact
Find approval rules
snow_execute_script_with_output
Test approval scripts
snow_create_business_rule
Create approval triggers
工具用途
snow_query_table
查询审批记录
snow_find_artifact
查找审批规则
snow_execute_script_with_output
测试审批脚本
snow_create_business_rule
创建审批触发器

Example Workflow

示例工作流

javascript
// 1. Query pending approvals
await snow_query_table({
    table: 'sysapproval_approver',
    query: 'state=requested^approver=javascript:gs.getUserID()',
    fields: 'sysapproval,state,sys_created_on'
});

// 2. Find approval rules
await snow_query_table({
    table: 'sysapproval_rule',
    query: 'table=change_request^active=true',
    fields: 'name,conditions,approver,approval_type'
});

// 3. Check delegations
await snow_execute_script_with_output({
    script: `
        var delegates = getActiveDelegates(gs.getUserID());
        gs.info('Active delegates: ' + JSON.stringify(delegates));
    `
});
javascript
// 1. Query pending approvals
await snow_query_table({
    table: 'sysapproval_approver',
    query: 'state=requested^approver=javascript:gs.getUserID()',
    fields: 'sysapproval,state,sys_created_on'
});

// 2. Find approval rules
await snow_query_table({
    table: 'sysapproval_rule',
    query: 'table=change_request^active=true',
    fields: 'name,conditions,approver,approval_type'
});

// 3. Check delegations
await snow_execute_script_with_output({
    script: `
        var delegates = getActiveDelegates(gs.getUserID());
        gs.info('Active delegates: ' + JSON.stringify(delegates));
    `
});

Best Practices

最佳实践

  1. Clear Conditions - Specific rule conditions
  2. Logical Order - Rule ordering matters
  3. Escalation - Handle non-response
  4. Delegation - Support out-of-office
  5. Notifications - Timely reminders
  6. Audit Trail - Track all decisions
  7. Testing - Test all approval paths
  8. ES5 Only - No modern JavaScript syntax
  1. 明确条件 - 规则条件需具体
  2. 逻辑排序 - 规则顺序至关重要
  3. 升级机制 - 处理未响应情况
  4. 委托功能 - 支持外出场景
  5. 通知提醒 - 及时发送提醒
  6. 审计追踪 - 记录所有决策
  7. 测试验证 - 测试所有审批路径
  8. 仅支持ES5 - 禁止使用现代JavaScript语法