Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The NetSuite Pro

The NetSuite Pro Logo The NetSuite Pro Logo

The NetSuite Pro Navigation

  • Home
  • About Us
  • Tutorials
    • NetSuite Scripting
    • Advanced PDF Templates in NetSuite
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask A Question
  • Home
  • About Us
  • Tutorials
    • NetSuite Scripting
    • Advanced PDF Templates in NetSuite
  • Blog
  • Contact Us
Home/ NetSuite Scripting/Working with Subrecords in SuiteScript 2.1

Working with Subrecords in SuiteScript 2.1

🔹 What’s a subrecord?

A subrecord is a mini-record embedded inside another record. You don’t open it from the global menu; instead you access it through a field on a body or through a sublist line. Common examples:

  • Address subrecord on a Customer (via the addressbook sublist)
  • Inventory Detail subrecord on transaction item lines (fulfillments, receipts, adjustments)
  • (Others exist—payment instruments, tax details, etc.)

Key APIs you’ll use:

  • Body level:
    • hasSubrecord({ fieldId })
    • getSubrecord({ fieldId })
    • createSubrecord({ fieldId })
    • removeSubrecord({ fieldId })
  • Sublist line level:
    • getSublistSubrecord({ sublistId, fieldId, line })
    • getCurrentSublistSubrecord({ sublistId, fieldId })
    • createCurrentSublistSubrecord({ sublistId, fieldId })
    • removeSublistSubrecord({ sublistId, fieldId, line })

Tip: Subrecord field IDs differ by context. For addresses it’s usually addressbookaddress. For inventory it’s inventorydetail.


🔹 Example 1 — Read the first Address subrecord on a Customer

Goal: Load a Customer, read the first address from the addressbook sublist, and log it.

/**
 * @NApiVersion 2.1
 * @NScriptType ScheduledScript
 */
define(['N/record'], (record) => {
  const execute = () => {
    try {
      const customerId = 123; // <-- replace with a real Customer internal ID

      // 1) Load the parent record (Customer)
      const cust = record.load({
        type: record.Type.CUSTOMER,
        id: customerId,
        isDynamic: false // dynamic not required for reading, but fine either way
      });

      // 2) Find how many address lines exist on the addressbook sublist
      const addrCount = cust.getLineCount({ sublistId: 'addressbook' });
      if (addrCount === 0) {
        log.debug('Addresses', 'No addressbook lines found.');
        return;
      }

      // 3) Access the first line’s Address subrecord
      //    - sublistId: 'addressbook' (the Customer address sublist)
      //    - fieldId: 'addressbookaddress' (the actual Address subrecord)
      const addressSubrec = cust.getSublistSubrecord({
        sublistId: 'addressbook',
        fieldId: 'addressbookaddress',
        line: 0
      });

      // 4) Read common address fields (field IDs are part of the Address subrecord)
      const addr1 = addressSubrec.getValue({ fieldId: 'addr1' });   // Street 1
      const city  = addressSubrec.getValue({ fieldId: 'city' });
      const state = addressSubrec.getValue({ fieldId: 'state' });
      const zip   = addressSubrec.getValue({ fieldId: 'zip' });
      const country = addressSubrec.getValue({ fieldId: 'country' }); // 2-letter ISO (e.g., US, CA)

      log.debug('Address[0]',
        `addr1=${addr1}, city=${city}, state=${state}, zip=${zip}, country=${country}`);

    } catch (e) {
      log.error('Error reading address subrecord', e.message);
    }
  };
  return { execute };
});

What beginners should notice:

  • You don’t search for an “Address” record type—you reach into the parent record’s sublist field to get the subrecord.
  • Subrecord fields (addr1, city, etc.) are read via the subrecord object, not the parent.

🔹 Example 2 — Add a new Address to a Customer (create subrecord on a new sublist line)

Goal: Insert a new line in addressbook, get its addressbookaddress subrecord, write fields, and save.

/**
 * @NApiVersion 2.1
 * @NScriptType ScheduledScript
 */
define(['N/record'], (record) => {
  const execute = () => {
    try {
      const customerId = 123; // <-- replace with a real Customer ID

      // 1) Load the Customer in DYNAMIC mode to work with sublist lines easily
      const cust = record.load({
        type: record.Type.CUSTOMER,
        id: customerId,
        isDynamic: true
      });

      // 2) Start a new line in the 'addressbook' sublist
      cust.selectNewLine({ sublistId: 'addressbook' });

      // Optional: mark as default shipping/billing at the addressbook line level
      cust.setCurrentSublistValue({
        sublistId: 'addressbook',
        fieldId: 'defaultshipping',
        value: true
      });
      cust.setCurrentSublistValue({
        sublistId: 'addressbook',
        fieldId: 'defaultbilling',
        value: false
      });

      // 3) Create or get the Address subrecord on the CURRENT line
      // If no subrecord exists yet, create it for this line:
      const addrSubrec = cust.createCurrentSublistSubrecord({
        sublistId: 'addressbook',
        fieldId: 'addressbookaddress'
      });

      // 4) Set address fields on the subrecord (these are subrecord fields)
      addrSubrec.setValue({ fieldId: 'addr1', value: '123 Demo Street' });
      addrSubrec.setValue({ fieldId: 'city',  value: 'Toronto' });
      addrSubrec.setValue({ fieldId: 'state', value: 'ON' });
      addrSubrec.setValue({ fieldId: 'zip',   value: 'M5H 2N2' });
      addrSubrec.setValue({ fieldId: 'country', value: 'CA' }); // Canada

      // 5) IMPORTANT: Commit the subrecord back to the line
      addrSubrec.save(); // persists the subrecord to the current line context

      // 6) Commit the sublist line itself
      cust.commitLine({ sublistId: 'addressbook' });

      // 7) Save parent record
      const id = cust.save();
      log.debug('Customer updated', `New address added. Customer ID: ${id}`);

    } catch (e) {
      log.error('Error creating address subrecord', e.message);
    }
  };
  return { execute };
});

Key takeaways:

  • Use dynamic mode for easier sublist editing (selectNewLine, commitLine).
  • You save the subrecord, then commit the line, then save the parent.

🔹 Example 3 — Inventory Detail subrecord on an Item Fulfillment line (assign Lot/Serial/Bin)

Goal: On an Item Fulfillment, open an item line and write Inventory Detail assignments
(lot/serial numbers, bins, and quantities). The exact required fields depend on your item type and enabled features (Lot vs Serial, Bins enabled or not).

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 */
define(['N/record'], (record) => {
  const afterSubmit = (context) => {
    try {
      if (context.type !== context.UserEventType.CREATE &&
          context.type !== context.UserEventType.EDIT) return;

      // 1) Load the Item Fulfillment in DYNAMIC mode
      const ifId = context.newRecord.id;
      const fulfil = record.load({
        type: record.Type.ITEM_FULFILLMENT,
        id: ifId,
        isDynamic: true
      });

      // 2) Find line to edit on the 'item' sublist (example assumes line 0)
      const lineCount = fulfil.getLineCount({ sublistId: 'item' });
      if (lineCount === 0) {
        log.debug('Item Fulfillment', 'No item lines to assign.');
        return;
      }

      // Select the first line (adjust as needed or loop over all lines)
      fulfil.selectLine({ sublistId: 'item', line: 0 });

      // 3) Create or get the Inventory Detail subrecord for this line
      // If there is no inventory detail yet, create it on the current line:
      let invDetail;
      try {
        invDetail = fulfil.getCurrentSublistSubrecord({
          sublistId: 'item',
          fieldId: 'inventorydetail'
        });
      } catch (_) {
        invDetail = fulfil.createCurrentSublistSubrecord({
          sublistId: 'item',
          fieldId: 'inventorydetail'
        });
      }

      // 4) Inside Inventory Detail subrecord there is a sublist: 'inventoryassignment'
      // Each line represents a lot/serial/bin assignment with a quantity.
      // Clear existing lines if needed (optional)
      const assignCount = invDetail.getLineCount({ sublistId: 'inventoryassignment' });
      for (let i = assignCount - 1; i >= 0; i--) {
        invDetail.removeLine({ sublistId: 'inventoryassignment', line: i });
      }

      // 5) Add a new assignment line
      invDetail.selectNewLine({ sublistId: 'inventoryassignment' });

      // Common fields (depends on setup):
      // - For LOT items:   'issueinventorynumber' (the lot number to issue)
      // - For SERIAL items:'issueinventorynumber' (serial number), quantity usually 1 per line
      // - If BINS enabled: 'binnumber' (internal ID of the bin)

      // Example: assign from Bin internal ID 7, Lot "LOT-ABC-001", with quantity 3
      invDetail.setCurrentSublistValue({
        sublistId: 'inventoryassignment',
        fieldId: 'binnumber',    // only if bins are enabled and bins required
        value: 7                 // <-- replace with actual Bin internal ID
      });
      invDetail.setCurrentSublistValue({
        sublistId: 'inventoryassignment',
        fieldId: 'issueinventorynumber',
        value: 'LOT-ABC-001'     // <-- replace with actual Lot/Serial number
      });
      invDetail.setCurrentSublistValue({
        sublistId: 'inventoryassignment',
        fieldId: 'quantity',
        value: 3                  // For serial items, this is typically 1 per line
      });

      // Commit the inventory assignment line
      invDetail.commitLine({ sublistId: 'inventoryassignment' });

      // 6) Save the Inventory Detail subrecord back to the line
      invDetail.save();

      // 7) Commit the 'item' line and save the parent fulfillment
      fulfil.commitLine({ sublistId: 'item' });
      const savedId = fulfil.save();
      log.debug('Inventory Detail', `Assignments saved on IF ${savedId}`);

    } catch (e) {
      log.error('Error setting Inventory Detail', e.message);
    }
  };
  return { afterSubmit };
});

Important notes:

  • Field IDs (issueinventorynumber, binnumber) may vary by item type and features.
  • Serial-numbered items: you usually need one line per serial with qty 1.
  • Lot-numbered items: one line can carry qty > 1 for the same lot.
  • If you get “invalid subrecord operation,” verify:
    • The item on that line actually requires inventory detail
    • You’re using the correct subrecord/sublist field IDs
    • You’re in dynamic mode when editing sublist lines

🔹 Troubleshooting & Tips

  • “You have attempted an invalid subrecord or sublist operation”
    Ensure you used the correct method:
    • Body subrecord → getSubrecord/createSubrecord
    • Sublist subrecord → get(Sublist|CurrentSublist)Subrecord
  • Always save/commit in the right order:
    • Save subrecord → commit the line → save the parent.
  • Check features: If Inventory Detail isn’t required or bins aren’t enabled, the field may be missing.
  • Use logs: Add log.debug() before/after each critical step.

âś… Key Takeaway

Subrecords are embedded data structures you access through parent record fields or sublist lines. Mastering the subrecord APIs lets you build precise automations like address management and inventory assignments—cleanly and reliably.

Share
  • Facebook

Sidebar

Ask A Question

Stats

  • Questions 6
  • Answers 6
  • Best Answers 0
  • Users 2
  • Popular
  • Answers
  • Rocky

    Issue in running a client script in NetSuite SuiteScript 2.0 ...

    • 1 Answer
  • admin

    How can I send an email with an attachment in ...

    • 1 Answer
  • admin

    How do I avoid SSS_USAGE_LIMIT_EXCEEDED in a Map/Reduce script?

    • 1 Answer
  • admin
    admin added an answer The issue is usually caused by following Wrong script file… September 14, 2025 at 10:33 pm
  • admin
    admin added an answer Steps to send an Invoice PDF by email: define(['N/email', 'N/render',… August 28, 2025 at 3:05 am
  • admin
    admin added an answer This error means your script hit NetSuite’s governance usage limit… August 28, 2025 at 3:02 am

Top Members

Rocky

Rocky

  • 1 Question
  • 21 Points
Begginer
admin

admin

  • 5 Questions
  • 2 Points

Trending Tags

clientscript netsuite scripting suitescript

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

© 2025 The NetSuite Pro. All Rights Reserved