Module 3: Using SuiteScript Objects

Week 2 • NetSuite SuiteScript 2.0 Training • ~60 minutes

🎯 Learning Objectives

By the end of this module, you will be able to:

1. The Context Object

What Is It?

Every entry point function in SuiteScript 2.x receives a context object. Think of it as a "package of information" that NetSuite hands to your function, containing everything you need to know about the current operation.

💡 Key Insight

The context object is called "context" because its contents change depending on the context of execution. A beforeLoad context contains different properties than an afterSubmit context.

Basic Structure

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 */
define(['N/log'], function(log) {
    
    function afterSubmit(context) {
        // context is passed automatically by NetSuite
        // It contains info about this specific execution
        
        log.debug('Context Type', context.type);  // 'create', 'edit', etc.
    }
    
    return { afterSubmit: afterSubmit };
});

Common Context Properties

PropertyDescriptionAvailable In
context.newRecordThe current record being processedAll User Event entry points
context.oldRecordThe record before changes (read-only)beforeSubmit, afterSubmit (edit only)
context.typeThe trigger type (create, edit, delete, view, etc.)All User Event entry points
context.formThe form object for UI manipulationbeforeLoad only
⚠️ SuiteScript 1.0 vs 2.x

In SuiteScript 1.0, entry points had multiple separate parameters (e.g., afterSubmit(type, form, request)). In 2.x, everything is bundled into the single context object. This is cleaner and more maintainable.

2. Getting the Record Object

newRecord vs oldRecord

The context object gives you access to the record through two properties:

function afterSubmit(context) {
    // Get the current state of the record
    var currentRecord = context.newRecord;
    
    // Get the previous state (only on edit/delete)
    var previousRecord = context.oldRecord;
    
    // Example: Compare old vs new values
    if (context.type === 'edit') {
        var oldStatus = previousRecord.getValue('status');
        var newStatus = currentRecord.getValue('status');
        
        if (oldStatus !== newStatus) {
            log.audit('Status Changed', 'From ' + oldStatus + ' to ' + newStatus);
        }
    }
}
✅ When to Use Each
  • newRecord: 95% of the time — this is the current version of the record
  • oldRecord: When you need to compare "before and after" on edits

Standard Fields vs Custom Fields

Standard fields are created by NetSuite and exist in all accounts (e.g., companyname, email, entity). Custom fields are created by administrators for specific business needs.

Field TypeID PatternExample
Body field (transaction)custbody_custbody_approval_notes
Column field (sublist)custcol_custcol_item_discount
Record fieldcustrecord_custrecord_project_phase
Entity fieldcustentity_custentity_loyalty_tier

3. getValue() vs getText()

This is one of the most important concepts in SuiteScript. Choosing the wrong method is a common source of bugs.

The Rule

🎯 Simple Decision Rule
  • getValue(): Returns the internal ID or raw value
  • getText(): Returns the display text (only works on dropdown/list fields)

For Regular Fields (No Dropdown)

// Text, number, date, email, etc. — always use getValue()
var customerName = record.getValue('companyname');  // "Acme Corp"
var email = record.getValue('email');               // "john@acme.com"
var phone = record.getValue('phone');               // "555-1234"
var amount = record.getValue('amount');             // 1500.00

For Dropdown/List Fields

// List fields (dropdowns) — you have a choice
var salesRepId = record.getValue('salesrep');    // Returns: 42 (internal ID)
var salesRepName = record.getText('salesrep');   // Returns: "John Smith" (display text)

var statusId = record.getValue('status');        // Returns: "A" (internal value)
var statusText = record.getText('status');       // Returns: "Approved" (display text)

✅ When to Use getValue()

  • Saving the ID to another field
  • Loading a related record
  • Comparing against known IDs
  • All non-dropdown fields

✅ When to Use getText()

  • Displaying to users
  • Including in emails/PDFs
  • Logging human-readable info
  • Only works on dropdowns!
🚫 Common Mistake

Using getText() on a regular text field returns null. Only use getText() on fields with dropdowns!

// WRONG - email is not a dropdown
var email = record.getText('email');  // Returns: null

// CORRECT
var email = record.getValue('email'); // Returns: "john@acme.com"

4. setValue() vs setText()

Setting values follows similar rules, but with an important difference in syntax.

// Setting a regular field value
record.setValue({
    fieldId: 'custbody_notes',
    value: 'Approved by manager'
});

// Setting a dropdown by ID (most common)
record.setValue({
    fieldId: 'salesrep',
    value: 42  // Internal ID of the sales rep
});

// Setting a dropdown by text (when you don't know the ID)
record.setText({
    fieldId: 'salesrep',
    text: 'John Smith'  // Display name - NetSuite looks up the ID
});
⚠️ Performance Note

setText() is slower than setValue() because NetSuite has to look up the ID from the text. When possible, use setValue() with the internal ID.

5. Log Levels Explained

NetSuite provides four log levels, each with a specific purpose:

LevelMethodPurposeWhen to Use
1. Debuglog.debug()Development infoDuring development, troubleshooting
2. Auditlog.audit()Track changesRecording business events, who did what
3. Errorlog.error()Handled errorsWhen something goes wrong but is handled
4. Emergencylog.emergency()Critical failuresScript-breaking issues (rarely used)
log.debug('Title', 'Detail message');     // Development
log.audit('Status Changed', 'A to B');    // Business tracking
log.error('API Failed', error.message);   // Error handling
log.emergency('Critical', 'Data loss!');  // Rarely needed

How Log Levels Work on Deployments

Each script deployment has a "Log Level" setting that acts as a filter:

💡 Log Level Hierarchy

If you set the deployment to "Error", only Error and Emergency logs will appear. Debug and Audit logs are silently ignored. This is useful for production to reduce log volume.

  • Debug: Shows Debug + Audit + Error + Emergency
  • Audit: Shows Audit + Error + Emergency
  • Error: Shows Error + Emergency
  • Emergency: Shows Emergency only
🚫 Log Limit Warning

If your script generates more than 100,000 logs per hour, NetSuite automatically increases your log level to reduce volume. Design your logging carefully!

6. Why Never Use DOM

If you have JavaScript experience, you might be tempted to access form elements directly using document.getElementById() or similar DOM methods. Don't do this.

🚫 Never Do This

// DOM manipulation - WRONG!
var field = document.getElementById('salesrep');
field.value = 42;

✅ Always Do This

// SuiteScript API - CORRECT
record.setValue({
    fieldId: 'salesrep',
    value: 42
});

Why DOM Access is Dangerous

  1. Not Future-Proof: NetSuite can change the underlying HTML element names in any release. Your script will break.
  2. Bypasses Validation: SuiteScript's setValue() triggers sourcing, field validation, and other built-in logic. DOM doesn't.
  3. Cross-Browser Issues: SuiteScript handles browser differences. DOM code might work in Chrome but fail in Safari.
  4. Server Sync Issues: DOM changes may not sync to the server correctly, causing data corruption.

7. Debugging SuiteScript

Server-Side Debugging

For User Event scripts and other server-side code, use NetSuite's built-in Script Debugger:

  1. Navigate to Customization → Scripting → Script Debugger
  2. Click "Debug Existing" and select your script
  3. Set breakpoints by clicking line numbers
  4. Trigger your script (e.g., save a record)
  5. Use the debugger to step through code
⚠️ Debugger Requirements
  • You must be the Owner of the script
  • The deployment Status must be Testing (not Released)
  • You must use the debugger URL (debugger.na1.netsuite.com, etc.)
  • Debug sessions time out after a few minutes of inactivity

Debugger Features

FeatureDescription
Local VariablesView all variables and their current values
WatchesTrack specific variables you care about
Evaluate ExpressionRun ad-hoc code like record.getValue('email')
Execution LogSee log output in real-time
Step Over/IntoExecute line-by-line

Using REQUIRE for Quick Tests

When testing small code snippets in the debugger, use require instead of define:

// Quick test script (no return statement needed)
require(['N/search', 'N/log'], function(search, log) {
    var results = search.create({
        type: 'customer',
        filters: [['email', 'contains', '@acme.com']],
        columns: ['companyname', 'email']
    }).run().getRange({ start: 0, end: 10 });
    
    log.debug('Results', JSON.stringify(results));
});

🏋️ Practice Exercises

Exercise 1: Context Exploration

Create a User Event script that logs all available context properties on different trigger types. Deploy to an Employee record and observe what's available in beforeLoad vs afterSubmit.

Exercise 2: getValue vs getText

On a Sales Order, log both getValue() and getText() for the following fields: entity (customer), status, memo, total. Note which method works for each field type.

Exercise 3: Change Detection

Create an afterSubmit script that detects when a specific field changes (e.g., status or salesrep). Use oldRecord and newRecord to compare, and log the change with an audit log.

🎯 Key Takeaways

  • The context object provides execution information — its contents vary by entry point
  • Use context.newRecord for current data, context.oldRecord for comparing changes
  • getValue() returns IDs/raw values; getText() returns display text (dropdowns only)
  • Custom field IDs follow patterns: custbody_, custcol_, custrecord_, custentity_
  • Log levels are hierarchical — set deployment level based on environment
  • Never use DOM — always use SuiteScript's record API methods
  • Debug with Status = Testing and you as Owner