| name | hr-service-delivery |
| description | Build ServiceNow HR Service Delivery — sn_hr_core_case from Employee Center or HR catalog, sn_hr_le_lifecycle_event onboarding/offboarding with templated activities, and HR document generation. |
| license | Apache-2.0 |
| compatibility | Designed for Snow-Code and ServiceNow development |
| metadata | {"author":"serac","version":"1.0.0","category":"servicenow"} |
| tools | ["snow_create_hr_case","snow_query_table","snow_artifact_manage","snow_execute_script"] |
HR Service Delivery for ServiceNow
HR Service Delivery (HRSD) streamlines employee services through cases, lifecycle events, and self-service.
HRSD Architecture
Employee Center (Portal)
├── HR Service Catalog
│ ├── HR Catalog Items
│ └── Requests → HR Cases
├── Knowledge Articles
└── My HR Cases
HR Cases (sn_hr_core_case)
├── Case Tasks
├── Lifecycle Events
└── Document Requests
Key Tables
| Table | Purpose |
|---|
sn_hr_core_case | HR cases |
sn_hr_core_case_operation | Case operations/tasks |
sn_hr_le_lifecycle_event | Lifecycle events |
sn_hr_le_activity | Lifecycle activities |
sn_hr_core_service | HR services |
HR Cases (ES5)
Create HR Case
var hrCase = new GlideRecord("sn_hr_core_case")
hrCase.initialize()
hrCase.setValue("short_description", "Request for salary verification letter")
hrCase.setValue("description", "Employee needs salary verification for mortgage application")
hrCase.setValue("hr_service", getHRService("Document Requests"))
hrCase.setValue("hr_service_type", "general_inquiry")
hrCase.setValue("subject_person", employeeSysId)
hrCase.setValue("opened_for", employeeSysId)
hrCase.setValue("opened_by", gs.getUserID())
hrCase.setValue("assignment_group", getGroupSysId("HR Operations"))
hrCase.setValue("priority", 3)
var caseSysId = hrCase.insert()
HR Case from Catalog
;(function executeActivity(inputs, outputs, scratchpad) {
var ritm = inputs.request_item
var variables = ritm.variables
var hrCase = new GlideRecord("sn_hr_core_case")
hrCase.initialize()
hrCase.setValue("short_description", ritm.cat_item.getDisplayValue() + " for " + ritm.opened_for.getDisplayValue())
hrCase.setValue("hr_service", ritm.cat_item.u_hr_service)
hrCase.setValue("subject_person", ritm.opened_for)
hrCase.setValue("opened_for", ritm.opened_for)
hrCase.setValue("opened_by", ritm.opened_by)
hrCase.setValue("u_effective_date", variables.effective_date)
hrCase.setValue("u_reason", variables.reason)
hrCase.setValue("parent", ritm.getUniqueValue())
outputs.hr_case = hrCase.insert()
})(inputs, outputs, scratchpad)
Lifecycle Events (ES5)
Onboarding Lifecycle Event
function createOnboardingEvent(employeeSysId, startDate, details) {
var lifecycle = new GlideRecord("sn_hr_le_lifecycle_event")
lifecycle.initialize()
lifecycle.setValue("name", "Onboarding - " + getEmployeeName(employeeSysId))
lifecycle.setValue("subject_person", employeeSysId)
lifecycle.setValue("state", "ready")
lifecycle.setValue("le_type", getLifecycleType("onboarding"))
lifecycle.setValue("planned_start", startDate)
lifecycle.setValue("department", details.department)
lifecycle.setValue("location", details.location)
lifecycle.setValue("manager", details.manager)
var eventSysId = lifecycle.insert()
generateActivitiesFromTemplate(eventSysId, "onboarding")
return eventSysId
}
Lifecycle Activities
function generateActivitiesFromTemplate(lifecycleEventSysId, templateName) {
var lifecycleEvent = new GlideRecord("sn_hr_le_lifecycle_event")
if (!lifecycleEvent.get(lifecycleEventSysId)) {
return
}
var template = new GlideRecord("sn_hr_le_activity_template")
template.addQuery("template", templateName)
template.orderBy("order")
template.query()
var plannedStart = new GlideDateTime(lifecycleEvent.getValue("planned_start"))
while (template.next()) {
var activity = new GlideRecord("sn_hr_le_activity")
activity.initialize()
activity.setValue("lifecycle_event", lifecycleEventSysId)
activity.setValue("short_description", template.getValue("name"))
activity.setValue("description", template.getValue("description"))
activity.setValue("activity_type", template.getValue("activity_type"))
activity.setValue("assignment_group", template.getValue("assignment_group"))
var dueDate = new GlideDateTime(plannedStart)
dueDate.addDaysLocalTime(parseInt(template.getValue("day_offset"), 10))
activity.setValue("due_date", dueDate)
activity.setValue("order", template.getValue("order"))
activity.setValue("state", "pending")
activity.insert()
}
}
Offboarding Automation
;(function executeRule(current, previous) {
if (current.active.changesTo(false) && current.u_employment_status.changesTo("terminated")) {
createOffboardingEvent(current)
}
})(current, previous)
function createOffboardingEvent(user) {
var lifecycle = new GlideRecord("sn_hr_le_lifecycle_event")
lifecycle.initialize()
lifecycle.setValue("name", "Offboarding - " + user.getDisplayValue())
lifecycle.setValue("subject_person", user.getUniqueValue())
lifecycle.setValue("le_type", getLifecycleType("offboarding"))
lifecycle.setValue("state", "ready")
lifecycle.setValue("planned_start", user.getValue("u_termination_date") || new GlideDateTime())
lifecycle.setValue("u_current_groups", getCurrentGroups(user.getUniqueValue()))
lifecycle.setValue("u_current_roles", getCurrentRoles(user.getUniqueValue()))
var eventSysId = lifecycle.insert()
generateActivitiesFromTemplate(eventSysId, "offboarding")
gs.eventQueue("hr.offboarding.initiated", lifecycle, user.manager, "")
}
HR Services (ES5)
HR Service Configuration
var service = new GlideRecord("sn_hr_core_service")
service.initialize()
service.setValue("name", "Benefits Enrollment")
service.setValue("short_description", "Enroll in or change benefit plans")
service.setValue("description", "Request enrollment or changes to health, dental, vision, and retirement benefits")
service.setValue("topic", getHRTopic("Benefits"))
service.setValue("fulfillment_group", getGroupSysId("Benefits Administration"))
service.setValue("default_sla", getSLASysId("HR Standard Response"))
service.setValue("visibility", "all_employees")
service.setValue("employee_center_visible", true)
service.insert()
Document Generation
function generateHRDocument(hrCaseSysId, documentType) {
var hrCase = new GlideRecord("sn_hr_core_case")
if (!hrCase.get(hrCaseSysId)) {
return null
}
var employee = hrCase.subject_person.getRefRecord()
var template = new GlideRecord("sn_hr_core_document_template")
template.addQuery("name", documentType)
template.query()
if (!template.next()) {
gs.error("Document template not found: " + documentType)
return null
}
var content = template.getValue("template_body")
content = processTemplate(content, {
employee_name: employee.getDisplayValue(),
employee_id: employee.getValue("employee_number"),
title: employee.getValue("title"),
department: employee.department.getDisplayValue(),
start_date: employee.getValue("u_start_date"),
salary: employee.getValue("u_annual_salary"),
manager_name: employee.manager.getDisplayValue(),
current_date: new GlideDateTime().getLocalDate().toString(),
})
var doc = new GlideRecord("sn_hr_core_document")
doc.initialize()
doc.setValue("hr_case", hrCaseSysId)
doc.setValue("subject_person", employee.getUniqueValue())
doc.setValue("document_type", documentType)
doc.setValue("name", documentType + " - " + employee.getDisplayValue())
doc.setValue("content", content)
doc.setValue("state", "draft")
return doc.insert()
}
function processTemplate(template, data) {
for (var key in data) {
if (data.hasOwnProperty(key)) {
var pattern = new RegExp("\\{\\{" + key + "\\}\\}", "g")
template = template.replace(pattern, data[key] || "")
}
}
return template
}
Employee Center Integration (ES5)
Case Status Widget
;(function () {
var userId = gs.getUserID()
data.cases = []
var gr = new GlideRecord("sn_hr_core_case")
gr.addQuery("subject_person", userId)
gr.addQuery("active", true)
gr.orderByDesc("opened_at")
gr.setLimit(10)
gr.query()
while (gr.next()) {
data.cases.push({
sys_id: gr.getUniqueValue(),
number: gr.getValue("number"),
short_description: gr.getValue("short_description"),
state: gr.state.getDisplayValue(),
priority: gr.priority.getDisplayValue(),
opened_at: gr.getValue("opened_at"),
hr_service: gr.hr_service.getDisplayValue(),
})
}
data.activities = []
var activity = new GlideRecord("sn_hr_le_activity")
activity.addQuery("lifecycle_event.subject_person", userId)
activity.addQuery("state", "IN", "pending,in_progress")
activity.orderBy("due_date")
activity.query()
while (activity.next()) {
data.activities.push({
sys_id: activity.getUniqueValue(),
short_description: activity.getValue("short_description"),
due_date: activity.getValue("due_date"),
state: activity.state.getDisplayValue(),
})
}
})()
MCP Tool Integration
Available Tools
| Tool | Purpose |
|---|
snow_query_table | Query HR cases and activities |
snow_artifact_manage (action=find) | Find HR configurations |
snow_execute_script | Test HR scripts |
snow_artifact_manage (action=create) | Deploy HR widgets |
Example Workflow
await snow_query_table({
table: "sn_hr_core_case",
query: "active=true^assignment_group=HR Operations",
fields: "number,short_description,subject_person,state,opened_at",
})
await snow_query_table({
table: "sn_hr_le_lifecycle_event",
query: "state=in_progress",
fields: "name,subject_person,le_type,planned_start",
})
await snow_execute_script({
script: `
var hrCase = new GlideRecord('sn_hr_core_case');
hrCase.initialize();
hrCase.short_description = 'Test HR Case';
hrCase.subject_person = gs.getUserID();
gs.info('Created: ' + hrCase.insert());
`,
})
Best Practices
- Service Catalog - Use catalog for common requests
- Templates - Lifecycle activity templates
- Automation - Trigger events on HR changes
- Documents - Template-based generation
- Privacy - Respect HR data sensitivity
- SLAs - Define service commitments
- Employee Center - Self-service focus
- ES5 Only - No modern JavaScript syntax