field-service

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Field 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 Optimization
Work Order (wm_order)
    ├── Work Order Tasks (wm_task)
    │   ├── Time Entries
    │   └── Parts Used
    ├── Asset/CI
    └── Location

Dispatch
    ├── Scheduling
    └── Route Optimization

Key Tables

核心表

TablePurpose
wm_order
Work orders
wm_task
Work order tasks
wm_resource
Field technicians
wm_schedule_entry
Schedule entries
wm_territory
Service territories
表名用途
wm_order
工单
wm_task
工单任务
wm_resource
现场技术人员
wm_schedule_entry
排班记录
wm_territory
服务区域

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

可用工具

ToolPurpose
snow_query_table
Query FSM tables
snow_execute_script_with_output
Test FSM scripts
snow_find_artifact
Find configurations
工具用途
snow_query_table
查询FSM表
snow_execute_script_with_output
测试FSM脚本
snow_find_artifact
查找配置项

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

最佳实践

  1. Skills Matching - Match technician skills to requirements
  2. Territory Planning - Optimize service areas
  3. Route Optimization - Minimize travel time
  4. Mobile-First - Design for field use
  5. Real-Time Updates - GPS and status tracking
  6. Parts Management - Track inventory
  7. Time Tracking - Accurate time entries
  8. ES5 Only - No modern JavaScript syntax
  1. 技能匹配 - 确保技术人员技能与需求匹配
  2. 区域规划 - 优化服务区域
  3. 路线优化 - 最小化出行时间
  4. 移动优先 - 为现场使用场景设计功能
  5. 实时更新 - GPS与状态追踪
  6. 备件管理 - 追踪库存
  7. 工时追踪 - 准确记录工时
  8. 仅使用ES5 - 禁止使用现代JavaScript语法