Need Help: Make Attachment Mandatory on submission Only When Subject Person’s Country = India

NowNinja727
Giga Contributor
Hi Community,

I’m working on a requirement for a record producer in Service Portal where:
  • There is a reference variable called Subject Person (points to sys_user).
  • If the selected user’s country = India, then at least one attachment must be added before submission.
  • If the country is not India, submission should proceed without attachment.

 

What We Tried So Far:

  1. OnSubmit Client Script using getReference()
    • We fetched the country from the Subject Person record and then checked attachments using Angular DOM (scope.attachments.length).
    • Issue: Works partially, but fails when attachments are added (still blocks submission).
  2. DOM Manipulation Approach
    • Used document.getElementsByClassName('get-attachment') for Service Portal and $j("li.attachment_list_items") for native UI.
  3. GlideAjax + Script Include
    • Created a Script Include (CheckAttachment) to check attachments and country.
    • Tried multiple patterns (pause submission, resubmit after validation).
    • Issue: Logic works for blocking when no attachment, but still blocks even after attachment is added.
  4. System Property Approach
    • Attempted to make country configurable via system property and validate in Script Include.
    • Issue: Still facing the same problem with attachment validation.

Where We Are Stuck:

  • We need a robust solution that works in Service Portal and Mobile UI.
  • Ideally, we want one GlideAjax call that:
    • Gets the country.
    • Checks if attachments exist for the current record.
    • Returns true/false so we can allow or block submission.

 

Any sample code or guidance would be greatly appreciated!

Thanks!
3 ACCEPTED SOLUTIONS

@NowNinja727 
I don't know that you can use a scripted method and have it work on both the Service Portal and in Mobile UI. Even if you can pass the necessary info to the Glide Ajax to lookup the attachments the Mobile UI doesn't execute onChange and onLoad client scripts the same way so you would need separate configurations for both.

 

If you don't want to use a business rule then I think you would need to use an attachment variable on the Record Producer instead of just turning on attachments. Create an Attachment variable on the record producer then use Catalog UI Policy with a UI Policy Action to make it required and visible. This piece would be respected and applied by both the Mobile UI and Service Portal.

 

In order to do this on both the Service Portal and the Mobile UI you would need to surface the country of the selected Subject Person as a variable in the Record Producer. Once the variable exists you could populate it on the Service Portal side with an onChange script and on the Mobile UI side you would need to use a mobile script or might be able to use a data item lookup. You would then be able to reference the value of this variable in your condition on the Catalog UI Policy.

View solution in original post

Aniket Chavan
Tera Sage
Tera Sage

Hi @NowNinja727 ,

I completely understand the pain here , especially when trying to mix onSubmit Client Scripts + attachment checks + server-side country validation. I've faced something very similar recently, and the biggest challenge was exactly what you’re seeing:

  • onSubmit timing issues

  • GlideAjax getXML wait problems

  • attachment DOM logic not syncing fast enough

  • submission blocked even after attaching a file

There are multiple solution patterns available, but if you’re open to a simpler and more reliable approach, I’d strongly suggest using an Attachment variable instead of relying on the default portal attachment panel.

 

  • You can make the attachment variable mandatory only when Subject Person's country = India

  • Just use an onChange script on Subject Person to evaluate the country, you can use here getReference

  • If country = India → make attachment field mandatory + show any message you want and also you can show this variable only if subject person is from India for other you can keep hidden

This avoids the entire async onSubmit/GlideAjax complexity.

One thing to note:


Attachment variables do NOT automatically attach files to the target record, so add this in your record producer script section:

new global.VariableUtil().copyAttachment(producer.<variableName>, '<tableName>', current.sys_id);

 

This approach should save you a lot of time and frustration.

 

🔹 Please mark Correct if this solves your query, and 👍 Helpful if you found the response valuable.

 

Best regards,
Aniket Chavan
🏆 ServiceNow MVP 2025 | 🌟 ServiceNow Rising Star 2024

View solution in original post

 

@NowNinja727 , I was actually about to share another improvement idea, but while drafting I noticed @John Gilmore  already mentioned something similar , and I definitely agree with him.

 

If you haven’t considered it yet, I think the most efficient direction is:

Create a variable to store Subject Person Country

  • Auto-populate based on Subject Person reference

  • Mark it as hidden, so that it will not show up on the portal.....

 Then apply validation using that variable

This gives you a clean and consistent condition to check instead of relying on getReference() timing or any script inlcude or anything

 

Below is a sample onSubmit script you can adapt to your variable names:

function onSubmit() {
    var country = g_form.getValue('subject_person_country');

    if (country === 'India') {
        var attachmentCount = getSCAttachmentCount(); // Portal attachment count API

        if (attachmentCount <= 0) {
            g_form.addErrorMessage(
                'Please add at least one attachment before submitting this request.'
            );
            return false;
        }
    }
}

 

This should work smoothly, without the async GlideAjax or DOM dependency.

 

Give it a try and let us know how it goes , if something still blocks, we can troubleshoot further. 🙌

 

🔹 Please mark Correct if this solves your query, and 👍 Helpful if you found the response valuable.

 

Best regards,
Aniket Chavan
🏆 ServiceNow MVP 2025 | 🌟 ServiceNow Rising Star 2024

 

View solution in original post

13 REPLIES 13

Hi Sandeep,

Thanks for your response! Here are the approaches I tried and where I faced issues:


1. Partially Working Code (Using getReference() for Country Check)

This script correctly identifies if the Subject Person’s country is India and blocks submission when no attachment exists. It also allows submission when the country is not India.\ Issue: Even after adding attachments, the form still blocks submission for India users.

 

 
function onSubmit() {
    if (gscratchpad && gscratchpad.isFormValid) return true;

    // Fetch full user record for hrsubjectperson
    gform.getReference('hrsubjectperson', function(userRecord) {
        var country = (userRecord.ucountry || '').toLowerCase(); // Adjust field name if needed

        if (country === 'india') {
            // Check if attachments exist in Service Portal
            var attachmentCount = getPortalAttachmentCount();
            if (attachmentCount === 0) {
                gform.addErrorMessage('Attachment is mandatory when the subject person is from India.');
                return false; // Stop submission
            }
        }

        // ✅ For non-India OR validation passed → resubmit
        gscratchpad.isFormValid = true;
        gform.submit('submit');
    });

    return false; // Pause initial submission until callback completes
}

// Helper for Service Portal attachment count
function getPortalAttachmentCount() {
    try {
        var scope = angular.element("#sccatitem").        var scope = angular.element("#sccat_item").scope();
        if (scope && scope.attachments) {
            return scope.attachments.length;
        }
    } catch (e) {
        return 0; // Default to 0 if error
    }

 


2. GlideAjax + Script Include Approach

Here I combined the client script with a Script Include to check both conditions (country and attachment) server-side.\ Issue: Validation works, but the form does not resubmit correctly after the async call. It either loops infinitely or does nothing.

Client Script:

 

 
function onSubmit() {
    if (gscratchpad && gscratchpad.isFormValid) return true;

    var subjectPerson = gform.getValue('hrsubjectperson'); // Replace with your variable name
    if (!subjectPerson) {
        return true; // No subject person → allow submission
    }

    var ga = new GlideAjax('CheckAttachment');
    ga.addParam('sysparmname', 'validateIndiaAttachment');
    ga.addParam('sysparmpersonsysid', subjectPerson);
    ga.addParam('sysparmtable', gform.getTableName());
    ga.addParam('sysparmsysid', gform.getUniqueValue());

    ga.getXMLAnswer(function(response) {
        if (response === 'false') {
            gform.addErrorMessage('Attachment is mandatory when the Subject Person is from India.');
            return false; // Stop submission
               }

        // ✅ Validation passed → resubmit once
        gscratchpad = gscratchpad || {};
        gscratchpad.isFormValid = true;

        var actionName = gform.getActionName();
        gform.submit(actionName); // Re-submit after validation
    });

    return false; // Pause initial submission until callback completes
}

 

Script Include:

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

    validateIndiaAttachment: function() {
        var personSysId = this.getParameter('sysparmpersonsysid');
        var tableName = this.getParameter('sysparmtable');
        var recordSysId = this.getParameter('sysparmsysid');

        // Get country of Subject Person
        var country = '';
        var userGR = new GlideRecord('sysuser'); // Adjust table if needed
        if (userGR.get(personSysId)) {
            country = userGR.getValue('ucountry'); // Adjust field name if needed
        }

        // If country is India, check attachments
        if (country && country.toLowerCase() === 'india') {
            var attachGR = new GlideRecord('sysattachment');
            attachGR.addQuery('tablename', tableName);
            attachGR.addQuery('tablesysid', recordSysId);
            attachGR.query();
            if (!attachGR.hasNext()) {
                return 'false'; // No attachment
            }
        }

        return 'true'; // Either not India OR attachment exists
       },

    type: 'CheckAttachment '
});

 

Could you please help me with the code, 

Thanks!

 

@NowNinja727 Please update the GlideAjax approach as follows.

 

Client Script: 

function onSubmit() {
    if (g_scratchpad && g_scratchpad.isFormValid) return true;

    var subjectPerson = g_form.getValue('hrsubjectperson'); // Replace with your variable name
    if (!subjectPerson) {
        return true; // No subject person → allow submission
    }

    var ga = new GlideAjax('CheckAttachment');
    ga.addParam('sysparm_name', 'validateIndiaAttachment');
    ga.addParam('sysparm_personsysid', subjectPerson);
    ga.addParam('sysparm_table', g_form.getTableName());
    ga.addParam('sysparm_sysid', g_form.getUniqueValue());

    ga.getXMLAnswer(function(response) {
        if (response === 'false') {
            g_form.addErrorMessage('Attachment is mandatory when the Subject Person is from India.');
            return false; // Stop submission
               }

        // ✅ Validation passed → resubmit once
        g_scratchpad = g_scratchpad || {};
        g_scratchpad.isFormValid = true;

        var actionName = g_form.getActionName();
        g_form.submit(actionName); // Re-submit after validation
    });

    return false; // Pause initial submission until callback completes
}

 

Script Include:

 

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

    validateIndiaAttachment: function() {
        var personSysId = this.getParameter('sysparm_personsysid');
        var tableName = this.getParameter('sysparm_table');
        var recordSysId = this.getParameter('sysparm_sysid');

        // Get country of Subject Person
        var country = '';
        var userGR = new GlideRecord('sys_user'); // Adjust table if needed
        if (userGR.get(personSysId)) {
            country = userGR.getValue('country'); // Adjust field name if needed
        }

        // If country is India, check attachments
        if (country && country.name.toLowerCase() === 'india') {
            var attachGR = new GlideRecord('sys_attachment');
            attachGR.addQuery('table_name', tableName);
            attachGR.addQuery('table_sys_id', recordSysId);
            attachGR.query();
            if (!attachGR.hasNext()) {
                return 'false'; // No attachment
            }
        }

        return 'true'; // Either not India OR attachment exists
       },

    type: 'CheckAttachment '
});

 

For more information, please refer to https://www.servicenow.com/community/developer-blog/how-to-async-glideajax-in-an-onsubmit-script/ba-...

 

Hope this helps.

@NowNinja727 
I don't know that you can use a scripted method and have it work on both the Service Portal and in Mobile UI. Even if you can pass the necessary info to the Glide Ajax to lookup the attachments the Mobile UI doesn't execute onChange and onLoad client scripts the same way so you would need separate configurations for both.

 

If you don't want to use a business rule then I think you would need to use an attachment variable on the Record Producer instead of just turning on attachments. Create an Attachment variable on the record producer then use Catalog UI Policy with a UI Policy Action to make it required and visible. This piece would be respected and applied by both the Mobile UI and Service Portal.

 

In order to do this on both the Service Portal and the Mobile UI you would need to surface the country of the selected Subject Person as a variable in the Record Producer. Once the variable exists you could populate it on the Service Portal side with an onChange script and on the Mobile UI side you would need to use a mobile script or might be able to use a data item lookup. You would then be able to reference the value of this variable in your condition on the Catalog UI Policy.

lauri457
Giga Sage

In portal it can be achieved by messing around with the scope of the catalog item widget and $apply will cause a digest cycle so you get the asterisk on the attachment widget. This is not good practice development of course but will likely be quite robust. This should work in the mobile app as last I checked it just used the catalog item page me_sc_cat_item with the same SC Catalog Item widget. I did not test it on mobile though

 

Onchange client script:

function onChange(control, oldValue, newValue, isLoading) {
	if (isLoading || newValue == '') {
		return;
	}
	const catitemScope = this.angular.element("#sc_cat_item").scope();
	if (newValue == "india") {
		catitemScope.data.sc_cat_item.mandatory_attachment = true;
	} else {
		catitemScope.data.sc_cat_item.mandatory_attachment = false;
	}
	catitemScope.$apply()
}

 Instead of storing the country value in a variable you can just do a glideajax to check the country server-side.

@lauri457 

Could you please help what the above script does?

It would be helpful for members.

Regards,
Ankur
Certified Technical Architect  ||  9x ServiceNow MVP  ||  ServiceNow Community Leader