Table of Contents
This article explains a quick “hack” to implement custom state labels on NetSuite native and custom records as illustrated below.
Background
Some NetSuite transactions types have a native state machine. For convenience, NetSuite display the state prominently at the top of the page. This is very handy.
However, there are situations where NetSuite’s native state machine does not suffice to capture the business process being implemented. For example, in a recent integration with Amazon Seller Central, I modelled sending inventory to Amazon using a transfer order. After analyzing the Seller Central APIs and mapping out the calls to be made including error handling, I ended up with more than 20 distinct states for the item fulfillment record (yes, the process is quite complex!).
As you may know, with the requisite features enabled, NetSuite natively supports only 3 item fulfillment states, namely: Picked, Packed, or Shipped. Thus, we needed a custom state machine. As per common NetSuite design patterns, I implemented a custom record/field to model and expose the state machine. However, for a better user experience, I though it would be great to see the Amazon transfer state next to the native state at the top of the item fulfillment page where users are accustomed to seeing record states. It turned out to be quite easy!
Solution
Below is a code snippet to accomplish the desired result, It injects the status in the beforeLoad
event of a user event script deployed to the target record type. Note that because NetSuite does not offer any API for this kind of operation, I resorted to using jQuery to inject the HTML element (more on that shortly).
/**
* @NApiVersion 2.1
* @NScriptType UserEventScript
*/
define([],
() => {
/**
* An enumeration of state colors per NetSuite house style
*/
const NsLabelColor = {
BLUE: '#d5e0ec',
YELLOW: '#fcf9cf',
GREEN: '#d7fccf',
RED: '#fccfcf'
}
const beforeLoad = (scriptContext) => {
try {
let stateTxt = 'TODO: Implement';
let errState = false;
// TODO: Implement logic to determine state text/color
let bgColor = NsLabelColor.YELLOW;
if (errState) {
bgColor = NsLabelColor.RED;
}
scriptContext.form.addField({
id: 'custpage_nsi_status',
label: 'Custom State',
type: 'inlinehtml'
}).defaultValue = `<script>jQuery(function($){
require([], function() {
/* $(".uir-page-title-secondline").find("div.uir-record-status").remove(); // Uncomment to remove native state */
$(".uir-page-title-secondline").append('<div class="uir-record-status" id="pri-amz-status" style="background-color: ${bgColor}">${stateTxt}</div>');
});
})</script>`;
} catch (e) {
// Deliberately suppress any errors as this non-critical function should not block the record from loading
log.error('Error', `Suppressing error encountered while attempting to set the custom state: ${e}`)
}
}
return {beforeLoad}
});
Depending on your use case, it might make sense to hide the native status completely by uncommenting line 34 above.
You can also implement color coding to distinguish different states. For your convenience, I’ve provided the hex color codes that match NetSuite’s house style. The blue matches the native state background color; the other colors match the Native page notification message types – green (confirmation), yellow (warning), and red (error).
Observations
1. DOM Manipulation is Not Officially Supported
NetSuite discourages DOM operations like the one above (in simpler terms, that means you’re on your own if you decide to play with the HTML of NetSuite pages outside of the provided APIs). Thus, this solution is effectively a hack that may break if NetSuite changes the internal HTML logic.
Nevertheless, this use case falls within my scope of reasonable hacks because:
- The functionality is not business critical; and
- Thanks to the
try/catch
wrapper around the code, the system will gracefully fall back to native behavior if injecting the status fails.
The interested reader is welcome to review my more elaborate assessment on when it is reasonably safe to perform DOM manipulations.
2. This Approach is Not Limited to Transaction Record Types
I deployed the same solution to a custom record and it worked without issues as demonstrated below.
3. NetSuite’s “New” UI Requires Different Handling
Records that use the “new” NetSuite UI (e.g. Invoice Groups) have different HTML class names. Thus, the above code will need to be adapted accordingly.
Based on quick HTML inspection, replacing uir-page-title-secondline
with n-f-title-second-row
and uir-record-class
with n-f-title-recordstatus
in the provided code snippet should do the trick. Which leads me to my next point for those wondering how to discover these HTML elements.
In principle, you should be able to can combine the logic for the current and new NetSuite UI in a single implementation as jQuery should fail gracefully if the target class is not found.
4. Here’s How to Easily Discover HTML Elements
The following snippet shows how to easily inspect the HTML of a page and find the target elements using Google Chrome. Pressing F12
will reveal the developer tools area from which you can select the target element, inspect and even modify the HTML to validate your ideas.
Conclusion
This article illustrates a quality-of-life enhancement that produces a better user experience. I hope you found it insightful. Often, these little features add up and make an already good solution, better.
Although I illustrated the approach in the context of a NetSuite integration with Amazon Seller Central, there are more common scenarios where I assess that this functionality would be very welcome by users. Take transaction approval workflows for instance. Custom approval workflow typically introduce additional states which may confuse users. It is common to have an “Open” state to indicate that a transaction (e.g. invoice, vendor bill or journal entry) has not yet been submitted for approval. In such a case, it would be helpful to replace the default native “Pending Approval” state label at the top with the custom “Open” state so that users can easily distinguish records have been submitted for approval (Pending Approval) from those that are not yet in the queue (Open). I am sure there are a bunch of other use cases you can come up with.
Finally, if you are looking to integrate NetSuite with Amazon Seller Central for inbound fulfillment or other operations supported by the APIs, you’re in great company. Reach out and let’s discuss your use case.
NetSuite Insights is on a mission to raise the standards around NetSuite practices, one insight at a time. If that resonates with you, learn how you can become an author/collaborator here.
Don’t miss a beat – subscribe to our no-nonsense email list to have these game-changing insights hit your inbox as soon as they’re published. Ignorance? Nah, you’ve got a smarter option. Choose wisdom, choose insights!
Had the same issue, what fix it was that instead of appending the data on multiple lin breaks in the editor, put it all in one line, so instead of
.append(‘${stateTxt}
‘);
put it like
.append(‘${stateTxt}’);
Thanks for letting us know. We’ve removed the line breaks.
[continued], find code is cut, so here use < and > to to replace ”, as that are not allowed to input
scriptContext.form.addField({
id: ‘custpage_nsi_status’,
label: ‘Custom State’,
type: ‘inlinehtml’
}).defaultValue = = `< script src=”https://code.jquery.com/jquery-3.7.0.min.js” >…</script>
Hi there,
NS already loads jQuery so you don’t need to add it yourself. Beyond that, I don’t see anything strange. Confirm that the script is deployed, etc. Please open the developer console and refresh the employee page to see if any errors will be logged in the console.
Thank you to check it. I collate the JQuery section code into one line and delete `//` , then it works on my end 🙂
Thank you for this idea. I tried to add it in employee record type, but I can’t find any change on the page. I add this code in UE script’s beforeLoad function like following, any idea?
const beforeLoad = (scriptContext) => {
try {
log.error(‘Error’, `test`)
let stateTxt = ‘TODO: Implement’;
let errState = false;
// TODO: Implement logic to determine state text/color
let bgColor = NsLabelColor.YELLOW;
if (errState) {
bgColor = NsLabelColor.RED;
}
scriptContext.form.addField({
id: ‘custpage_nsi_status’,
label: ‘Custom State’,
type: ‘inlinehtml’
}).defaultValue = `jQuery(function($){
require([], function() {
$(“div .uir-page-title-secondline”).find(“div.uir-record-status”).remove(); // Uncomment to remove native state
$(“div .uir-page-title-secondline”)
.append(‘${stateTxt}
‘);
});
})`;
} catch (e) {
// Deliberately suppress any errors as this non-critical function should not block the record from loading
log.error(‘Error’, `Suppressing error encountered while attempting to set the custom state: ${e}`)
}
}
Great article. As the writer said, be careful with DOM manipulation. You could also add an email notification if the “catch” is triggered so that you can quickly fix any errors (i.e. if the DOM has changed).
Great idea to send an email notification if the DOM changes.