Advanced Reference Qualifier only works for certain role

leslie8
Mega Expert

I have installed the Loaner Management application by Chuck Tomasi. This was made in an old version of ServiceNow and I have made it work in Fuji with some customization.

I am running into a major issue with the Advanced Reference Qualifier in the Record Producer on the field cmdb_ci.

The reference qualifier is designed to return only the particular loaner items that are available between the selected start and end date. Thus, if the loaner is already reserved for any date in between the selected date, it doesn't show up in the list.

This works great -- ONLY for users with the role "loaner_admin".   I noticed that any user that doesnt have that role, the reference qualifier isn't working! The reference field returns ALL of the items, even if they are already booked for the time period.

I tried logging in as a loaner_admin user and it works, but when I log in as a user with any other role, ALL of the items are available in the list and this results in a DOBULE booking. Yikes!

I then granted my test user the loaner_admin role and it worked. So, I know it has to do with an ACL on that role.

I experimented with the ACL rules, I added the role "user" to the "write" action on the cmdb_ci field but that did not solve the issue.

Reference Qualifier on cmdb_ci

javascript: new LoanerUtilsClient(current).availableCis(current.variables.item_type, current.variables.depot, current.variables.start_date, current.variables.end_date);

20 REPLIES 20

rob_pastore
ServiceNow Employee
ServiceNow Employee

This must be where the issue is:



    if (checkAvailProperty) {                   // Check only available matching items


              if (this.isAvailable(id, start, end))


                  availableItems.push(id);



as it's the only place dealing with the dates.



Can you paste the isAvailable function?


That's what appears to be failing.   (if we can confitm that the checkAvailProperty is returning true for both ess and la)


Here's the entire script include


var LoanerUtils = Class.create();




LoanerUtils.prototype = {


  /**********


    * Initialization stuff here...


    *


    **********/


  initialize : function() {


      this.overdueRemindTime = gs.getProperty('glide.loaner.overdue.remindtime', 24);


      this.pickupLeadTime       = gs.getProperty('glide.loaner.pickup.leadtime', 24) * -1; //paramm to gs.daysAgo()


      this.returnRemindTime   = gs.getProperty('glide.loaner.return.remindtime', 24) * -1;


  },




  /**********


    *


    * getOverdueRequests - retrieve a list of overdue records


    *


    * @param - None


    * @return: array of sys_ids


    *


    **********/


  getOverdueRequests : function() {




      var a = new Array();


      var gr = new GlideRecord('loaner_request');



      gr.addQuery('end_date', '<=', gs.nowDateTime());


      gr.addQuery('state', 17);


      gr.query();




      while (gr.next()) {


          a.push(gr.sys_id.toString());


      }


      return a;


  },




  /**********


    *


    * timeToRemindOverdue - Use the passed in ID to check if the record is time to remind again


    *


    * @param - id: sys_id of the loaner record to check


    * @return - boolean: true = time to remind


    *


    **********/


  timeToRemindOverdue : function(id) {




      var gr = new GlideRecord('loaner_request');




      if (!gr.get(id)) {


          gs.log('>>>DEBUG: LoanerUtils: timeToRemindOverdue(): FAILED TO RETRIEVE RECORD');


          return false;


      }


      var baseDate = (gr.last_overdue_reminder == '') ? gr.end_date : gr.last_overdue_reminder;




      return (baseDate <= gs.hoursAgo(this.overdueRemindTime)) ? true : false;


  },




  /**********


    *


    * sendOverdueReminder - Queue an event to send a reminder and update the necessary


    *                                             record fields to indicate they've been reminded


    *


    * @param - id: sys_id of the loaner record to check


    * @return - None


    *


    **********/


  sendOverdueReminder : function(id) {




      var gr = new GlideRecord('loaner_request');




      if (!gr.get(id)) {


          gs.log('>>>DEBUG: LoanerUtils: setLastOverdueReminder(): FAILED TO RETRIEVE RECORD');


          return;


      }


      ++gr.overdue_count;




      // Record the last time a reminder was sent


      gr.last_overdue_reminder = gs.nowDateTime();


      gr.update();




      // Send the overdue reminder


      gs.eventQueue('loaner.overdue', gr, gr.start_time, gr.end_time);


  },




  /**********


    *


    * getNullPickupReminders - Get a list of records in state 'Ready for Pickup'


    *                                                   that haven't been reminded before


    *


    * @param - None


    * @return - array of sys_ids


    *


    **********/


  getNullPickupReminders : function() {




      var a = new Array();


      var gr = new GlideRecord('loaner_request');




      gr.addQuery('state', 16);


      gr.addQuery('pickup_reminder', '');


      gr.addQuery('start_date', '<=', gs.hoursAgo(this.pickupLeadTime));


      gr.query();




      while (gr.next()) {


          a.push(gr.sys_id.toString());


      }


      return a;


  },




  /**********


    *


    * sendPickupReminder - Queue an event to send a reminder and update the pickup reminder field


    *


    * @param - id: sys_id of the loaner record to check


    * @return - None


    *


    **********/


  sendPickupReminder : function(id) {




      var gr = new GlideRecord('loaner_request');




      if (!gr.get(id)) {


          gs.log('>>>DEBUG: LoanerUtils: sendPickupReminder(): FAILED TO RETRIEVE RECORD');


          return;


      }




      // Log the time the pickup reminder was sent and avoid sending again


      gr.pickup_reminder = gs.nowDateTime();


      gr.update();




      // Send the pickup reminder


      gs.eventQueue('loaner.pickup', gr, gr.start_time, gr.end_time);


      return;


  },




  /**********


    *


    * getNullReturnReminders - Get a list of records that need reminding to bring their item back


    *


    * @param - None


    * @return - array of sys_ids


    *


    **********/


  getNullReturnReminders : function() {




      var a = new Array();


      var gr = new GlideRecord('loaner_request');




      gr.addQuery('state', 17);


      gr.addQuery('return_reminder', '');


      gr.addQuery('end_date', '<=', gs.hoursAgo(this.returnRemindTime));


      gr.query();




      while (gr.next()) {


          a.push(gr.sys_id.toString());


      }


      return a;


  },




  /**********


    *


    * setReturnReminder - Queue a return reminder and update field when reminder was sent


    *


    * @param - id: sys_id of the loaner record to check


    * @return - None


    *


    **********/


  sendReturnReminder : function(id) {




      var gr = new GlideRecord('loaner_request');




      if (!gr.get(id)) {


          gs.log('>>>DEBUG: LoanerUtils: sendReturnReminder(): FAILED TO RETRIEVE RECORD');


          return;


      }




      // Set this record to never remind again


      gr.return_reminder = gs.nowDateTime();


      gr.update();




      // Send the return reminder


      gs.eventQueue('loaner.return', gr, gr.start_time, gr.end_time);


  },




  /*********


    *


    * withdrawRequest - Update a request and all related bits


    *                                     (child tasks are handled by a standard bus. rule)


    *


    * @param lr - GlideRecord of the request


    * @return None


    *


    **********/


  withdrawRequest : function(lr) {




      var wf = new Workflow();




      wf.cancel(lr);


      lr.active=false;


      lr.state=7;


      lr.update();




      gs.addInfoMessage(gs.getMessage('loaner_withdraw'));


  },


   


  /*********


    *


    * isAvailable - check to see if there are no conflicts w/ci


    *                             being available between start and end times


    *                             and CI is "in" (loaner request state=Requested, Closed, Withdrawn)


    *


    * @param ci - GlideRecord or sys_id of the CI being requested


    * @param start - start date-time of the requested reservation


    * @param end - end date-time of the requested reservation


    * @return boolean - true=no conflicts


    *


    **********/


  isAvailable : function(ci, start, end) {




      var ci_id = ci;


      var count = 0;


      var lr       = new GlideAggregate('loaner_request');




      if (JSUtil.nil(ci))


          return false; // Null CI defaults to "not available"




      if (ci instanceof GlideRecord)


          ci_id = ci.sys_id;




      var myQuery = 'active=true^';


      myQuery += 'cmdb_ci=' + ci + '^';


      myQuery += 'start_date<' + end + '^';


      myQuery += 'end_date>' + start + '^';


      myQuery += 'NQ';


      myQuery += 'active=true^';


      myQuery += 'cmdb_ci=' + ci + '^';


      myQuery += 'end_date<' + gs.daysAgo(0);




      lr.addAggregate('COUNT');


      lr.addQuery(myQuery);


      lr.query();




      if (lr.next()) {


            count = lr.getAggregate('COUNT');


      }




      return (count == 0);


  },




  /**********


    *


    * availableCis - get a list of CIs in a class/depot that are available


    *


    * @param class_name - CI class


    * @param depot_name - Name of the pick up location


    * @param start - reservation start date


    * @param end - reservation end date


    * @return - comma separated list of sys_ids that are available at that time


    *


    **********/


  availableCis : function(class_name, depot_name, start, end) {




      var ci                                       = new GlideRecord('cmdb_ci');


      var installStatusProperty = gs.getProperty('glide.loaner.install_status');


      var checkAvailProperty       = gs.getProperty('glide.loaner.check_availability') == 'true';


      var availableItems               = [];


   


        gs.log("Test loaner class: " + class_name);


   


      if (!JSUtil.nil(installStatusProperty)) {


          ci.addQuery('install_status', 'IN', installStatusProperty);


      }


      ci.addQuery('loaner', true);


      ci.addQuery('sys_class_name', class_name);


      ci.addQuery('depot', depot_name);


      ci.query();




      while (ci.next()) {


          var id = ci.sys_id.toString();




          if (checkAvailProperty) {                   // Check only available matching items


              if (this.isAvailable(id, start, end))


                  availableItems.push(id);




          } else {                                                   // Take any matching items


              availableItems.push(id);


          }


      }




      return availableItems.join(',');


  },




  type: 'LoanerUtils'


}


rob_pastore
ServiceNow Employee
ServiceNow Employee

OK,


that isAvailable function is querying 'loaner_request'.



I would bet that this table has an ACL only allowing loaner_admin to read.


Hm Ok I will look there again, but I disabled all the ACLs on that table actually.



I also tried adding Allow Read for all roles and that didn't work.


coryseering
ServiceNow Employee
ServiceNow Employee

Hi Leslie,



A quick way to check is to add a new ACL of type "record" and operation "read". Leave the "Requires role" list empty, select the "loaner_request" table as the Name and leave the other select box (which lets you select fields) set to -- None --.


Check the box for "Advanced" and make this script:



        answer = true;



This should give everyone read access to that table.



To confirm, enable ACL Debugging (in the left-hand nav search for "Debug Security") then Impersonate one of the users with this problem. Navigate directly to the loaner_request table and verify you can read the records there.