How to validate duplicate check

Devi-01
Mega Explorer

Hi everyone,

 

I’m working on a Service Catalog item (e.g., “Software License Request”) where the user enters a license number.

 

My requirement is to:

 

* Check in real-time if the entered license number already exists in the system (e.g., in sc_req_item or a custom table)

* If a duplicate is found, show an error message immediately

* Optionally provide a link to the existing request

 

I’m planning to use:

 

* onChange Catalog Client Script

* GlideAjax

* Script Include

 

My questions:

 

1. Is this the correct/best approach for real-time validation?

2. Are there any best practices or alternative methods (like Data Policy / Business Rule)?

3. How can I efficiently return the existing record link to display in the message?

 

Any sample implementation or guidance would be really helpful.

 

Thanks in advance!

3 REPLIES 3

Naveen20
ServiceNow Employee

You can try as below

var LicenseValidator = Class.create();
LicenseValidator.prototype = Object.extendsObject(AbstractAjaxProcessor, {

checkDuplicate: function() {
var lic = (this.getParameter('sysparm_license') || '').trim();
var result = { exists: false };
if (!lic) return JSON.stringify(result);

var gr = new GlideRecord('sc_req_item');
gr.addQuery('variables.license_number', lic);
gr.addActiveQuery();
gr.setLimit(1);
gr.query();

if (gr.next()) {
result.exists = true;
result.number = gr.getValue('number');
result.url = '/nav_to.do?uri=sc_req_item.do?sys_id=' + gr.getUniqueValue();
}
return JSON.stringify(result);
},

type: 'LicenseValidator'
});

onChange Catalog Client Script on license_number:

javascript
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || !newValue || newValue === oldValue) return;

g_form.hideFieldMsg('license_number', true);

var ga = new GlideAjax('LicenseValidator');
ga.addParam('sysparm_name', 'checkDuplicate');
ga.addParam('sysparm_license', newValue);
ga.getXMLAnswer(function(response) {
var r = JSON.parse(response);
if (r.exists) {
var msg = 'License already exists on <a href="' + r.url +
'" target="_blank">' + r.number + '</a>.';
g_form.showFieldMsg('license_number', msg, 'error', true); // 4th arg = isHTML
}
});
}


Best practices
- Add a before-insert BR on sc_req_item with the same check + current.setAbortAction(true) — client scripts can be bypassed via API.
- Data Policy / UI Policy don't fit here.

vaishali231
Kilo Sage

Hey @Devi-01 

 Approach

Use a layered validation strategy:

1. Client-side (real-time user feedback)

       Catalog Client Script

      GlideAjax call to Script Include

      Purpose: Immediate validation and better user experience

2. Server-side (data integrity – mandatory)

  • Before Insert Business Rule OR Unique Index
  • Purpose: Prevent duplicates from all entry points (UI, API, imports)

 

 Why this matters

Client-side validation alone can be bypassed (e.g., integrations, imports, race conditions).
Server-side enforcement ensures true data integrity.

 

 Implementation

1. Catalog Client Script (onChange)

function onChange(control, oldValue, newValue, isLoading) {
   if (isLoading || !newValue) {
       return;
   }
   var ga = new GlideAjax('LicenseValidatorAjax');
   ga.addParam('sysparm_name', 'checkDuplicate');
   ga.addParam('sysparm_license', newValue);
   ga.getXMLAnswer(function(response) {
       if (response) {
           var result = JSON.parse(response);
           if (result.exists) {
               var link = '<a href="' + result.link + '" target="_blank">' + result.number + '</a>';
               g_form.showFieldMsg(
                   'license_number',
                   'Duplicate license found: ' + link,
                   'error'
               );
           }
       }

   });

}

2. Script Include (Client Callable)

var LicenseValidatorAjax = Class.create();
LicenseValidatorAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
   checkDuplicate: function() {
       var license = this.getParameter('sysparm_license');
       var gr = new GlideRecord('sc_req_item');
       gr.addQuery('u_license_number', license);
       gr.setLimit(1);
       gr.query();
       if (gr.next()) {
           return JSON.stringify({
               exists: true,
               sys_id: gr.getUniqueValue(),
               number: gr.number.toString(),
               link: '/sc_req_item.do?sys_id=' + gr.getUniqueValue()
           });
       }
       return JSON.stringify({ exists: false });
   }
});

3. Server-side Protection (Critical)

Option A: Before Insert Business Rule

(function executeRule(current, previous) {
    if (!current.u_license_number) {
        return;
    }
    var gr = new GlideRecord('sc_req_item');
    gr.addQuery('u_license_number', current.u_license_number);

    // Prevent self-match (important for update scenarios)
    gr.addQuery('sys_id', '!=', current.sys_id);
    gr.setLimit(1);
    gr.query();

    if (gr.next()) {
        gs.addErrorMessage('Duplicate license request already exists: ' + gr.number);
        current.setAbortAction(true);
    }

})(current, previous);

Option B 

  • Create a Unique Index on u_license_number
  • Ensures duplicates are blocked at the database level

************************************************************************************************************************************

If this response helps, please mark it as Accept as Solution and Helpful.

Doing so helps others in the community and encourages me to keep contributing.

Regards

Vaishali Singh












Hey @Devi-01 

Hope you are doing well.

Did my previous reply answer your question?

If it was helpful, please mark it as correct ✓ and close the thread . This will help other readers find the solution more easily.

 

Thankyou & Regards

Vaishali Singh

Servicenow Developer
Linkedin - https://www.linkedin.com/in/vaishali-singh-2273361bb