field-service
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseField Service Management for ServiceNow
ServiceNow 现场服务管理(FSM)
Field Service Management (FSM) manages work orders, technician dispatch, and mobile field operations.
现场服务管理(FSM)负责管理工单、技术人员调度以及移动现场作业。
FSM Architecture
FSM 架构
Work Order (wm_order)
├── Work Order Tasks (wm_task)
│ ├── Time Entries
│ └── Parts Used
├── Asset/CI
└── Location
Dispatch
├── Scheduling
└── Route OptimizationWork Order (wm_order)
├── Work Order Tasks (wm_task)
│ ├── Time Entries
│ └── Parts Used
├── Asset/CI
└── Location
Dispatch
├── Scheduling
└── Route OptimizationKey Tables
核心表
| Table | Purpose |
|---|---|
| Work orders |
| Work order tasks |
| Field technicians |
| Schedule entries |
| Service territories |
| 表名 | 用途 |
|---|---|
| 工单 |
| 工单任务 |
| 现场技术人员 |
| 排班记录 |
| 服务区域 |
Work Orders (ES5)
工单管理(ES5)
Create Work Order
创建工单
javascript
// Create work order (ES5 ONLY!)
var workOrder = new GlideRecord('wm_order');
workOrder.initialize();
// Basic info
workOrder.setValue('short_description', 'HVAC repair - Building A');
workOrder.setValue('description', 'AC unit not cooling properly');
workOrder.setValue('priority', 2);
// Classification
workOrder.setValue('work_order_type', 'repair');
workOrder.setValue('category', 'hvac');
// Location
workOrder.setValue('location', locationSysId);
workOrder.setValue('cmdb_ci', hvacUnitCISysId);
// Customer/Contact
workOrder.setValue('account', customerAccountSysId);
workOrder.setValue('contact', contactSysId);
// Scheduling
var scheduledStart = new GlideDateTime();
scheduledStart.addDaysLocalTime(1);
workOrder.setValue('scheduled_start', scheduledStart);
// Assignment
workOrder.setValue('assignment_group', fieldServiceGroupSysId);
// SLA
workOrder.setValue('sla', slaDefinitionSysId);
workOrder.insert();javascript
// Create work order (ES5 ONLY!)
var workOrder = new GlideRecord('wm_order');
workOrder.initialize();
// Basic info
workOrder.setValue('short_description', 'HVAC repair - Building A');
workOrder.setValue('description', 'AC unit not cooling properly');
workOrder.setValue('priority', 2);
// Classification
workOrder.setValue('work_order_type', 'repair');
workOrder.setValue('category', 'hvac');
// Location
workOrder.setValue('location', locationSysId);
workOrder.setValue('cmdb_ci', hvacUnitCISysId);
// Customer/Contact
workOrder.setValue('account', customerAccountSysId);
workOrder.setValue('contact', contactSysId);
// Scheduling
var scheduledStart = new GlideDateTime();
scheduledStart.addDaysLocalTime(1);
workOrder.setValue('scheduled_start', scheduledStart);
// Assignment
workOrder.setValue('assignment_group', fieldServiceGroupSysId);
// SLA
workOrder.setValue('sla', slaDefinitionSysId);
workOrder.insert();Work Order Tasks
工单任务
javascript
// Create work order tasks (ES5 ONLY!)
function createWorkOrderTasks(workOrderSysId, tasks) {
var createdTasks = [];
for (var i = 0; i < tasks.length; i++) {
var task = new GlideRecord('wm_task');
task.initialize();
task.setValue('work_order', workOrderSysId);
task.setValue('short_description', tasks[i].description);
task.setValue('order', (i + 1) * 100);
// Estimated duration
task.setValue('estimated_duration', tasks[i].duration);
// Skills required
if (tasks[i].skills) {
task.setValue('skills', tasks[i].skills);
}
// Parts needed
if (tasks[i].parts) {
task.setValue('u_parts_required', tasks[i].parts);
}
var taskSysId = task.insert();
createdTasks.push({
sys_id: taskSysId,
number: task.getValue('number')
});
}
return createdTasks;
}
// Example
createWorkOrderTasks(workOrderSysId, [
{ description: 'Diagnose AC unit', duration: '01:00:00', skills: 'hvac_certified' },
{ description: 'Replace compressor', duration: '02:00:00', parts: 'COMP-AC-001' },
{ description: 'Test and verify', duration: '00:30:00' }
]);javascript
// Create work order tasks (ES5 ONLY!)
function createWorkOrderTasks(workOrderSysId, tasks) {
var createdTasks = [];
for (var i = 0; i < tasks.length; i++) {
var task = new GlideRecord('wm_task');
task.initialize();
task.setValue('work_order', workOrderSysId);
task.setValue('short_description', tasks[i].description);
task.setValue('order', (i + 1) * 100);
// Estimated duration
task.setValue('estimated_duration', tasks[i].duration);
// Skills required
if (tasks[i].skills) {
task.setValue('skills', tasks[i].skills);
}
// Parts needed
if (tasks[i].parts) {
task.setValue('u_parts_required', tasks[i].parts);
}
var taskSysId = task.insert();
createdTasks.push({
sys_id: taskSysId,
number: task.getValue('number')
});
}
return createdTasks;
}
// Example
createWorkOrderTasks(workOrderSysId, [
{ description: 'Diagnose AC unit', duration: '01:00:00', skills: 'hvac_certified' },
{ description: 'Replace compressor', duration: '02:00:00', parts: 'COMP-AC-001' },
{ description: 'Test and verify', duration: '00:30:00' }
]);Technician Management (ES5)
技术人员管理(ES5)
Create Resource Profile
创建资源档案
javascript
// Create field technician profile (ES5 ONLY!)
var resource = new GlideRecord('wm_resource');
resource.initialize();
// Link to user
resource.setValue('user', userSysId);
// Skills
resource.setValue('skills', 'hvac_certified,electrical,plumbing');
// Territory
resource.setValue('territory', territorySysId);
// Availability
resource.setValue('work_schedule', scheduleId);
// Vehicle/Equipment
resource.setValue('vehicle', vehicleCISysId);
// Active
resource.setValue('active', true);
resource.insert();javascript
// Create field technician profile (ES5 ONLY!)
var resource = new GlideRecord('wm_resource');
resource.initialize();
// Link to user
resource.setValue('user', userSysId);
// Skills
resource.setValue('skills', 'hvac_certified,electrical,plumbing');
// Territory
resource.setValue('territory', territorySysId);
// Availability
resource.setValue('work_schedule', scheduleId);
// Vehicle/Equipment
resource.setValue('vehicle', vehicleCISysId);
// Active
resource.setValue('active', true);
resource.insert();Check Technician Availability
检查技术人员可用性
javascript
// Get available technicians for time slot (ES5 ONLY!)
function getAvailableTechnicians(scheduledStart, scheduledEnd, requiredSkills, territory) {
var available = [];
// Get all active technicians in territory
var resource = new GlideRecord('wm_resource');
resource.addQuery('active', true);
if (territory) {
resource.addQuery('territory', territory);
}
resource.query();
while (resource.next()) {
// Check skills
if (requiredSkills && !hasRequiredSkills(resource, requiredSkills)) {
continue;
}
// Check availability
if (!isAvailable(resource, scheduledStart, scheduledEnd)) {
continue;
}
var user = resource.user.getRefRecord();
available.push({
resource_sys_id: resource.getUniqueValue(),
user_sys_id: user.getUniqueValue(),
name: user.getDisplayValue(),
skills: resource.getValue('skills'),
territory: resource.territory.getDisplayValue()
});
}
return available;
}
function hasRequiredSkills(resource, requiredSkills) {
var techSkills = resource.getValue('skills').split(',');
var required = requiredSkills.split(',');
for (var i = 0; i < required.length; i++) {
if (techSkills.indexOf(required[i].trim()) === -1) {
return false;
}
}
return true;
}
function isAvailable(resource, start, end) {
// Check for conflicting assignments
var assignment = new GlideRecord('wm_schedule_entry');
assignment.addQuery('resource', resource.getUniqueValue());
assignment.addQuery('start', '<', end);
assignment.addQuery('end', '>', start);
assignment.query();
return !assignment.hasNext();
}javascript
// Get available technicians for time slot (ES5 ONLY!)
function getAvailableTechnicians(scheduledStart, scheduledEnd, requiredSkills, territory) {
var available = [];
// Get all active technicians in territory
var resource = new GlideRecord('wm_resource');
resource.addQuery('active', true);
if (territory) {
resource.addQuery('territory', territory);
}
resource.query();
while (resource.next()) {
// Check skills
if (requiredSkills && !hasRequiredSkills(resource, requiredSkills)) {
continue;
}
// Check availability
if (!isAvailable(resource, scheduledStart, scheduledEnd)) {
continue;
}
var user = resource.user.getRefRecord();
available.push({
resource_sys_id: resource.getUniqueValue(),
user_sys_id: user.getUniqueValue(),
name: user.getDisplayValue(),
skills: resource.getValue('skills'),
territory: resource.territory.getDisplayValue()
});
}
return available;
}
function hasRequiredSkills(resource, requiredSkills) {
var techSkills = resource.getValue('skills').split(',');
var required = requiredSkills.split(',');
for (var i = 0; i < required.length; i++) {
if (techSkills.indexOf(required[i].trim()) === -1) {
return false;
}
}
return true;
}
function isAvailable(resource, start, end) {
// Check for conflicting assignments
var assignment = new GlideRecord('wm_schedule_entry');
assignment.addQuery('resource', resource.getUniqueValue());
assignment.addQuery('start', '<', end);
assignment.addQuery('end', '>', start);
assignment.query();
return !assignment.hasNext();
}Dispatch & Scheduling (ES5)
调度与排班(ES5)
Assign Work Order
分配工单
javascript
// Dispatch work order to technician (ES5 ONLY!)
function dispatchWorkOrder(workOrderSysId, resourceSysId, scheduledStart, scheduledEnd) {
// Create schedule entry
var schedule = new GlideRecord('wm_schedule_entry');
schedule.initialize();
schedule.setValue('work_order', workOrderSysId);
schedule.setValue('resource', resourceSysId);
schedule.setValue('start', scheduledStart);
schedule.setValue('end', scheduledEnd);
schedule.setValue('state', 'scheduled');
schedule.insert();
// Update work order
var wo = new GlideRecord('wm_order');
if (wo.get(workOrderSysId)) {
wo.setValue('assigned_to', getResourceUser(resourceSysId));
wo.setValue('scheduled_start', scheduledStart);
wo.setValue('scheduled_end', scheduledEnd);
wo.setValue('state', 'assigned');
wo.update();
}
// Notify technician
gs.eventQueue('wm.work_order.assigned', wo, resourceSysId, '');
return schedule.getUniqueValue();
}javascript
// Dispatch work order to technician (ES5 ONLY!)
function dispatchWorkOrder(workOrderSysId, resourceSysId, scheduledStart, scheduledEnd) {
// Create schedule entry
var schedule = new GlideRecord('wm_schedule_entry');
schedule.initialize();
schedule.setValue('work_order', workOrderSysId);
schedule.setValue('resource', resourceSysId);
schedule.setValue('start', scheduledStart);
schedule.setValue('end', scheduledEnd);
schedule.setValue('state', 'scheduled');
schedule.insert();
// Update work order
var wo = new GlideRecord('wm_order');
if (wo.get(workOrderSysId)) {
wo.setValue('assigned_to', getResourceUser(resourceSysId));
wo.setValue('scheduled_start', scheduledStart);
wo.setValue('scheduled_end', scheduledEnd);
wo.setValue('state', 'assigned');
wo.update();
}
// Notify technician
gs.eventQueue('wm.work_order.assigned', wo, resourceSysId, '');
return schedule.getUniqueValue();
}Auto-Dispatch
自动调度
javascript
// Auto-dispatch to best available technician (ES5 ONLY!)
function autoDispatch(workOrderSysId) {
var wo = new GlideRecord('wm_order');
if (!wo.get(workOrderSysId)) {
return { success: false, message: 'Work order not found' };
}
// Get requirements
var scheduledStart = new GlideDateTime(wo.getValue('scheduled_start'));
var estimatedDuration = wo.getValue('estimated_duration') || '02:00:00';
var scheduledEnd = new GlideDateTime(scheduledStart);
var durationParts = estimatedDuration.split(':');
scheduledEnd.addSeconds(
parseInt(durationParts[0], 10) * 3600 +
parseInt(durationParts[1], 10) * 60 +
parseInt(durationParts[2], 10)
);
var requiredSkills = wo.getValue('u_required_skills');
var location = wo.location.getRefRecord();
var territory = location.getValue('u_territory');
// Find available technicians
var available = getAvailableTechnicians(
scheduledStart,
scheduledEnd,
requiredSkills,
territory
);
if (available.length === 0) {
return { success: false, message: 'No available technicians' };
}
// Select best match (first available, could add routing optimization)
var bestMatch = available[0];
// Dispatch
var scheduleId = dispatchWorkOrder(
workOrderSysId,
bestMatch.resource_sys_id,
scheduledStart,
scheduledEnd
);
return {
success: true,
technician: bestMatch.name,
schedule_id: scheduleId
};
}javascript
// Auto-dispatch to best available technician (ES5 ONLY!)
function autoDispatch(workOrderSysId) {
var wo = new GlideRecord('wm_order');
if (!wo.get(workOrderSysId)) {
return { success: false, message: 'Work order not found' };
}
// Get requirements
var scheduledStart = new GlideDateTime(wo.getValue('scheduled_start'));
var estimatedDuration = wo.getValue('estimated_duration') || '02:00:00';
var scheduledEnd = new GlideDateTime(scheduledStart);
var durationParts = estimatedDuration.split(':');
scheduledEnd.addSeconds(
parseInt(durationParts[0], 10) * 3600 +
parseInt(durationParts[1], 10) * 60 +
parseInt(durationParts[2], 10)
);
var requiredSkills = wo.getValue('u_required_skills');
var location = wo.location.getRefRecord();
var territory = location.getValue('u_territory');
// Find available technicians
var available = getAvailableTechnicians(
scheduledStart,
scheduledEnd,
requiredSkills,
territory
);
if (available.length === 0) {
return { success: false, message: 'No available technicians' };
}
// Select best match (first available, could add routing optimization)
var bestMatch = available[0];
// Dispatch
var scheduleId = dispatchWorkOrder(
workOrderSysId,
bestMatch.resource_sys_id,
scheduledStart,
scheduledEnd
);
return {
success: true,
technician: bestMatch.name,
schedule_id: scheduleId
};
}Mobile Field Service (ES5)
移动现场服务(ES5)
Update Work Order Status (Mobile)
更新工单状态(移动端)
javascript
// Update from mobile app (ES5 ONLY!)
function updateWorkOrderFromMobile(workOrderSysId, statusUpdate) {
var wo = new GlideRecord('wm_order');
if (!wo.get(workOrderSysId)) {
return { success: false, message: 'Work order not found' };
}
// Update state
if (statusUpdate.state) {
wo.setValue('state', statusUpdate.state);
if (statusUpdate.state === 'work_in_progress') {
wo.setValue('actual_start', new GlideDateTime());
} else if (statusUpdate.state === 'closed_complete') {
wo.setValue('actual_end', new GlideDateTime());
}
}
// Add work notes
if (statusUpdate.notes) {
wo.work_notes = statusUpdate.notes;
}
// Update location (GPS)
if (statusUpdate.latitude && statusUpdate.longitude) {
wo.setValue('u_technician_latitude', statusUpdate.latitude);
wo.setValue('u_technician_longitude', statusUpdate.longitude);
}
wo.update();
return { success: true };
}javascript
// Update from mobile app (ES5 ONLY!)
function updateWorkOrderFromMobile(workOrderSysId, statusUpdate) {
var wo = new GlideRecord('wm_order');
if (!wo.get(workOrderSysId)) {
return { success: false, message: 'Work order not found' };
}
// Update state
if (statusUpdate.state) {
wo.setValue('state', statusUpdate.state);
if (statusUpdate.state === 'work_in_progress') {
wo.setValue('actual_start', new GlideDateTime());
} else if (statusUpdate.state === 'closed_complete') {
wo.setValue('actual_end', new GlideDateTime());
}
}
// Add work notes
if (statusUpdate.notes) {
wo.work_notes = statusUpdate.notes;
}
// Update location (GPS)
if (statusUpdate.latitude && statusUpdate.longitude) {
wo.setValue('u_technician_latitude', statusUpdate.latitude);
wo.setValue('u_technician_longitude', statusUpdate.longitude);
}
wo.update();
return { success: true };
}Record Time Entry
记录工时
javascript
// Record technician time (ES5 ONLY!)
function recordTimeEntry(workOrderSysId, timeData) {
var entry = new GlideRecord('time_card');
entry.initialize();
entry.setValue('task', workOrderSysId);
entry.setValue('user', gs.getUserID());
entry.setValue('type', timeData.type); // work, travel, break
entry.setValue('start_time', timeData.startTime);
entry.setValue('end_time', timeData.endTime);
// Calculate duration
var start = new GlideDateTime(timeData.startTime);
var end = new GlideDateTime(timeData.endTime);
var duration = GlideDateTime.subtract(start, end);
entry.setValue('duration', duration);
// Notes
entry.setValue('comments', timeData.notes);
return entry.insert();
}javascript
// Record technician time (ES5 ONLY!)
function recordTimeEntry(workOrderSysId, timeData) {
var entry = new GlideRecord('time_card');
entry.initialize();
entry.setValue('task', workOrderSysId);
entry.setValue('user', gs.getUserID());
entry.setValue('type', timeData.type); // work, travel, break
entry.setValue('start_time', timeData.startTime);
entry.setValue('end_time', timeData.endTime);
// Calculate duration
var start = new GlideDateTime(timeData.startTime);
var end = new GlideDateTime(timeData.endTime);
var duration = GlideDateTime.subtract(start, end);
entry.setValue('duration', duration);
// Notes
entry.setValue('comments', timeData.notes);
return entry.insert();
}MCP Tool Integration
MCP 工具集成
Available Tools
可用工具
| Tool | Purpose |
|---|---|
| Query FSM tables |
| Test FSM scripts |
| Find configurations |
| 工具 | 用途 |
|---|---|
| 查询FSM表 |
| 测试FSM脚本 |
| 查找配置项 |
Example Workflow
示例工作流
javascript
// 1. Query open work orders
await snow_query_table({
table: 'wm_order',
query: 'state!=closed_complete^state!=cancelled',
fields: 'number,short_description,location,scheduled_start,assigned_to'
});
// 2. Find available technicians
await snow_execute_script_with_output({
script: `
var available = getAvailableTechnicians(
new GlideDateTime(),
new GlideDateTime().addHours(2),
'hvac_certified',
null
);
gs.info(JSON.stringify(available));
`
});
// 3. Get technician schedule
await snow_query_table({
table: 'wm_schedule_entry',
query: 'resource.user=technician_user_id^startONToday',
fields: 'work_order,start,end,state'
});javascript
// 1. Query open work orders
await snow_query_table({
table: 'wm_order',
query: 'state!=closed_complete^state!=cancelled',
fields: 'number,short_description,location,scheduled_start,assigned_to'
});
// 2. Find available technicians
await snow_execute_script_with_output({
script: `
var available = getAvailableTechnicians(
new GlideDateTime(),
new GlideDateTime().addHours(2),
'hvac_certified',
null
);
gs.info(JSON.stringify(available));
`
});
// 3. Get technician schedule
await snow_query_table({
table: 'wm_schedule_entry',
query: 'resource.user=technician_user_id^startONToday',
fields: 'work_order,start,end,state'
});Best Practices
最佳实践
- Skills Matching - Match technician skills to requirements
- Territory Planning - Optimize service areas
- Route Optimization - Minimize travel time
- Mobile-First - Design for field use
- Real-Time Updates - GPS and status tracking
- Parts Management - Track inventory
- Time Tracking - Accurate time entries
- ES5 Only - No modern JavaScript syntax
- 技能匹配 - 确保技术人员技能与需求匹配
- 区域规划 - 优化服务区域
- 路线优化 - 最小化出行时间
- 移动优先 - 为现场使用场景设计功能
- 实时更新 - GPS与状态追踪
- 备件管理 - 追踪库存
- 工时追踪 - 准确记录工时
- 仅使用ES5 - 禁止使用现代JavaScript语法