Scripts & Functions — Deep Dive (Interview Reference)
شرح كل الدوال في تطبيقنا + أهم APIs في ServiceNow للمقابلة.
الأسلوب: عربية + إنجليزية تكمّلان بعضهما — بدون ترجمة حرفية لأسماء الشاشات.
Part 1 — Our Script Includes (line-by-line purpose)
HRLXProLeaveUtilsV2
File: src/server/script_includes/HRLXProLeaveUtilsV2.js
| Function | Parameters | Returns | Purpose |
|---|---|---|---|
| calculateBusinessDays | startDate, endDate (yyyy-MM-dd) | number | Counts Mon–Fri inclusive |
| setApproverFromEmployee | currentRecord (GlideRecord) | void | Sets u_approver_user from employee.u_manager |
| auditStateChange | leaveSysId, oldState, newState, eventType | void | Inserts audit row |
| recalculateBalance | employeeSysId, year | void | Updates u_used, u_remaining on balance |
| _sumApprovedDays | employeeSysId, year | number | Private — SUM u_days_count via GlideAggregate |
| ajaxGetBusinessDays | (from GlideAjax params) | string | Client-callable wrapper |
Interview: Why GlideAggregate for _sumApprovedDays? — أفضل أداء من loop على GlideRecord — يجمع على الخادم.
HRLXProSecurityUtilsV2
File: src/server/script_includes/HRLXProSecurityUtilsV2.js
| Function | Purpose |
|---|---|
| isHrAdmin | gs.hasRole(‘x_786686_hr_leave.hr_admin’) |
| canReadLeave | Owner, manager, or HR admin |
| canWriteLeave | draft only for owner; HR admin always |
| _isManagerOfEmployee | Checks employee.u_manager == current user |
Used in: ACL scripts (src/security/acls.md).
HRLXProIntegrationUtilsV2
File: src/server/script_includes/HRLXProIntegrationUtilsV2.js
| Function | Purpose |
|---|---|
| inboundCreateLeave | Parse JSON → find employee → insert leave req |
| outboundPushDecision | POST approve/reject to ERP via RESTMessageV2 |
| _findEmployeeByExternalId | Lookup by u_employee_id |
| _findUserByEmployee | Map employee → sys_user |
Part 2 — Our Business Rules
| BR | When | Trigger logic |
|---|---|---|
| BR1 | before insert/update | Days + approver |
| BR2 | after insert/update | if state→submitted: gs.eventQueue |
| BR3 | after update | if state changed: audit + recalculateBalance |
BR2 event example:
gs.eventQueue('x_786686_hr_leave.leave_v2.submitted', current,
current.getValue('u_approver_user'), current.getValue('u_number'));
Interview: Why after for BR2 not before? — لأننا نريد التأكد أن السجل حُفظ بحالة submitted قبل إطلاق الإشعار.
Part 3 — Server-Side API (gs.*) — Interview Must-Know
| API | Purpose | Example |
|---|---|---|
| gs.getUserID() | Current user sys_id | audit actor |
| gs.getUserName() | Username | logging |
| gs.hasRole(‘role’) | Role check | security |
| gs.info() / gs.error() | Logs | debugging |
| gs.addErrorMessage() | Block + message | BR abort |
| gs.eventQueue() | Fire event | notifications |
| gs.getProperty() | System property | config |
| gs.nil() | Null/empty check | validation |
| current.setAbortAction(true) | Stop BR save | validation fail |
Part 4 — GlideRecord patterns
var gr = new GlideRecord('table');
gr.addQuery('field', 'value');
gr.addActiveQuery(); // active=true
gr.orderByDesc('sys_created_on');
gr.setLimit(1);
gr.query();
if (gr.next()) {
gr.field = 'newValue';
gr.update();
}
// Insert
gr.initialize();
gr.field = ‘x’;
var sysId = gr.insert();
// Secure (respects ACL)
var grs = new GlideRecordSecure(‘table’);
Interview: When use GlideRecordSecure? — Service Portal، multi-tenant، أي سياق المستخدم النهائي.
Part 5 — GlideAggregate
var ga = new GlideAggregate('x_786686_hr_leave_req_v2');
ga.addQuery('u_state', 'approved');
ga.addAggregate('SUM', 'u_days_count');
ga.query();
if (ga.next()) {
var sum = ga.getAggregate('SUM', 'u_days_count');
}
Part 6 — Client-Side API (g_form.*)
| API | Purpose |
|---|---|
| g_form.getValue(‘field’) | Read field |
| g_form.setValue(‘field’, val) | Write field |
| g_form.setMandatory(‘field’, true) | Mandatory UI |
| g_form.setVisible(‘field’, false) | Hide |
| g_form.setReadOnly(‘field’, true) | Read only |
| g_form.addErrorMessage(‘msg’) | Show error |
| g_form.clearValue(‘field’) | Clear |
onSubmit:
function onSubmit() {
if (invalid) {
g_form.addErrorMessage('Fix dates');
return false; // blocks save
}
}
Part 7 — GlideAjax (client → server)
var ga = new GlideAjax('ScriptIncludeName');
ga.addParam('sysparm_name', 'methodName');
ga.addParam('sysparm_custom', value);
ga.getXMLAnswer(function(response) {
// response is string return value
});
Requirements: Script Include must be Client callable + extend AbstractAjaxProcessor (or have sysparm_name handler).
Part 8 — REST (inbound/outbound)
Inbound (Scripted REST):
(function process(request, response) {
var body = request.body.data;
response.setStatus(201);
response.setBody({ sys_id: '...' });
})(request, response);
Outbound (RESTMessageV2):
var rm = new sn_ws.RESTMessageV2('REST Message Name', 'POST method');
rm.setRequestBody(JSON.stringify(payload));
var res = rm.execute();
var status = res.getStatusCode();
Part 9 — Portal Widget server.js pattern
(function() {
data.items = [];
var gr = new GlideRecordSecure('x_786686_hr_leave_req_v2');
gr.addQuery('u_requested_for_user', gs.getUserID());
gr.query();
while (gr.next()) {
data.items.push({
number: gr.getDisplayValue('u_number'),
state: gr.getDisplayValue('u_state')
});
}
})();
Part 10 — Common interview “write this script” scenarios
1. Set assignment group by category:
if (current.category == 'network')
current.assignment_group.setDisplayValue('Network');
2. Prevent save if field empty:
if (!current.u_reason) {
gs.addErrorMessage('Reason required');
current.setAbortAction(true);
}
3. Copy manager to approver:
Same as setApproverFromEmployee in our app.
4. Query related records:
var child = new GlideRecord('child_table');
child.addQuery('parent', current.sys_id);
child.query();
Part 11 — Where each script type lives (navigation)
| Type | Navigate |
|---|---|
| Script Include | Studio or sys_script_include.list |
| Business Rule | Studio or sys_script.list |
| Client Script | Studio or sys_script_client.list |
| UI Policy | All > System UI > UI Policies · Open direct |
| Flow | $flow-designer.do |
| Scripted REST | All > System Web Services > Scripted REST APIs · Open direct |
Part 12 — Link to full development guide
File: ../01_DEVELOPMENT.md — every artifact explained with options.
File: 00_MASTER_DEV_ADMIN_INTERVIEW.md — platform + admin tour.