Prevent a booking from being created if the selected resource has approved time off during the booking period

This script prevents a booking from being created if the selected resource has approved time off during the booking period.

Custom "Override vacation" custom fields on the Booking entity form.

Follow the steps below or download the solutions file, see Creating Solutions for details.

Setup

  1. Create a new Booking form script deployment.

  2. Enter a Filename and click SAVE. The extension ‘.js’ is automatically appended if not supplied.

  3. Click on the script link to launch the Scripting Studio.

  4. (1) Copy the Program Listing below into the editor, (2) set the Before save event, and set validate_vacation as the Entrance Function.

    Scripting Studio tools and settings panel showing the form association, the user event association and the selected entrance function.
  5. Set up a Checkbox and a Text Area custom field for Booking.

    Custom field list view showing the vacation override custom fields.
  6. Use the Form schema to identify the correct param names for the custom fields and change the array at the top of the script accordingly.

    Scripting Studio form schema explorer listing the vacation override custom fields.
    Tip:

    If the new custom fields are not listed in the Form schema, navigate to Resources, open a booking form (this with will refresh the custom field list), and then open the Scripting Studio again.

Program Listing

          // timetype_id depends on account settings
var CUST_FIELD = 'bk_override_vacation__c';
var CUST_FIELD_NOTES = 'bk_override_vacation_notes__c';

function validate_vacation() {
    // To support exception situations where booking should be allowed over scheduled timeoff,
    // new checkbox custom field with associated notes field has been added to Booking form.
    // When that field is checked, we want the notes field to be required, so we validate the
    // custom fields settings at the start.
    var override = NSOA.form.getValue(CUST_FIELD);
    var req_notes = NSOA.form.getValue(CUST_FIELD_NOTES);

    // If we are overriding the booking vacation restrictions
    if (override) {
        // And the notes field is not set
        if (!req_notes) { // This is a basic has-a-value check, typically check should be
            // more extensive, i.e. not blank spaces, of certain length, etc.
            // Set custom field error message to indicate required, and prevent form saving
            NSOA.form.error(CUST_FIELD,
                'When overriding vacation restrictions, notes are required');
        }
        return; // Stop, as no further checks are required
    }

    // getValue returns JS Date objects for Date type fields
    var start = NSOA.form.getValue('startdate'); // While adding/changing a script,
    var end = NSOA.form.getValue('enddate'); // the Form Schema section provides a list
    // of available form fields and the expected
    // return values of those fields

    // Create the oaSchedulerequest object for the WSAPI read search
    // Information on available records can be found in the user scripting guide
    // Note the form field is user_id but the SOAP API field is userid
    var approvedSchedReq = new NSOA.record.oaSchedulerequest();
    approvedSchedReq.userid = NSOA.form.getValue('user_id');
    approvedSchedReq.approval_status = 'A'; // (A)pproved, (O)pen, (S)ubmitted, (R)ejected
    approvedSchedReq.timetypeid = '5'; // Personal time is timetypeid 5

    // Pull the start and end dates for Schedulerequests that match our criteria
    var aPTO = NSOA.wsapi.read({
        type: 'Schedulerequest', // The SOAP API complex type
        method: 'equal to',
        fields: 'startdate,enddate', // start & end fields for Schedulerequest complex type
        attributes: [{
            name: 'limit', // ReadRequest objects must have a limit specified
            value: '100' // '100' returns up to 100, '50,100' returns 50 - 100
        }],
        objects: [approvedSchedReq] // The previously created search object
    });

    // NSOA.wsapi.read() returns an array of objects with error and objects properties
    for (x = 0; x < aPTO.length; x++) {
        // If there were errors, notify the user and stop
        if (aPTO[x].errors) {
            var errorMsg = '';
            for (i = 0; i < aPTO[x].errors.length; i++) {
                errorMsg += 'SOAP error [' + aPTO[x].errors[i].code + ']:';
                errorMsg += aPTO[x].errors[i].text + ' - ';
                errorMsg += aPTO[x].errors[i].comment + "\n";
            }
            NSOA.form.error('', errorMsg); // Set the main form error message with the details
            return;
        }

        // If there were approved personal Schedulerequest objects found
        if (aPTO[x].objects) {
            NSOA.meta.alert(aPTO[x].objects.length);
            // Loop through and validate the time off doesn't overlap booking request period
            for (i = 0; i < aPTO[x].objects.length; i++) {
                var thisStart = convertToDate(aPTO[x].objects[i].startdate);
                var thisEnd = convertToDate(aPTO[x].objects[i].enddate);

                // If the PTO overlaps the start of the period
                if ((thisStart <= start && thisEnd <= end && thisEnd >= start) ||
                    (thisStart <= start && thisEnd >= end) || // Or overlaps whole period
                    (thisStart >= start && thisEnd <= end) || // Or is wrapped by the period
                    (thisStart >= start && thisStart <= end && thisEnd >= end)) { // Or end
                    var malDate;
                    if (thisStart.getTime() == thisEnd.getTime()) { // If the is a single day
                        malDate = thisStart.toDateString(); // Only display one date
                    } else { // Else start/end range
                        malDate = thisStart.toDateString() + ' - ' + thisEnd.toDateString();
                    }

                    // Set the form startdate error message accordingly, then stop.
                    NSOA.form.error('startdate', 'The requested resource has approved personal time off' + ' during the selected booking period: ' + malDate);
                    return;
                }
            }
        }
    }
}

// Helper function for converting date strings to JS Date objects
function convertToDate(vDate) {
    // Expected date format is a string: YYYY-MM-DD 0:0:0
    var aYMD = vDate.split(' ');
    aYMD = aYMD[0].split('-');
    return new Date(aYMD[0], aYMD[1] - 1, aYMD[2]);
}