Last updated on February 13, 2023.
Challenge
The mission is simple: Via SuiteScript, add a user note to a custom record.
You look up the SuiteScript record browser [I]NetSuite. SuiteScript Records Browser (version 2022.2) for the Note record. Available at: https://system.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2022_2/script/record/note.html. [Accessed: … Continue reading, Google it, or ask ChatGPT and you come up with the following code. (I actually tried ChatGPT for the fun of it; more on that at the end of the article.)
define(['N/record'], function(record) {
function linkNoteToCustomRecord(customRecordId, noteText) {
record.create({ type: record.Type.NOTE })
.setValue('note', noteText)
.setValue('record', customRecordId)
.setValue('recordtype', 'customrecord_abc_123')
.save();
}
return {
linkNoteToCustomRecord: linkNoteToCustomRecord
};
});
But, surprisingly, you get an error:
You have entered an Invalid Field Value customrecord_abc_123 for the following field: recordtype
It turns out that the recordtype
field expects the Internal ID of the record type, not the script ID! Annoyingly, that is not explicit in the documentation and the error message is not very helpful. Anyway, somehow, you figured it out. Thus, the next challenge is getting the custom record type’s internal ID.
You’re tempted to just go find it from the record customization page (Customization > Lists, Records, & Fields > Record Types
) and hardcode it but the voice of your undergrad software design lecturer rings in your head, saying “Hardcoding is a poor software practice, avoid it whenever you can”. Besides that nagging voice in your head, you also that there’s a practical constraint: Internal IDs are environment-specific and chances are that, if this is a new record type that you’ve created in Sandbox, the internal ID might change when you deploy it to Production which will break your code. Essentially, the same problem we’ve described (and solved) before but in a different context.
As a (NetSuite) developer, you should feel uneasy every time you find yourself hardcoding values especially if they are environment-specific. And, if there’s no way around it, be sure to add a comment explaining why it was necessary.
A Better Way
SuiteQL offers a CustomRecordType
table [II]Tim Deitrich (August 23, 2021). NetSuite: Use SuiteQL to Query for Custom Record Types and Custom Fields. Available at … Continue reading which you can query to get the internal ID of a custom record:
let customRecordType = query.runSuiteQL({
query:
`SELECT internalid
FROM CustomRecordType
WHERE scriptid = 'CUSTOMRECORD_ABC_123'`
// Note: The scriptid must be UPPERCASE
}).asMappedResults()[0].internalid;
You revise your code accordingly and it works!
define(['N/query', 'N/record'], function(query, record) {
function linkNoteToCustomRecord(customRecordId, customRecordScriptId, noteText) {
let customRecordType = query.runSuiteQL({
query:
`SELECT internalid
FROM CustomRecordType
WHERE scriptid = ?`,
params: [customRecordScriptId.toUpperCase()]
}).asMappedResults()[0].internalid;
record.create({ type: record.Type.NOTE })
.setValue('note', noteText)
.setValue('record', customRecordId)
.setValue('recordtype', customRecordType)
.save();
}
return {
linkNoteToCustomRecord: linkNoteToCustomRecord
};
});
You’ve quieted the voice in your head by doing the right thing and your future self has one less thing to fuss about. The learning continues…
Learn SuiteQL
I’ve written a few articles about how SuiteQL opens doors to solving problems that previously were difficult or impossible to solve. The list continues to grow. If you are a NetSuite developer and you’re still avoiding learning SuiteQL, you’re limiting yourself.
Related Posts
Experiments with Chat GPT
After solving the problem, I was curious if Open AI’s ChatGPT could have been useful in getting to this solution and if it could have gotten me there faster. Spoiler alert: Mission failed.
ChatGPT’s Take on the Cause of the Error
In trying to understand the cause of the issue, the bot was quite helpful in mentioning that I need a valid internal ID. As an aside, the sample code it claimed to be SuiteScript 2.0 was actually SuiteScript 1.0, and, as far as I know, it is not possible to set the recordtype
field on any record. Moreover, the path Setup > SuiteCommerce > Advanced > Record Types
is not where you would go to find record types. Nevertheless, I consider this a generally helpful response as it pointed me in the right direction regarding the need for an Internal ID.
ChatGPT’s Solutions to the Error
ChatGPT was unable to provide a reasonable answer to the challenge. I tried formulating the question in different ways, to no avail. I aborted the mission after about half a dozen attempts. Here are a few of the interactions.
As an aside, to set a body field, it is more efficient (in terms of performance and governance usage) to use
record.submitFields
instead of loading and saving the record.
The above answer incorrectly assumes that user notes is a custom field on the custom record. Moreover, it offers an inefficient way to set a field on a record. I figured I needed to formulate the query differently, so I tried the following:
This time, ChatGPT disturbingly offered a non-existent record.addNotes()
method to get the job done. Of course, this is invalid. Equally disturbing is the fact that the catch block of the sample code suppresses any errors that might occur. This is poor practice.
Next, I finally got a formulation of the request that got the bot on the right track. But, the answer was still incomplete:
As I mentioned, the above answer is in the right direction in that it creates a note record and even sets the record
field. However, running this code will produce no errors but not give the desired result as I explain in the next paragraph. Moreover, the answer does not address the question of getting the record type’s internal ID. Also, notice the inefficiency of loading the custom record to get its ID which we already had and used to load it in the first place!
ChatGPT’s answer above led me to something weird (I think it’s a NetSuite glitch): If you try creating a note without setting record
and recordtype
(and without setting any other references like transaction
or entity
), you’ll get the error “You cannot create a standalone note record.
” which is reasonable. However, if you set the record
but omit the recordtype
, you’ll get no error and a standalone note will get created but not attached to the specified record. Clearly, that is not so useful as the note will not be visible on the target record.
define(['N/record'], function(record) {
function linkNoteToCustomRecord(customRecordId, noteText) {
record.create({ type: record.Type.NOTE })
.setValue('note', noteText)
.setValue('record', customRecordId)
//.setValue('recordtype', 'customrecord_abc_123')
// If we set 'record' but not 'recordtype', there wil be no error
// and a (useless) standalone note will be created.
.save();
}
return {
linkNoteToCustomRecord: linkNoteToCustomRecord
};
});
Reflection
While this experiment was not intended to be a comprehensive evaluation of ChatGPT and should not be interpreted as such, a few things come to light:
- Writing natural language queries that ChatGPT correctly interprets to produce the right answer is not always trivial.
- At this stage, ChatGPT is not strong in handling non-trivial SuiteScript queries.
- ChatGPT might produce and/or reinforce anti-patterns based on the sample code it produces e.g. unnecessarily loading and saving records, or suppressing errors in the catch block. These kinds of practices will ultimately produce weaker developers.
- ChatGPT sounds confident and authoritative in its explanation of the answers it provides even when they are really off. This is disturbing in a general sense because when anything or anyone sounds very confident while sharing inaccuracies, it often opens the door for deceit.
On AI and the future
There are lots of speculations about how AI-driven solutions like ChatGPT will change the world. It is really not a question of “if” but of “when” and “how”. Regardless of how things unfold, I hope you keep faith in and continue to cultivate what is arguably your most important asset – your mind. The human ability to reason and make assessments should never be underestimated. In the world of the future, I believe this capacity will become even more important in distinguishing quality professionals from others as bots become the authority and more humans trade in their superior capacities for the convenience and perhaps fantasy of AI. In the meantime, keep on honing your skills and applying sound judgment in deciding when to engage the services of bots. And whether you’re getting answers from Google, a bot, or somewhere else, do not apply a solution if you do not understand it.
As a rule of thumb, do not copy/use any (SuiteScript) code fragment if you do not understand how and why it works.
On a personal note, it’s an exciting time to live in and I am curious to see how things unfold. On Chat GPT specifically, I would really love its answers to include references/sources of the information. I’m curious how it came up with the stuff above. I could not find the code samples on Google and I suspect that it actually somehow pieces information out there together to generate its own code (and even define non-existent NetSuite API functions as it deems fit).
If you are a NetSuite manager wondering whether bots offer an alternative to NetSuite professionals, I hope you see that we’re not anywhere close to that. I argue that even as bots get “smarter”, they will not replace high-caliber NetSuite professionals. If you have a NetSuite challenge and need a competent team to talk to, reach out to us at Prolecto Resources Inc.
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!
Further Reading
Hello Chidi,
Readers may want to review my 2015 article to see how to dynamically get that internalID:
https://blog.prolecto.com/2015/03/31/netsuite-lookup-get-custom-record-script-id-from-internal-id-integer/
It’s SuiteScript 1.0 but presumably is another option.
Marty
Going further, you are trying to get it from another direction. I also wrote this 2018 article that is similar:
https://blog.prolecto.com/2018/11/24/how-to-lookup-netsuite-custom-record-script-ids which seems like it would give you want you need in an internal ID as a reference.
Marty
Thanks for sharing! Your article addresses the reverse use case of getting the script ID given the internal ID which can also be solved via SuiteQL. I didn’t realize that there is a customrecordtype search.
Hey Chidi, thanks for the article! What do you think about wrapping that in a cache loader to improve performance? Also, any thoughts on error management there? The unconditional reference to [0] is a bit concerning. I only mention it, because you mention similar points later on – I definitely understand sample code is sample code :D, but maybe mentioning those 2 things would help others as they use this article to learn SuiteScripting.
Thanks for all the information you make available!
Hi Stephen, you have raised good points for readers to contemplate. I opted for brevity over writing production-level code.
Chidi, great article! I love how you write technical articles in a way that laymen (with some reasonable NetSuite knowledge) can understand. I too tested ChatGPT to try to produce SQL queries, and came up disappointed and relieved – disappointed in the bot’s missteps; relieved to see that my brain is not yet obsolete.
Hi Meir, thanks for your kind words. Yes, our brains are still extremely valuable!