HR Leave · Australia

01 — Development (Studio From Scratch)

Complete Studio create guide: Application, 4 tables, Script Includes, BR1–4, CS1–2, UI Policy/Action, Menu, Modules, Flow

Scopex_786686_hr_leave
sys_ide5f5bc9493a303507520703efaba104d
Instance labdev398980
URL token{BASE}
Quick open Visual map Studio Tables Leave Requests Business Rules Flow Designer

Visual map — Ch.01 · Development — app from zero

See in 10 seconds

App → 4 tables → SI → BR1–4 → CS → UI → menu → Flow

You build

x_786686_hr_leave · employee/req/bal/audit_v2 · HRLX* utils

Track

Development

APPCreate scope
→
TBL4 × _v2
→
SIUtils
→
BR100→300
→
UICS+Policy
→
FLOWApprove
✓ u_* columns only✓ BR1 = before✓ Commit keys if SDK✗ Extend Task✗ current.update() in BR✗ Legacy duration_days

Full visual path (all chapters): VISUAL_FROM_SCRATCH_00_16.html

Studio-only from scratch: This chapter is a complete create guide for all core development objects. No SDK, no Fluent, no CLI, no npx. Application scope x_786686_hr_leave · sys_id e5f5bc9493a303507520703efaba104d.

Also open: Properties + Direct Links · Schema · Studio Create All Objects

Use HRLXProLeaveUtilsV2, HRLXProSecurityUtilsV2, and u_* columns only — not legacy LeaveRequestUtil or duration_days.

How development objects relate

Application (HR Leave)
  └── Tables: employee_v2 ←── bal_v2
                 ↑              ↑
                 └── req_v2 ────┴── audit_v2
                       │
        ┌──────────────┼──────────────┐
        ▼              ▼              ▼
  Script Includes   Business Rules   Client Scripts
  LeaveUtilsV2  ←── BR1 days/approver
                ←── BR2/BR4 events
                ←── BR3 audit/balance
  SecurityUtils ←── ACLs (ch.02)
  LeaveUtils    ←── CS2 GlideAjax
                       │
        ┌──────────────┼──────────────┐
        ▼              ▼              ▼
   UI Policy/Action   Menu/Modules   Flow Approval
   (sick / Submit)    (navigator)    (Ask For Approval)

Data flow: Employee (manager) → Leave Request → BR1 sets days + approver → Submit → BR2 event + Flow → Approve → BR3 audit/balance + BR4 notification event.


Rule 0 — Before any object (Application + Update Set + Studio)

Repeat these checks before creating or editing any application file. This section is also section 00 Update Set — create the capture set first.

  1. Application picker — top-right globe / application menu → select HR Leave (x_786686_hr_leave). Never leave Global selected when creating scoped artifacts.
  2. Update Set — All → System Update Sets → Local Update Sets → New:
    FieldValue
    NameHR Leave – Build
    ApplicationHR Leave
    StateIn Progress
    DescriptionCaptures all HR Leave lab artifacts
    After submit: open the record → click Make Current. Direct list: {BASE}/sys_update_set_list.do
  3. Open Studio — {BASE}/$studio.do → confirm header shows HR Leave.

If artifacts land in Global scope, delete them and recreate under HR Leave. ACLs and events created globally will not behave correctly for this app.

Verify Update Set: Create a test Script Include in Studio → open HR Leave – Build → Related List Customer Updates shows the new file.


Part 1: Application Setup

Purpose

Creates the scoped HR Leave application container. All other OBJ artifacts belong to scope x_786686_hr_leave and app sys_id e5f5bc9493a303507520703efaba104d on the lab instance.

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
NameHR Leave
Scope namex_786686_hr_leave
Version1.0.0
Short descriptionHR Leave management v2 lab app
Package typeCustom application
Create application menuChecked
StudioChecked (default)

Step-by-step create

  1. Open Studio.
  2. Click Create Application.
  3. Enter every field from the table. Scope must be x_786686_hr_leave.
  4. Click Create. Studio opens HR Leave workspace.
  5. Classic verify: Application record → Scope = x_786686_hr_leave.
  6. Proceed to Rule 0 Update Set → HR Leave – Build → Make Current.

Verify checklist

Demo / test

Switch picker Global ↔ HR Leave. New Update Set shows Application = HR Leave.


Part 2: Table — HR Employee (employee_v2)

Purpose

Stores HR employee profiles linked to platform users. BR1 reads u_manager to populate leave request approver.

Navigation path

Direct URL

Prerequisites

Table form fields (create the table first)

Field on New Table formSuggested professional value
LabelHR Employee
Nameemployee_v2 → full name becomes x_786686_hr_leave_employee_v2
Extends table(leave empty — do not extend Task)
Create moduletrue (or add later in Modules)
Create access controlstrue (refine later in Administration)
ApplicationHR Leave

Where: Studio → Create Application File → Table · Direct: {BASE}/sys_db_object.do

Column form fields (each column New)

On Columns related list → New: set Column label, Type, Max length, Reference. Element name = code below.

ColumnColumn labelTypeMax lengthReferenceChoice valuesDisplayMandatory
u_userUserReference—sys_user—NoYes
u_employee_idEmployee IDString40——NoYes
u_departmentDepartmentString100——NoNo
u_managerManagerReference—sys_user—NoYes
u_join_dateJoin DateDate———NoNo
u_gradeGradeString20——NoNo

Form Layout — x_786686_hr_leave_employee_v2

  1. All → System Definition → Tables → open x_786686_hr_leave_employee_v2 (or Studio → Tables).
  2. Related link Form Layout → New or edit default.
  3. Drag fields into sections: Profile (u_user, u_employee_id, u_department, u_grade, u_join_date), Management (u_manager).
  4. Save.

List Layout — x_786686_hr_leave_employee_v2

  1. Same table record → related link List Layout.
  2. Add columns (left to right): User, Employee ID, Department, Manager, Join Date, Grade.
  3. Save → open list URL and confirm columns appear.

Step-by-step create

  1. Studio → Create Application File → Table.
  2. Label: HR Employee · Name: employee_v2 → full name x_786686_hr_leave_employee_v2.
  3. Extends table: leave blank (standalone).
  4. Submit table record.
  5. For each column in the table above: Table → Columns related list → New:
    • Element = column name · Column label = friendly name · Type = as listed · Max length · Reference · Application = HR Leave.
  6. Complete Form Layout and List Layout steps above.

Verify checklist

Demo / test

  1. New record → User = employee1, Employee ID = E-1001, Manager = manager1.
  2. Save — no mandatory errors.

Part 3: Table — Leave Request (req_v2)

Purpose

Core leave request table. Business rules, Flow, catalog, portal, and REST all target this table.

Navigation path

Direct URL

Prerequisites

Table form fields (create the table first)

Field on New Table formSuggested professional value
LabelLeave Request
Namereq_v2 → x_786686_hr_leave_req_v2
Extends table(empty)
Create moduletrue
ApplicationHR Leave

Where: Studio → Create Application File → Table · New table

Column form fields (each column New)

On Columns related list → New: set Column label, Type, Max length, Reference. Element name = code below.

ColumnColumn labelTypeMax lengthReferenceChoice valuesDisplayMandatory
u_numberNumberString40——Yes (display)No
u_requested_for_userRequested ForReference—sys_user—NoYes
u_employee_refEmployeeReference—x_786686_hr_leave_employee_v2—NoYes
u_leave_typeLeave TypeChoice——annual, sick, unpaidNoYes
u_start_dateStart DateDate———NoYes
u_end_dateEnd DateDate———NoYes
u_days_countDaysInteger———NoNo (read-only on form)
u_stateStateChoice——draft, submitted, approved, rejected, cancelledNoYes
u_approver_userApproverReference—sys_user—NoNo (set by BR1)
u_reasonReasonString1000——NoConditional (UI Policy sick)

Choice values — u_leave_type

ValueLabel
annualAnnual
sickSick
unpaidUnpaid

Choice values — u_state

ValueLabel
draftDraft
submittedSubmitted
approvedApproved
rejectedRejected
cancelledCancelled

Form Layout — x_786686_hr_leave_req_v2

  1. All → System Definition → Tables → open x_786686_hr_leave_req_v2 (or Studio → Tables).
  2. Related link Form Layout → New or edit default.
  3. Drag fields into sections: Request (u_number, u_requested_for_user, u_employee_ref, u_leave_type), Dates (u_start_date, u_end_date, u_days_count), Approval (u_approver_user, u_reason), Status (u_state).
  4. Save.

List Layout — x_786686_hr_leave_req_v2

  1. Same table record → related link List Layout.
  2. Add columns (left to right): Number, Requested For, Leave Type, Start Date, End Date, Days, State, Approver.
  3. Save → open list URL and confirm columns appear.

Step-by-step create

  1. Studio → Create Application File → Table.
  2. Label: Leave Request · Name: req_v2 → x_786686_hr_leave_req_v2.
  3. Create all columns per field table. For Choice columns add values in Choice List related list.
  4. Table record → Display field = u_number.
  5. Dictionary: mark u_days_count read-only on form (optional dictionary flag).
  6. Complete Form Layout and List Layout above.

Verify checklist

Demo / test

New req_v2 as admin: fill dates Mon–Fri, save — u_days_count fills after Business Rule BR1 BR1.


Part 4: Table — Leave Balance (bal_v2)

Purpose

Tracks per-employee leave entitlement and usage by year and leave type. BR3 recalculates on approval.

Navigation path

Direct URL

Prerequisites

Table form fields (create the table first)

Field on New Table formSuggested professional value
LabelLeave Balance
Namebal_v2 → x_786686_hr_leave_bal_v2
Extends table(empty)
ApplicationHR Leave

Column form fields (each column New)

On Columns related list → New: set Column label, Type, Max length, Reference. Element name = code below.

ColumnColumn labelTypeMax lengthReferenceChoice valuesDisplayMandatory
u_employee_refEmployeeReference—x_786686_hr_leave_employee_v2—NoYes
u_yearYearInteger———NoYes
u_leave_typeLeave TypeChoice——annual, sick, unpaidNoYes
u_entitledEntitledInteger———NoYes
u_usedUsedInteger———NoNo
u_remainingRemainingInteger———NoNo

Form Layout — x_786686_hr_leave_bal_v2

  1. All → System Definition → Tables → open x_786686_hr_leave_bal_v2 (or Studio → Tables).
  2. Related link Form Layout → New or edit default.
  3. Drag fields into sections: Employee (u_employee_ref, u_year, u_leave_type), Balance (u_entitled, u_used, u_remaining).
  4. Save.

List Layout — x_786686_hr_leave_bal_v2

  1. Same table record → related link List Layout.
  2. Add columns (left to right): Employee, Year, Leave Type, Entitled, Used, Remaining.
  3. Save → open list URL and confirm columns appear.

Step-by-step create

  1. Studio → Table → Name bal_v2.
  2. Add six columns per table.
  3. Reuse u_leave_type choice values from req_v2.
  4. Form/List layouts per steps above.

Verify checklist

Demo / test

Seed: employee, year 2026, annual, entitled 20, used 0, remaining 20.


Part 5: Table — Leave Audit (audit_v2)

Purpose

Append-only audit trail for leave state changes. BR3 writes rows automatically.

Navigation path

Direct URL

Prerequisites

Table form fields (create the table first)

Field on New Table formSuggested professional value
LabelLeave Audit
Nameaudit_v2 → x_786686_hr_leave_audit_v2
Extends table(empty)
ApplicationHR Leave

Column form fields (each column New)

On Columns related list → New: set Column label, Type, Max length, Reference. Element name = code below.

ColumnColumn labelTypeMax lengthReferenceChoice valuesDisplayMandatory
u_leave_refLeaveReference—x_786686_hr_leave_req_v2—NoYes
u_old_stateOld StateString40——NoNo
u_new_stateNew StateString40——NoYes
u_event_typeEvent TypeString60——NoNo
u_actorActorReference—sys_user—NoNo

Form Layout — x_786686_hr_leave_audit_v2

  1. All → System Definition → Tables → open x_786686_hr_leave_audit_v2 (or Studio → Tables).
  2. Related link Form Layout → New or edit default.
  3. Drag fields into sections: Audit (u_leave_ref, u_event_type, u_old_state, u_new_state, u_actor).
  4. Save.

List Layout — x_786686_hr_leave_audit_v2

  1. Same table record → related link List Layout.
  2. Add columns (left to right): Leave Request, Old State, New State, Event Type, Actor, Created.
  3. Save → open list URL and confirm columns appear.

Step-by-step create

  1. Studio → Table → audit_v2.
  2. Add five columns.
  3. Layouts as above.

Verify checklist

Demo / test

After Business Rule BR3 BR3: change req state → new audit row appears.


Part 6: Script Include — HRLXProLeaveUtilsV2

Purpose

Server-side utility for business-day calculation, approver resolution, audit writes, balance recalc, and GlideAjax for CS2.

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
NameHRLXProLeaveUtilsV2
API Namex_786686_hr_leave.HRLXProLeaveUtilsV2
Client callableChecked
ActiveChecked
Accessible fromAll application scopes
ScriptPaste block below

Extends AbstractAjaxProcessor for ajaxGetBusinessDays.

Step-by-step create

  1. Studio → Script Include → New.
  2. Fill form fields table.
  3. Paste full script below into Script field.
  4. Save — fix any syntax errors.

Full script (paste-ready)

/**
 * Script Include: HRLXProLeaveUtilsV2
 * Scope: x_786686_hr_leave
 * Application: Custom · Scope: x_786686_hr_leave · sys_id: 83d5298c932b8f107520703efaba1072
 * Client callable: true  (extends AbstractAjaxProcessor)
 * Purpose: Leave calculations + helpers (business days, approver, balance, audit).
 *
 * Create in ServiceNow:
 *   Studio > Create Application File > Script Include
 *   Name: HRLXProLeaveUtilsV2
 *   Client callable: checked
 */
var HRLXProLeaveUtilsV2 = Class.create();
HRLXProLeaveUtilsV2.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {

    /**
     * Count business days (Mon-Fri) inclusive between two date strings.
     * @param {string} startDate - 'yyyy-MM-dd'
     * @param {string} endDate   - 'yyyy-MM-dd'
     * @return {number}
     */
    calculateBusinessDays: function (startDate, endDate) {
        if (!startDate || !endDate) return 0;
        var start = new GlideDateTime(startDate + ' 00:00:00');
        var end = new GlideDateTime(endDate + ' 00:00:00');
        if (end.before(start)) return 0;

        var days = 0;
        var cursor = new GlideDateTime(start);
        while (!cursor.after(end)) {
            var dow = cursor.getDayOfWeekLocalTime(); // 1=Mon ... 7=Sun
            if (dow >= 1 && dow <= 5) days++;
            cursor.addDaysLocalTime(1);
        }
        return days;
    },

    /**
     * Resolve the approver (employee's manager) and set it on the record.
     * @param {GlideRecord} currentRecord - leave request record
     */
    setApproverFromEmployee: function (currentRecord) {
        var empId = currentRecord.getValue('u_employee_ref');
        if (!empId) return;
        var emp = new GlideRecord('x_786686_hr_leave_employee_v2');
        if (emp.get(empId) && emp.u_manager) {
            currentRecord.u_approver_user = emp.getValue('u_manager');
        }
    },

    /**
     * Write an audit row when the state changes.
     */
    auditStateChange: function (leaveSysId, oldState, newState, eventType) {
        var a = new GlideRecord('x_786686_hr_leave_audit_v2');
        a.initialize();
        a.u_leave_ref = leaveSysId;
        a.u_old_state = oldState || '';
        a.u_new_state = newState || '';
        a.u_event_type = eventType || 'state_change';
        a.u_actor = gs.getUserID();
        a.insert();
    },

    /**
     * Recalculate remaining balance for an employee/year.
     */
    recalculateBalance: function (employeeSysId, year) {
        var used = this._sumApprovedDays(employeeSysId, year);
        var bal = new GlideRecord('x_786686_hr_leave_bal_v2');
        bal.addQuery('u_employee_ref', employeeSysId);
        bal.addQuery('u_year', year);
        bal.query();
        if (bal.next()) {
            bal.u_used = used;
            bal.u_remaining = parseInt(bal.getValue('u_entitled') || 0, 10) - used;
            bal.update();
        }
    },

    _sumApprovedDays: function (employeeSysId, year) {
        var total = 0;
        var gr = new GlideAggregate('x_786686_hr_leave_req_v2');
        gr.addQuery('u_employee_ref', employeeSysId);
        gr.addQuery('u_state', 'approved');
        gr.addQuery('u_start_date', '>=', year + '-01-01');
        gr.addQuery('u_start_date', '<=', year + '-12-31');
        gr.addAggregate('SUM', 'u_days_count');
        gr.query();
        if (gr.next()) total = parseInt(gr.getAggregate('SUM', 'u_days_count') || 0, 10);
        return total;
    },

    /**
     * GlideAjax entry point for client-side day calculation.
     */
    ajaxGetBusinessDays: function () {
        return this.calculateBusinessDays(
            this.getParameter('sysparm_start'),
            this.getParameter('sysparm_end')
        );
    },

    type: 'HRLXProLeaveUtilsV2'
});

Verify checklist

Demo / test

var util = new x_786686_hr_leave.HRLXProLeaveUtilsV2();
gs.info('Days: ' + util.calculateBusinessDays('2026-09-01', '2026-09-05'));

Part 7: Script Include — HRLXProSecurityUtilsV2

Purpose

Centralized read/write authorization logic consumed by table ACLs on req_v2.

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
NameHRLXProSecurityUtilsV2
Client callableUnchecked
ActiveChecked

Step-by-step create

  1. Studio → Script Include → New.
  2. Name = HRLXProSecurityUtilsV2, Client callable = false.
  3. Paste script below. Save.

Full script (paste-ready)

/**
 * Script Include: HRLXProSecurityUtilsV2
 * Scope: x_786686_hr_leave
 * Application: Custom · Scope: x_786686_hr_leave · sys_id: 83d5298c932b8f107520703efaba1072
 * Client callable: false
 * Purpose: Centralized security checks used by ACLs.
 *
 * Used by ACL scripts, e.g.:
 *   answer = new x_786686_hr_leave.HRLXProSecurityUtilsV2().canReadLeave(current);
 */
var HRLXProSecurityUtilsV2 = Class.create();
HRLXProSecurityUtilsV2.prototype = {

    initialize: function () {
        this.userId = gs.getUserID();
    },

    /** HR admin can do anything. */
    isHrAdmin: function () {
        return gs.hasRole('x_786686_hr_leave.hr_admin');
    },

    /** Can the current user READ this leave request? */
    canReadLeave: function (current) {
        if (this.isHrAdmin()) return true;
        // owner
        if (current.getValue('u_requested_for_user') === this.userId) return true;
        // approver / manager
        if (current.getValue('u_approver_user') === this.userId) return true;
        // manager of the employee
        return this._isManagerOfEmployee(current.getValue('u_employee_ref'));
    },

    /** Can the current user WRITE this leave request? */
    canWriteLeave: function (current) {
        if (this.isHrAdmin()) return true;
        var state = current.getValue('u_state');
        // owner can edit only while draft/submitted
        if (current.getValue('u_requested_for_user') === this.userId) {
            return (state === 'draft' || state === 'submitted');
        }
        // approver can act when submitted
        if (current.getValue('u_approver_user') === this.userId) {
            return (state === 'submitted');
        }
        return false;
    },

    _isManagerOfEmployee: function (employeeSysId) {
        if (!employeeSysId) return false;
        var emp = new GlideRecord('x_786686_hr_leave_employee_v2');
        if (emp.get(employeeSysId)) {
            return emp.getValue('u_manager') === this.userId;
        }
        return false;
    },

    type: 'HRLXProSecurityUtilsV2'
};

Verify checklist

Demo / test

Background: instantiate and call canReadLeave on a sample req_v2 GlideRecord.


Part 8: Business Rule BR1 — Calculate Days

Purpose

Calculates u_days_count and sets u_approver_user from employee manager before save.

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
NameHRLX BR1 Calculate Days
Tablex_786686_hr_leave_req_v2
AdvancedChecked
Whenbefore
InsertChecked
UpdateChecked
DeleteUnchecked
Order100
ActiveChecked

Step-by-step create

  1. Studio → Business Rule → New.
  2. Fill every field from table. Check Advanced.
  3. Paste script below into Script field.
  4. Save. Confirm Active = true.

Full script (paste-ready)

/**
 * Business Rule: BR1 - Calculate Days + Approver
 * Application: Custom · Scope: x_786686_hr_leave · sys_id: 83d5298c932b8f107520703efaba1072
 * Table:  x_786686_hr_leave_req_v2
 * When:   before
 * Insert: true   Update: true
 * Order:  100
 *
 * Calculates u_days_count and resolves the approver before saving.
 */
(function executeRule(current, previous /*null when inserting*/) {

    var util = new x_786686_hr_leave.HRLXProLeaveUtilsV2();

    if (current.u_start_date.changes() || current.u_end_date.changes() || current.isNewRecord()) {
        current.u_days_count = util.calculateBusinessDays(
            current.getValue('u_start_date'),
            current.getValue('u_end_date')
        );
    }

    if (current.u_employee_ref.changes() || current.isNewRecord()) {
        util.setApproverFromEmployee(current);
    }

})(current, previous);

Verify checklist

Demo / test

See section demo after BR is active (days calc, events, audit, balance).


Part 9: Business Rule BR2 — On Submit Event

Purpose

Queues submitted event when u_state becomes submitted (notifications + Flow trigger).

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
NameHRLX BR2 On Submit Event
Tablex_786686_hr_leave_req_v2
AdvancedChecked
Whenafter
InsertChecked
UpdateChecked
DeleteUnchecked
Order200
ActiveChecked

Step-by-step create

  1. Studio → Business Rule → New.
  2. Fill every field from table. Check Advanced.
  3. Paste script below into Script field.
  4. Save. Confirm Active = true.

Full script (paste-ready)

/**
 * Business Rule: BR2 - On Submit Fire Event
 * Application: Custom · Scope: x_786686_hr_leave · sys_id: 83d5298c932b8f107520703efaba1072
 * Table:  x_786686_hr_leave_req_v2
 * When:   after
 * Update: true   Insert: true
 * Order:  200
 *
 * Fires an event when state becomes 'submitted' (drives notification / flow).
 */
(function executeRule(current, previous) {

    if (current.u_state.changesTo('submitted')) {
        gs.eventQueue(
            'x_786686_hr_leave.leave_v2.submitted',
            current,
            current.getValue('u_approver_user'),
            current.getValue('u_number')
        );
    }

})(current, previous);

Verify checklist

Demo / test

See section demo after BR is active (days calc, events, audit, balance).


Part 10: Business Rule BR3 — Audit State Change

Purpose

Writes audit_v2 row on state change; recalculates balance on approval.

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
NameHRLX BR3 Audit State Change
Tablex_786686_hr_leave_req_v2
AdvancedChecked
Whenafter
InsertUnchecked
UpdateChecked
DeleteUnchecked
Order300
ActiveChecked

Step-by-step create

  1. Studio → Business Rule → New.
  2. Fill every field from table. Check Advanced.
  3. Paste script below into Script field.
  4. Save. Confirm Active = true.

Full script (paste-ready)

/**
 * Business Rule: BR3 - Audit State Change + Balance
 * Application: Custom · Scope: x_786686_hr_leave · sys_id: 83d5298c932b8f107520703efaba1072
 * Table:  x_786686_hr_leave_req_v2
 * When:   after
 * Update: true
 * Order:  300
 *
 * Writes an audit row on any state change and recalculates balance on approval.
 */
(function executeRule(current, previous) {

    if (!current.u_state.changes()) return;

    var util = new x_786686_hr_leave.HRLXProLeaveUtilsV2();
    util.auditStateChange(
        current.getUniqueValue(),
        previous ? previous.getValue('u_state') : '',
        current.getValue('u_state'),
        'state_change'
    );

    if (current.u_state.changesTo('approved') && current.u_employee_ref) {
        var year = new GlideDateTime(current.getValue('u_start_date')).getYearLocalTime();
        util.recalculateBalance(current.getValue('u_employee_ref'), year);
    }

})(current, previous);

Verify checklist

Demo / test

See section demo after BR is active (days calc, events, audit, balance).


Part 11: Business Rule BR4 — Approve / Reject Event

Purpose

Queues approved/rejected events for notifications.

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
NameHRLX BR4 Approve Reject Event
Tablex_786686_hr_leave_req_v2
AdvancedChecked
Whenafter
InsertUnchecked
UpdateChecked
DeleteUnchecked
Order250
ActiveChecked

Step-by-step create

  1. Studio → Business Rule → New.
  2. Fill every field from table. Check Advanced.
  3. Paste script below into Script field.
  4. Save. Confirm Active = true.

Full script (paste-ready)

/**
 * Business Rule: BR4 - Fire Approved / Rejected Events
 * Application: Custom · Scope: x_786686_hr_leave · sys_id: 83d5298c932b8f107520703efaba1072
 * Table:  x_786686_hr_leave_req_v2
 * When:   after
 * Update: true
 * Insert: false
 * Order:  250
 *
 * Drives notifications for approved and rejected leave requests.
 * Requires events registered in Event Registry (see events.md).
 */
(function executeRule(current, previous) {

    if (current.u_state.changesTo('approved')) {
        gs.eventQueue(
            'x_786686_hr_leave.leave_v2.approved',
            current,
            current.getValue('u_requested_for_user'),
            current.getValue('u_number')
        );
    }

    if (current.u_state.changesTo('rejected')) {
        gs.eventQueue(
            'x_786686_hr_leave.leave_v2.rejected',
            current,
            current.getValue('u_requested_for_user'),
            current.getValue('u_number')
        );
    }

})(current, previous);

Verify checklist

Demo / test

See section demo after BR is active (days calc, events, audit, balance).


Part 12: Client Script CS1 — Validate Dates

Purpose

Client-side validation: end date cannot precede start date on req_v2 form.

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
NameHRLX CS1 Validate Dates
Tablex_786686_hr_leave_req_v2
TypeonChange
Field nameu_end_date
UI TypeDesktop + All checked
ActiveChecked

Step-by-step create

  1. Studio → Client Script → New.
  2. Fill fields. Type = onChange, Field = u_end_date.
  3. Paste script. Save.

Full script (paste-ready)

/**
 * Client Script: CS1 - Validate Dates
 * Application: Custom · Scope: x_786686_hr_leave · sys_id: 83d5298c932b8f107520703efaba1072
 * Table:  x_786686_hr_leave_req_v2
 * Type:   onChange
 * Field:  u_end_date
 *
 * Shows an error if end date is before start date.
 */
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
    if (isLoading || newValue === '') return;

    var start = g_form.getValue('u_start_date');
    var end = g_form.getValue('u_end_date');
    if (!start || !end) return;

    if (end < start) {
        g_form.showFieldMsg('u_end_date', getMessage('End date cannot be before start date'), 'error');
        g_form.clearValue('u_end_date');
    } else {
        g_form.hideFieldMsg('u_end_date', true);
    }
}

Verify checklist

Demo / test

Start 2026-09-10, End 2026-09-01 → error, end cleared.


Part 13: Client Script CS2 — Calc Days (GlideAjax)

Purpose

Calls HRLXProLeaveUtilsV2.ajaxGetBusinessDays to preview u_days_count before save.

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
NameHRLX CS2 Calc Days GlideAjax
Tablex_786686_hr_leave_req_v2
TypeonChange
Field nameu_end_date (duplicate CS for u_start_date if desired)
UI TypeDesktop + All
ActiveChecked

Step-by-step create

  1. Create Client Script after Script Include HRLXProLeaveUtilsV2.
  2. onChange on u_end_date — same field as CS1 (both execute).
  3. Paste script. Save.

Full script (paste-ready)

/**
 * Client Script: CS2 - Calculate Days (GlideAjax)
 * Application: Custom · Scope: x_786686_hr_leave · sys_id: 83d5298c932b8f107520703efaba1072
 * Table:  x_786686_hr_leave_req_v2
 * Type:   onChange
 * Field:  u_end_date
 *
 * Calls HRLXProLeaveUtilsV2.ajaxGetBusinessDays asynchronously to fill u_days_count.
 */
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
    if (isLoading || newValue === '') return;

    var start = g_form.getValue('u_start_date');
    var end = g_form.getValue('u_end_date');
    if (!start || !end || end < start) return;

    var ga = new GlideAjax('x_786686_hr_leave.HRLXProLeaveUtilsV2');
    ga.addParam('sysparm_name', 'ajaxGetBusinessDays');
    ga.addParam('sysparm_start', start);
    ga.addParam('sysparm_end', end);
    ga.getXMLAnswer(function (answer) {
        if (answer !== null && answer !== '') {
            g_form.setValue('u_days_count', answer);
        }
    });
}

Verify checklist

Demo / test

Valid Mon–Fri range → u_days_count populates before save.


Part 14: UI Policy — Sick Requires Reason

Purpose

When leave type is sick, makes u_reason mandatory on the req_v2 form.

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
Tablex_786686_hr_leave_req_v2
Short descriptionSick leave requires reason
ApplicationHR Leave
ActiveChecked
Reverse if falseChecked
On loadChecked
Conditionu_leave_type is sick

UI Policy Action (related list New)

FieldValue
Fieldu_reason
Mandatorytrue
Visibletrue

Step-by-step create

  1. Application = HR Leave.
  2. All → System UI → UI Policies → New.
  3. Fill policy fields. Condition builder: Leave Type is sick.
  4. Submit policy.
  5. Related list UI Policy Actions → New → u_reason mandatory true.
  6. Save action. Verify policy Active.

Verify checklist

Demo / test

Select Sick, leave Reason empty, try Save → client mandatory warning.


Part 15: UI Action — Submit

Purpose

Form button that moves draft leave requests to submitted state (triggers BR2 and Flow).

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
NameSubmit
Tablex_786686_hr_leave_req_v2
Action namesubmit_leave
Form buttonChecked
Form stylePrimary (optional)
Order100
Conditioncurrent.u_state == 'draft'
ClientUnchecked (server script)
ActiveChecked

Step-by-step create

  1. Studio → UI Action → New on req_v2.
  2. Fill fields. Uncheck Client checkbox.
  3. Paste server script below in Script field.
  4. Save. Open draft req_v2 → Submit button visible.

Full script (paste-ready)

current.u_state = 'submitted';
current.update();
action.setRedirectURL(current);

Verify checklist

Demo / test

Draft → Submit → BR2 event + Flow Flow Leave Approval starts.


Part 24: Application Menu

Purpose

Top-level Application Navigator entry for HR Leave modules.

Navigation path

Direct URL

Prerequisites

Form fields

FieldValue
TitleHR Leave
ApplicationHR Leave
Order100
Rolesx_786686_hr_leave.employee (minimum)

Step-by-step create

  1. If missing: Studio → Application Menu → New → fill fields.
  2. Save. Confirm appears in Application Navigator.

Verify checklist

Demo / test

employee1 login → sees HR Leave menu after modules Modules.


Part 25: Modules

Purpose

Four list modules linking to req_v2, employee_v2, bal_v2, audit_v2 tables.

Navigation path

Direct URL

Prerequisites

Form fields

TitleTypeTableOrderRoles
Leave RequestsListx_786686_hr_leave_req_v210employee, manager, hr_admin
EmployeesListx_786686_hr_leave_employee_v220hr_admin
BalancesListx_786686_hr_leave_bal_v230hr_admin
Audit TrailListx_786686_hr_leave_audit_v240hr_admin

Step-by-step create

  1. Module → New for each row. Application Menu = HR Leave.
  2. Type = List → Table = as listed. Order as table.
  3. Roles: restrict Employees/Balances/Audit to hr_admin; Requests to all three roles.

Verify checklist

Demo / test

employee1 → only Leave Requests visible.


Part 27: Flow Designer — Leave Approval

Purpose

When leave state becomes submitted, sends approval to u_approver_user; on approve/reject updates u_state.

Navigation path

Direct URL

Prerequisites

Form fields — Create Flow wizard

FieldValue
NameHRLX Leave Approval
ApplicationHR Leave
DescriptionApproval flow for submitted leave requests

Trigger — Record Updated

FieldValue
Trigger typeRecord Updated
Tablex_786686_hr_leave_req_v2
ConditionState [u_state] is submitted AND State changes
Run flowOnce per record (recommended)

Action 1 — Ask For Approval

InputValue
RecordTrigger → Leave Request Record (data pill)
Tablex_786686_hr_leave_req_v2
Approval fieldu_approver_user
Approval reasonLeave approval required
Rules → ApproveAnyone approves (required — do not leave empty)

If Approved → Update Record

RecordTrigger record pill
Field mapu_state = approved

Else → Update Record

Field mapu_state = rejected

Step-by-step create

  1. Flow Designer → Create Flow → Name HRLX Leave Approval, Application HR Leave.
  2. Add trigger → Record Updated → table req_v2 → condition u_state is submitted + changes.
  3. + → Ask for Approval → fill Action 1 table → Rules: Anyone approves → Done.
  4. + → Flow Logic → If → condition "approval was Approved".
  5. Yes branch → Update Record → u_state = approved.
  6. Else branch → Update Record → u_state = rejected.
  7. Save → fix any red errors (missing approval rule is common).
  8. Test → pick sample submitted req_v2 → Run test.
  9. Publish → Publish new version → wait for Published status.

Verify checklist

Demo / test

  1. employee1 submits leave (UI Action or catalog).
  2. manager1 → My Approvals → Approve.
  3. Verify state, audit_v2, bal_v2 updated.

Optional — CS1 on u_start_date

Duplicate CS1 logic on u_start_date onChange (same script body) so validation runs when the user changes start date after end date is set.

FieldValue
TypeonChange
Fieldu_start_date
UI TypeAll
Activetrue

Development verify / demo checklist

Complete these checks after creating all objects in this chapter (Application Setup through UI Action Submit, Application Menu, Modules, Flow Leave Approval).

  1. Update Set — HR Leave – Build is current; Customer Updates shows new artifacts.
  2. Tables — Four _v2 lists open; dictionary shows u_* columns only (Schema).
  3. Script Includes — HRLXProLeaveUtilsV2 client-callable; Background script: new x_786686_hr_leave.HRLXProLeaveUtilsV2().calculateBusinessDays('2026-06-01','2026-06-05') → 5.
  4. Business Rules — New req_v2 sets u_days_count and u_approver_user (BR1); submit fires event (BR2); approve writes audit + balance (BR3/BR4).
  5. Client Scripts — End date before start shows error (CS1); valid range fills u_days_count via GlideAjax (CS2). Optional: duplicate CS2 on u_start_date onChange.
  6. UI Policy — Sick → u_reason mandatory.
  7. UI Action Submit — Draft → submitted visible only on draft.
  8. Menu + Modules — HR Leave navigator shows four modules.
  9. Flow — Submit → manager approval → approved/rejected.

Extended properties catalog: PROFESSIONAL_HR_CREATE_PROPERTIES_AND_LINKS.html · Master OBJ index: STUDIO_CREATE_ALL_OBJECTS_01_16.html