Field Service Management for ServiceNow
Field Service Management (FSM) manages work orders, technician dispatch, and mobile field operations.
FSM Architecture
Work Order (wm_order)
├── Work Order Tasks (wm_task)
│ ├── Time Entries
│ └── Parts Used
├── Asset/CI
└── Location
Dispatch
├── Scheduling
└── Route Optimization
Key Tables
Table
Purpose
wm_order
Work orders
wm_task
Work order tasks
wm_resource
Field technicians
wm_schedule_entry
Schedule entries
wm_territory
Service territories
Work Orders (ES5)
Create Work Order
// 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
// 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)
Create Resource Profile
// 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
// 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)
Assign Work Order
// 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
// 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)
Update Work Order Status (Mobile)
// 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
// 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
Available Tools
Tool
Purpose
snow_query_table
Query FSM tables
snow_execute_script_with_output
Test FSM scripts
snow_find_artifact
Find configurations
Example Workflow
// 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
field-service
安装
npx skills add https://github.com/groeimetai/snow-flow --skill field-service