approval-workflows
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseApproval 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 UpdatedRecord (change_request, sc_req_item, etc.)
↓
Approval Rules (sysapproval_rule)
↓
Approval Records (sysapproval_approver)
↓ Approve/Reject
Record State UpdatedKey Tables
核心表
| Table | Purpose |
|---|---|
| Individual approval records |
| Group approval configuration |
| Approval rules |
| Approval workflow stages |
| 表名 | 用途 |
|---|---|
| 单个审批记录 |
| 审批组配置 |
| 审批规则 |
| 审批工作流阶段 |
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
可用工具
| Tool | Purpose |
|---|---|
| Query approvals |
| Find approval rules |
| Test approval scripts |
| Create approval triggers |
| 工具 | 用途 |
|---|---|
| 查询审批记录 |
| 查找审批规则 |
| 测试审批脚本 |
| 创建审批触发器 |
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
最佳实践
- Clear Conditions - Specific rule conditions
- Logical Order - Rule ordering matters
- Escalation - Handle non-response
- Delegation - Support out-of-office
- Notifications - Timely reminders
- Audit Trail - Track all decisions
- Testing - Test all approval paths
- ES5 Only - No modern JavaScript syntax
- 明确条件 - 规则条件需具体
- 逻辑排序 - 规则顺序至关重要
- 升级机制 - 处理未响应情况
- 委托功能 - 支持外出场景
- 通知提醒 - 及时发送提醒
- 审计追踪 - 记录所有决策
- 测试验证 - 测试所有审批路径
- 仅支持ES5 - 禁止使用现代JavaScript语法