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.

The SuiteScript Developer’s Guide on NetSuite Subrecords: Part 1

5 min read

Scripting NetSuite subrecords can be painful and frustrating, especially for new SuiteScript developers. This series will help you alleviate that pain and deepen your understanding of how to work effectively with subrecords (particularly, the address subrecord) in NetSuite.

TL;DR

Subrecords in NetSuite are similar to normal records in many ways, the main difference being that subrecords do not exist on their own. Moreover, scripting subrecords requires using a different set of API methods than those used for normal records. Eric T. Grubaugh’s 37-minute video tutorial “Working with NetSuite Subrecords in SuiteScript 2.0[I]Stoic Software (December 2, 2018). Working with NetSuite Subrecords in SuiteScript 2.0 [Video]. Available at https://www.youtube.com/watch?v=gPFTLeufBmI [Accessed: October 28, 2020] provides an excellent introduction to subrecords. This article reiterates and extends the key concepts from that tutorial to lay the foundation for the rest of the series. I highly recommend watching Eric’s video in addition to reading this article.

5 Things About NetSuite Subrecords You Need to Know

NetSuite’s data model centers around the concept of a record. NetSuite’s standard record types can be grouped into the following categories:

  • Entity e.g. Leads, Prospects, Customers, Contacts, Competitors, Vendors, Partners, or Employees.
  • Transaction e.g. Requisitions, Purchase Orders, Vendor Bills, Sales Orders, Invoices, or Journal Entries.
  • CRM e.g. Activities, Tasks, or Events.
  • Item e.g. Non-Inventory, Inventory, Service, or Discount items.

In addition to standard records, NetSuite supports creating custom records as business needs demand.

Records consist of fields that may be directly part of the record at “body” level or may be presented in sublists. Think of a sublist as a tabular structure organized in rows and columns, allowing us to associate a record with multiple pieces of information from related records. For example, a transaction has an item sublist which may contain multiple item rows, with each row displaying multiple fields from the linked item record.

Records are interconnected with one another in different ways e.g. as parent-child relationships, via database table “joins” or as subrecords. In the rest of this article, we’ll focus on subrecords. Specifically, we highlight key points to bear in mind when dealing with subrecords.

#1: Subrecords do not exist on their own

A subrecord always exists within the context of another record. For instance, the address subrecord cannot exist without an entity or transaction that encapsulates it. This explains why you cannot find a list of standalone addresses in NetSuite. You can only access addresses via the record (e.g. vendor) to which they belong. This is a reasonable representation of reality where addresses also do not exist in isolation and always belong to something else.

#2: Subrecords can be easily identified in the Record Browser

How can you tell if a particular entity is a record or subrecord? Answer: Use the NetSuite Records Browser. [II]NetSuite (June 15, 2018). The SuiteScript Records Browser. Available at https://netsuite.custhelp.com/app/answers/detail/a_id/74610 [Accessed: October 28, 2020] Subrecords have the type “summary” which links to a page describing the subrecord’s fields. In the following example, I show you how to confirm that the addresses on a vendor record are indeed subrecords.

How to Identify a Subrecord Using the Record Browser

#3: Subrecords may appear at body level or in sublists on the parent record

A subrecord may appear as a body record field and/or on a sublist, depending on how the parent record is structured. For example, the address subrecord typically appears as a sublist on entity record types but as a body field on transaction record types.

Consider a vendor (entity record type). The vendor may have one or more addresses. These are presented in the addressbook sublist as illustrated below:

Address Subrecord in a Sublist
Entity Address Subrecords are Presented in a Sublist

Suppose we create a Purchase Order (transaction record type) for the vendor. We are able to select the billing address from a dropdown address body field:

Address Subrecord as a Body Field
A PO’s Billing Address Subrecord is Captured as a Dropdown Body Field

#4: How to script a subrecord depends on whether it occurs in a body field or in a sublist

Here’s where things get more fun and potentially confusing:

Whether a subrecord exists as a body field or in a sublist determines which SuiteScript API methods you'll need to create or manipulate the subrecord! Click To Tweet

As such, it is extremely important not just to know if an entity is a subrecord but also to know where it exists on the parent record.

Gladly, NetSuite provides a pretty comprehensive guide on which APIs to use. I strongly recommend you review the section “SuiteScript 2.0 Scripting Subrecords” in the NetSuite Help Center (or equivalently Answer Id: 61059 [III]NetSuite (October 14, 2016). SuiteScript 2.0 Scripting Subrecords. Available at https://netsuite.custhelp.com/app/answers/detail/a_id/61059 [Accessed: October 28, 2020] and the linked articles in SuiteAnswers). You’ll find decent explanations and sample code to help you understand the concepts.

#5: The APIs for scripting subrecords depend on whether the parent record is loaded in standard or dynamic mode

An explanation of standard vs. dynamic mode goes beyond the scope of this article. Refer to SuiteAnswers (Answer Id: 73792) for a detailed explanation. In short, dynamic mode emulates the behavior of a record in the UI and imposes constraints on the order in which you set fields when scripting. On the other hand, as the name suggests, standard mode is the default mode and simplifies things a bit (at least in principle): “After a record [in standard mode] is submitted, NetSuite processes the record’s body fields and sublist line items in the correct order, regardless of the organization of your script.”[IV]NetSuite (April 27, 2018). SuiteScript 2.0 – Standard and Dynamic Modes. Available at https://netsuite.custhelp.com/app/answers/detail/a_id/73792 [Accessed: October 28, 2020]

Pro Tip: Be sure you thoroughly understand NetSuite’s standard and dynamic modes. You will save yourself a lot of frustration as a SuiteScript developer by making this investment now.

Section “SuiteScript 2.0 Scripting Subrecords” in the NetSuite Help Center contains explanations and sample code for manipulating subrecords in static and dynamic mode. The following code snippet, taken from that section (Answer Id: 61070), illustrates how to create an address subrecord in an entity’s address sublist.

/**
 * @NApiVersion 2.x
 * @NScriptType UserEventScript
 * Sample code to illustrate creating an address subrecord in dynamic mode.
 */
define([ 'N/record' ],
    function(record) {
        function afterSubmit(context) {

            // Create the entity record.
            var rec = record.create({
                type: record.Type.EMPLOYEE,
                isDynamic: true
            });

            // Set the required body fields.
            rec.setValue({
                fieldId: 'firstname',
                value: 'John'
            });

            rec.setValue({
                fieldId: 'lastname',
                value: 'Doe'
            });

            rec.setValue({
                fieldId: 'subsidiary',
                value: '1'
            });

            // Create a line in the Address sublist.
            rec.selectNewLine({
                sublistId: 'addressbook'
            });

            // Set an optional field on the sublist line.
            rec.setCurrentSublistValue({
                sublistId: 'addressbook',
                fieldId: 'label',
                value: 'Primary Address'
            });

            // Create an address subrecord for the line.
            var subrec = rec.getCurrentSublistSubrecord({
                sublistId: 'addressbook',
                fieldId: 'addressbookaddress'
            });

            // Set body fields on the subrecord. Because the script uses
            // dynamic mode, you should set the country value first. The country
            // value determines which address form is to be used, so by setting
            // this value first, you ensure that the values for the rest
            // of the form's fields will be set properly.

            subrec.setValue({
                fieldId: 'country',
                value: 'US'
            });

            subrec.setValue({
                fieldId: 'city',
                value: 'San Mateo'
            });

            subrec.setValue({
                fieldId: 'state',
                value: 'CA'
            });

            subrec.setValue({
                fieldId: 'zip',
                value: '94403'
            });

            subrec.setValue({
                fieldId: 'addr1',
                value: '2955 Campus Drive'
            });

            subrec.setValue({
                fieldId: 'addr2',
                value: 'Suite 100'
            });

            // Save the sublist line.
            rec.commitLine({
                sublistId: 'addressbook'
            });

            // Save the record.
            try {
                var recId = rec.save();

                log.debug({
                    title: 'Record created successfully',
                    details: 'Id: ' + recId
                });

                } catch (e) {
                    log.error({
                        title: e.name,
                        details: e.message
                    });
                }
             }

        return {
            afterSubmit: afterSubmit
        };
    });

The same action, in standard mode, will look slightly different (key differences highlighted below):

/**
 * @NApiVersion 2.x
 * @NScriptType UserEventScript
 * Sample code to illustrate creating an address subrecord in standard mode.
 */
define([ 'N/record' ],
    function(record) {
        function afterSubmit(context) {

            // Create the entity record in standard mode this time.
            var rec = record.create({
                type: record.Type.EMPLOYEE,
                isDynamic: false
            });

            // Set entity body fields as before.
            /* truncated for clarity */

            // Create a line in the Address sublist
            // using insertLine i.s.o. selectNewLine.
            rec.insertLine({
                sublistId: 'addressbook',
                line: 0,
            });

            // Set an optional field on the sublist line
            // using setSublistValue i.s.o. setCurrentSublistValue
            rec.setSublistValue({
                sublistId: 'addressbook',
                fieldId: 'label',
                line: 0,
                value: 'Primary Address'
            });

            // Create an address subrecord for the line
            // using getSublistSubrecord i.s.o. getCurrentSublistSubrecord.
            var subrec = rec.getSublistSubrecord({
                sublistId: 'addressbook',
                fieldId: 'addressbookaddress'
                line: 0
            });

            // Set body fields on the subrecord.
            // Unlike in dynamic mode, in static mode, you do not necessarily need to
            // set the country first but it is a good practice to stick to this order.
            subrec.setValue({
                fieldId: 'country',
                value: 'US'
            });

            // Set other address fields
            /* Truncated for clarity */

            // Note: Unlike in dynamic mode, there no need to commit the line
            // Saving the entity record will save the subrecord as well.
            try {
                var recId = rec.save();

                log.debug({
                    title: 'Record created successfully',
                    details: 'Id: ' + recId
                });

                } catch (e) {
                    log.error({
                        title: e.name,
                        details: e.message
                    });
                }
             }

        return {
            afterSubmit: afterSubmit
        };
    });

Recap

In this article, we outlined the basics of scripting subrecords in NetSuite. If you are a seasoned SuiteScript developer, this was probably a refresher. For newbies, this is likely an eye-opener. I wish I knew all this when I first started scripting some years back. Fortunately, you’re off to a better start.


We’re not done yet! Watch out for the next part in which we’ll zoom into the nuances of the address subrecord. For instance, did you know that the address subrecord appears to have two (different) internal IDs? And using the wrong one can lead to some subtle bugs in your scripts! Subscribe now so you do not miss the rest of this series or other valuable NetSuite Insights and freebies.


Do you know something interesting about the address subrecord or have a question you want us to cover? Drop a comment below or email us at stories[at]netsuite-insights.com

Further Reading[+]

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.

2 Replies to “The SuiteScript Developer’s Guide on NetSuite Subrecords: Part 1”

    1. Hi, unfortunately, I do not know as I’ve not played with the REST API for this.

Leave a Reply

Your email address will not be published. Required fields are marked *

×