📑 In This Module
🎯 Learning Objectives
By the end of this module, you will be able to:
- Understand what the context object is and how to use it
- Extract the record object from context
- Get and set field values correctly using the right methods
- Choose the appropriate log level for different situations
- Debug server-side scripts using the SuiteScript Debugger
- Avoid common mistakes like using DOM directly
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.
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
| Property | Description | Available In |
|---|---|---|
context.newRecord | The current record being processed | All User Event entry points |
context.oldRecord | The record before changes (read-only) | beforeSubmit, afterSubmit (edit only) |
context.type | The trigger type (create, edit, delete, view, etc.) | All User Event entry points |
context.form | The form object for UI manipulation | beforeLoad only |
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);
}
}
}
newRecord: 95% of the time — this is the current version of the recordoldRecord: 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 Type | ID Pattern | Example |
|---|---|---|
| Body field (transaction) | custbody_ | custbody_approval_notes |
| Column field (sublist) | custcol_ | custcol_item_discount |
| Record field | custrecord_ | custrecord_project_phase |
| Entity field | custentity_ | 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
- 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!
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
});
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:
| Level | Method | Purpose | When to Use |
|---|---|---|---|
| 1. Debug | log.debug() | Development info | During development, troubleshooting |
| 2. Audit | log.audit() | Track changes | Recording business events, who did what |
| 3. Error | log.error() | Handled errors | When something goes wrong but is handled |
| 4. Emergency | log.emergency() | Critical failures | Script-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:
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
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
- Not Future-Proof: NetSuite can change the underlying HTML element names in any release. Your script will break.
- Bypasses Validation: SuiteScript's setValue() triggers sourcing, field validation, and other built-in logic. DOM doesn't.
- Cross-Browser Issues: SuiteScript handles browser differences. DOM code might work in Chrome but fail in Safari.
- 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:
- Navigate to Customization → Scripting → Script Debugger
- Click "Debug Existing" and select your script
- Set breakpoints by clicking line numbers
- Trigger your script (e.g., save a record)
- Use the debugger to step through code
- 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
| Feature | Description |
|---|---|
| Local Variables | View all variables and their current values |
| Watches | Track specific variables you care about |
| Evaluate Expression | Run ad-hoc code like record.getValue('email') |
| Execution Log | See log output in real-time |
| Step Over/Into | Execute 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
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.
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.
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.newRecordfor current data,context.oldRecordfor 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