Users have limited permissions, and for good reason. Sometimes we want to allow them to perform specific actions that are forbidden by their permissions though. Unfortunately, NetSuite doesn't have very granular controls for how users can interact with records.
Let's say that a we have a "Trainer" role that can view, but not edit Employee records. Despite this, they need to be able to update Employee records to show that they trained a given employee. This involves setting a custom "Trained" checkbox, and a "Trained By" field that holds the ID of the trainer.
The trainer would like to navigate to the Employee record, somehow set the fields, and move on. We can let them do this with a custom button.
Button, Button, Who's Got The Button
Since our user doesn't have edit permissions on the record, this limits our methods of adding buttons. Unless we want to use a WorkFlow, a User Event is our only option. Here is the bare minimum to add our "Trained" button. We can deploy it for our Trainer role so that only they can see the button.
/** * @NApiVersion 2.1 * @NScriptType UserEventScript */ define(['N/runtime'], (runtime) => { function beforeLoad(context) { context.form.addButton({ label: 'Trained', id: 'custpage_trained', functionName: 'setTrainedFields' });
}
return { beforeLoad };
});
Now that we have the button, we need to make it do something. It's looking for a setTrainedFields() function that doesn't exist. We can add this function to a typical Client Script, but these only run on edit. One solution is to specify the CS from within the UE. This is done with the clientScriptModulePath property of the form object, and will make it load on view.
context.form.clientScriptModulePath = './setTrained_CS.js';Building Your Clientele
In our client script we need to somehow set two custom fields that we still don't have the required permissions to edit. Client scripts can't be set to run as a different role either. This means that we need to call yet another script, one that can have elevated permissions. The NetSuite help gives us a list of script types that can run as different roles here. Relevant options are: Suitelet, User Event, and Mass Update.
Scheduled scripts and Map/Reduces also run with elevated privileges, as they're hard coded to use the Administrator role. Unfortunately, these require the task module to call them, and it isn't available to client scripts.
Mass Update scripts can't be called from other scripts at all, so this leaves Suitelets and User Events. We would typically use the redirect module to get to a Suitelet, but this isn't available to CSs either. We can manually redirect the browser though.
A User Event would be convenient, since we're already using one to create the button. Normally we would use the beforeSubmit to change the field values, but no edit permission means beforeSubmit doesn't fire. We can continue to use the beforeLoad that we already created though.
We can have our CS redirect to the same page we're on, but with the addition of a parameter that the UE can read. Here's an example.
/** * @NApiVersion 2.1 * @NScriptType ClientScript */ define([], () => { function pageInit(context) { } //must have at least one entry point function setTrainedFields() { //reload record with param set to trigger UE window.location.replace(window.location + '&setTrained=T'); } return { pageInit, setTrainedFields }; });
Friends Are Always Tellin’ Me You’re a User
We just need to give our UE a role that can set the needed fields, and have it run when it sees the param from the CS. There is a wrinkle though; due to having to run in view mode beforeLoad, any values we change won't be shown in the UI. We have to redirect to the same record yet again in order to display the new values. Luckily, the redirect module is available now that we're in a UE.
We can pass another param to indicate that the button action has completed.
let { form, newRecord, request } = context; if (request.parameters['setTrained'] == 'T') { //set fields and reload const values = { custbody_trained: true, custbody_trainedby: runtime.getCurrentUser().id }; record.submitFields({ type: 'employee', id: newRecord.id, values }); redirect.toRecord({ type: 'employee', id: newRecord.id, parameters: { trainedSuccess: 'T' } }); }
We use the second param to trigger a confirmation message to inform the user that the action was performed successfully.
if (request.parameters['trainedSuccess'] == 'T') //show green success message form.addPageInitMessage({ type: 0.0, message: 'Approval Successful', duration: 7000 });
Here's one way to combine our UE logic:
/** * @NApiVersion 2.1 * @NScriptType UserEventScript */ define(['N/record', 'N/runtime', 'N/redirect'], (record, runtime, redirect) => { function beforeLoad(context) { let { form, newRecord, request } = context; if (request.parameters['setTrained'] == 'T') { //set fields and reload const values = { custbody_trained: true, custbody_trainedby: runtime.getCurrentUser().id }; record.submitFields({ type: 'employee', id: newRecord.id, values }); redirect.toRecord({ type: 'employee', id: newRecord.id, parameters: { trainedSuccess: 'T' } }); } else if (request.parameters['trainedSuccess'] == 'T') { //show green success message form.addPageInitMessage({ type: 0.0, message: 'Approval Successful', duration: 7000 }); } else { //display button form.addButton({ label: 'Trained', id: 'custpage_trained', functionName: 'approveVendBill' }); form.clientScriptModulePath = './setTrained_CS.js'; } } return { beforeLoad }; });
The Time Has Come to… Push the Button
Upon pushing the button, the page should reload with the 'setTrained' param. The UE will detect it, update the values, and reload again with the 'trainedSuccess' param. In this way, a user can edit a record without having edit permissions.
No comments:
Post a Comment