Chidi Okwudire IT Professional. ERP Enthusiast. NetSuite Certified (Administrator, SuiteCloud Developer II, and ERP Consultant). Celigo Certified (Level 4+). Passionate About Empowerment Through Knowledge Sharing. Always Eager to Learn.

Learn to Query and Reuse Script Audience Configuration

4 min read

Context

In a recent project, a client needed a way to transmit invoices in bulk from NetSuite to an external system where they managed invoice distribution to their customers. While we definitely can handle invoice distribution via NetSuite, the client had made a huge investment in the external system and wanted to continue using it. Thus, our scope was to generate the invoice PDFs and transmit them to the external system for distribution.

The following Prolecto accelerator bundles made it very easy to accomplish the task:

  • We used Prolecto CRE (Content Renderer Engine) to generate PDFs that matched the client’s specifications. CRE replaces NetSuite’s Advanced PDF generation solution as it offers a richer feature set and, importantly, overcomes NetSuite’s Advanced PDF data reach/join limitations.
  • We used the Prolecto Bulk Generator bundle to produce a user experience for (bulk) selecting the invoices to print. With the bulk generator, I did not have to write a single line of code! Instead, all I needed was to specify my data source (in this case a single saved search) and provide the configuration (in JSON) that the bundle used to produce the UI illustrated below. Moving away from reinventing the wheel in code each time to configuration of a generic solution is a powerful capacity. It does not just save time and money, it also means that analysts can take on these challenges without needing developers. But I digress…
  • We used the Prolecto sFTP Connector bundle to transmit the invoice PDFs to the client’s sFTP server from where they handled the rest of the process.
  • We used Prolecto RIEM (Record Import Export Manager), which integrates seamlessly with the bulk generator, to handle the task of generating the PDFs (by exercising the corresponding CRE profiles) and triggering the sFTP transmission.
  • We added some plumbing (read custom log record) to help track the status of the multi-step process.
Prolecto Bulk Generator for Bulk Invoice Printing
Prolecto Bulk Generator for Bulk Invoice Selection

Additional Client Request

The client was excited and relieved that we had solved this challenge without breaking the bank. But, like every good client working with a great team, they wanted more: “In addition to bulk printing, we would like to be able to print individual invoices or invoice groups”. Yes, we can!

Adding a button to an invoice or invoice group to trigger off the process is pretty straightforward. There are two main options:

  1. Add the button in a user event script that references a client script that implements the logic.
  2. Use a workflow to add the button and implement a workflow action script to handle the logic.

I opted for (1) as I wanted to be able to do end-user validation on client side. For example, if the user clicked on the button while a prior request was ongoing or had been successfully completed, I showed a popup to warn them about this and allow them to cancel the new request. The workflow approach is not well-suited for this kind of user interaction.

So far so good. Onto the last piece and the essence of this article. The bulk printing process was restricted to certain roles. We had easily configured this by specifying the audience accordingly on the Suitelet script of the bulk generator:

Script deployment showing audience configuration

You will notice that I included specific employees in the audience. While that is generally not a best practice (control permissions by roles instead whenever possible), I needed that option because, for go-live testing, certain employees needed to perform end-to-end tests in live systems. To prevent the risk of other users accidentally exercising the logic and distributing test invoices to customers, we temporarily turned off role-based access in favor of individual employee access. Thus, my button visibility logic needed to accommodate this.

Restricting Button Visibility Based on Bulk Generator Script Audience Configuration

I wanted to leverage the same permission restrictions to control the visibility of my button and wondered if that was possible. Indeed, it is possible using N/query as I’ll explain shortly.

NetSuite record catalog showing permission-related fields on the script deployment record type.

While N/search allows searching script deployments, it does not expose fields related to the audience. Thus, it was unsuitable for the job.

Here’s a sample query to get the desired information:

SELECT 
    D.audemployee,
    D.audslctrole,
    D.allroles,
    D.allemployees
FROM 
    scriptdeployment D
WHERE 
    D.scriptid = 'customdeploy_my_bulk_generator'

I only included the audience fields of interest for my use case but there are a few more as highlighted in the screenshot above e.g. audgroup, audpartner, allpartners, and audsubsidiary.

Here’s sample output of the query:

Sample script deployment query output

With this query, when loading an invoice or invoice group, I could determine if the current user had access to the bulk printing script. If not, I would not show them the button for printing individual invoices.

Here’s a code snippet for inspiration:

function hasAccessOnScriptDeployment(deploymentId) {
	var userObj = runtime.getCurrentUser();

	if (!deploymentId) {
		return false;
	}

	var q = 
		'SELECT ' +
			'D.audemployee, ' +
			'D.audslctrole, ' +
			'D.allroles, ' +
			'D.allemployees ' +
		'FROM ' +
			'scriptdeployment D ' +
		'WHERE ' +
			'D.scriptid = ?';

	var queryResults = query.runSuiteQL({
		query: q,
		params: [deploymentId]
	}).asMappedResults();
	

	var authorized = false;
	if (queryResults.length > 0) {
		authorized = (queryResults[0].allroles === 'T' || queryResults[0].allemployees === 'T');
		if (!authorized) { // Not authorized globally -> check by role.
			var roles = (queryResults[0].audslctrole || '').split(',').map(function(s) { return String(s).trim() });
			authorized = (roles).includes(String(userObj.role));
		}

		if (!authorized) { // Not authorized globally or by role -> check by employee.
			var employees = (queryResults[0].audemployee || '').split(',').map(function(s) { return String(s).trim() });
			authorized = (employees).includes(String(userObj.id));
		}
	}

	return authorized;
}

Notice that in addition to looking at the list of roles, the global “All Roles” flag should be evaluated as well; when that flag is set, field audslctrole will be blank. The same applies to “All Employees” versus individual employees, etc.


Design Principles

The solution described above reflects two related software design principles that are worth highlighting, namely localization of change and reuse.

A less-optimal solution to the problem would be to define some configuration for (or even worse hardcode) the criteria for controlling the visibility of the button, separate from the bulk generator configuration. Functionally, the outcome will be identical and the client will not notice any difference… but your NetSuite admin would not love you for it. In the future, when changes need to be made to who has access to the invoice printing functionality, it is very easy for the bulk generator audience and the button visibility to get out of sync leading to subtle backdoors. By localizing the audience configuration to the bulk generator and reusing it in other places, we avoid this situation.

Of course, if the requirement was different i.e. allowing certain users/roles to print individual invoices but not bulk print, the solution would be different. The point I’m trying to make is that when designing solutions, we should always be thinking about localization and reuse in the back of our minds. These are examples of non-functional requirements that make the difference between an “okay” solution and a solid one.

Conclusion

I am privileged to work with a team that brings so much to the table and makes NetSuite solution design and implementation a pleasure. I am also very pleased whenever I discover use cases like this where N/query alleviates the limitations of N/search and allows us to produce better solutions. If you’re not yet committed to learning the query capabilities offered by NetSuite, I encourage you to do so.

NetSuite Insights is proud to partner with Prolecto Resources Inc. – the unrivaled #1 NetSuite Systems Integrator and thought leader in the space! Learn more about how Prolecto can supercharge your NetSuite experience and deliver the best return on your NetSuite investment.

Related Posts

Chidi Okwudire IT Professional. ERP Enthusiast. NetSuite Certified (Administrator, SuiteCloud Developer II, and ERP Consultant). Celigo Certified (Level 4+). Passionate About Empowerment Through Knowledge Sharing. Always Eager to Learn.
×