SuiteScript 2.0 Extend NetSuite With JavaScript
SuiteScript 2.0 Extend NetSuite With JavaScript
0: Extend NetSuite
with JavaScript
Student Guide
This printing: August 2021.
This document contains proprietary information and is protected by copyright and other intellectual property laws.
You may copy and print this document solely for your own use in an Oracle NetSuite training course. The
document may not be modified or altered in any way.
Except where your use constitutes "fair use" under copyright law, you may not use, share, download, upload, copy,
print, display, perform, reproduce, publish, license, post, transmit, or distribute this document in whole or in part
without the express authorization of Oracle and/or its affiliates.
Oracle NetSuite
500 Oracle Parkway
Redwood Shores, CA 94065
Table of Contents | i
TABLE OF CONTENTS
Table of Contents ......................................................................................................... i
Before you begin ......................................................................................................... 1
Important Notes .......................................................................................................................... 1
Uploading Files Manually ............................................................................................................ 2
Introduction to SuiteScript .......................................................................................... 3
EXERCISE 01: Overview of Technical Components (Required) ............................................... 3
EXERCISE 02: Adjusting NetSuite Preferences (Optional) ....................................................... 5
EXERCISE 03: Reviewing the Basics of NetSuite Navigation (Optional) .................................. 6
Developing SuiteScripts ............................................................................................... 9
EXERCISE 01: Hello World Script (Required) ........................................................................... 9
Using SuiteScript Objects ........................................................................................... 13
EXERCISE 01: Add a Free-Form Text Entity Field (Required) ................................................. 13
EXERCISE 02: Log Data from Customer (Required) ............................................................... 15
EXERCISE 03: Debugging Server-side scripts (Required) ....................................................... 17
Understanding Entry Points ....................................................................................... 19
EXERCISE 01: Add a Checkbox Entity Field (Required) .......................................................... 19
EXERCISE 02: Enable and Disable Coupon Code (Required) ................................................. 21
EXERCISE 03: Debugging Client-side SuiteScript 2.0 Scripts (Required) ............................... 24
EXERCISE 04: Validate Coupon Code When Submitting Form (Required) ............................ 26
EXERCISE 05: Validate Coupon Code When Changing It (Optional) ...................................... 27
EXERCISE SOLUTIONS ................................................................................................................ 28
SuiteScript Modules .................................................................................................. 30
EXERCISE 01: Create Sales Rep Task (Required) .................................................................... 30
EXERCISE 02: Create Custom Task Form (Required) ............................................................. 33
EXERCISE 03: Send Email to Customer (Optional) ................................................................. 35
EXERCISE 04: Investigating the Promise API (Optional) ........................................................ 37
EXERCISE SOLUTIONS ................................................................................................................ 40
Scripting Sublists ....................................................................................................... 42
EXERCISE 01: Create Product Preferences Record Type (Required) ..................................... 42
EXERCISE 02: Script Product Preferences Record Type (Required)....................................... 46
EXERCISE 03: Schedule Welcome Conversation with Sales Rep (Required) ......................... 49
EXERCISE SOLUTIONS ................................................................................................................ 52
IMPORTANT
This student guide uses functionality not covered in the
videos.
If you need assistance with newly introduced syntax, please
refer to the Help Center.
IMPORTANT
Eclipse IDE has been deprecated. Use a different IDE of
your choosing.
Refer to the below steps on how to upload files manually.
2 Click on the Add File button on the top left and select the file to be uploaded.
INTRODUCTION TO SUITESCRIPT
MODULE EXERCISES
Required Exercises
Optional Exercises
Note: The behavior will be different across browsers. In some, the file will open
directly and in others you are prompted to download. Open the file in your
favorite text editor to view it.
This script is using the SuiteScript 1.0 syntax. If you’re part of a company that
created SuiteScripts before 2015.2, you will notice that the scripts look like this.
For this course, we’ll be concentrating on the SuiteScript 2.0 syntax.
Inspect a Field ID
4 In the General subtab, look for the Defaults section and make sure that the SHOW
INTERNAL IDS is checked.
BEST PRACTICES
This option exposes the internal ids of records and fields to the user. While usually
not relevant to end users, this information is invaluable for SuiteScript developers and
must always be enabled.
6 Notice the Internal ID column? This is where you will get the internal ids of records.
We’ll be using this information to load a specific record.
Note: If you don’t see any values, expand the FILTERS at the top of the page and
make sure the SALES REP is set to - All -.
7 Open the customer record for ABC Marketing Inc by clicking on the View link.
9 Look for the Field ID. This ID will be used when referring to a field value from the script.
Scenario: You may want to adjust how web pages in NetSuite display dates, time zone, as
well as the default language.
Note: The training materials assume the English (U.S.) language, but you can
switch between English (U.S.) and English (International).
2 On the General subtab (this is the default subtab); you may adjust the following in the
Localization and Formatting sections:
3 Click Save.
Scenario: Refer to this exercise if you’re new to NetSuite or have limited experience with it.
You can refer back to this exercise at any time to enhance your productivity.
1 On the main NetSuite page, navigate to Lists > Relationships > Customers. This displays
a list view of customer records.
2 There are various Filters at the top of the page such as Sales Rep, Stage, and Style.
There is nothing you need to change now, but keep this in mind when viewing other
record lists, as you may need to adjust the selectors on them.
3 List views can be sorted by clicking on most column headings. Notice the arrow in the
NAME column heading. This indicates records are currently sorted in ascending order by
NAME.
4 Click on the Name column heading to refresh the list with the set of customers in
descending order of Name.
5 Click the NAME column heading again to change the sort order back to ascending order
by NAME.
6 Click View beside the customer whose name is ABC Marketing Inc. Now you are in view-
only mode for the customer.
7 Click Edit on the customer record to open ABC Marketing Inc. in edit mode.
9 Save the record. You are taken back to view-only mode for the customer.
10 Hover your mouse over the More Actions dropdown and choose New. This opens a
form to create a new customer.
Note: You can also create a new customer by navigating to Lists > Relationships
> Customers > New.
12 Re-open the ABC Marketing Inc. customer by hovering the mouse over the recent
records icon (located on the far left of the tabs, looks like a clock). Choose Edit to open
the customer in edit mode or choose the customer itself to open in view only mode.
13 Re-open a list of tasks, but in a new tab. This preserves the ABC Marketing Inc customer
in a separate tab.
14 Navigate to Activities > Scheduling > Tasks, but right-click on the Tasks menu before
selecting it. A context window opens. Select Open Link in New Tab to open the list of
tasks in a new tab.
Note: The selection in the context menu may vary slightly across different
browsers and/or operating systems.
15 From the list of tasks; right-click to open the task titled Phase 2: Design in a separate
tab, in edit mode or view-only mode.
Note: You can open just about any link or menu selection in a new browser tab.
16 In the Search box at the top of the page, type cu: best:
17 This immediately performed a search in the system on customers (by using the first two
or three letters of the record type as a prefix) whose names contain best. From here you
can select the record for viewing or editing.
18 You can take the above approach with any type of record in NetSuite. You can also enter
without a prefix and the search will be across all records in NetSuite. E.g. customers
returns a set of customer pages (many of them reports) and searches.
19 Enter search(or just “se”):customers in the global Search box and the results are filtered
to saved searches containing customers in the title.
DEVELOPING SUITESCRIPTS
MODULE EXERCISES
Required Exercises
To start the script development, a simple “hello world” script will need to be created.
SuiteDreams would like to have their development team be productive in the development of
scripting solutions.
CAUTION
Not adding this annotation will prevent the system from recognizing this script. If you
attempt to upload the file to the file cabinet, it will be rejected. The file cabinet accepts
JavaScript files with proper JSDoc tags.
3 Go to your define function's return statement and add an afterSubmit function. Refer to
the following syntax:
7 Click Add File on the upper left corner. Upload the sdr_ue_customer.js file.
8 After the file is uploaded, go to Customization > Scripting > Scripts > New.
9 Choose the SCRIPT FILE that you’ve uploaded then click the Create Script Record
button.
ID _sdr_ue_customer
BEST PRACTICES
Make sure to get into the habit of adding meaningful descriptions to any
customization you create in NetSuite. Doing this makes it easier to maintain
customizations and helps you keep track of the customization and why it needed to
be created.
The instructor might skip the description during the demos but this is done only in the
interest of time. This should not happen in any customization that will end up in
production accounts.
Deployments (subtab)
APPLIES TO Customer
ID _sdr_ue_customer
STATUS Testing
IMPORTANT
You should ALWAYS populate the ID field wherever you see it. As a developer, it’s
critical for us to use the well-formed IDs since we’ll be referring to these a lot in our
code. Problem is, a lot of non-developer users in NetSuite tend to ignore this field
since it’s not mandatory. When that happens, fields are given a default ID of
<prefix>+<number> (e.g., custscript1, custentity5, etc). While you can create a script
with that ID, it’s very difficult to do so. Imagine developing an application with only
single letter variable names. It would be challenging to determine which variable is for
which value.
Also make an effort to educate non-developers who has access to customization
options that have these IDs.
The format for the script id is _<companyAbbr>_<scriptType>_<description>.
➢ All IDs will be given a prefix. Starting with an underscore separates the ID from
the system generated prefix.
➢ Adding a company abbreviation to your ID prevents collisions in case you’re
installing a script from another company through a bundle, or if you’re a partner
and you’re selling your script to your own customers.
Note: You need not make any changes to the record. If a confirmation prompt
appears, click OK.
15 Go back to your script record (Customization > Scripting > Scripts; click View on the
script record).
16 Click the Execution Log subtab and check if your "Hello World" message was logged.
IMPORTANT
Visit the Help Center and SuiteAnswers for a full overview of SuiteScript functionality.
MODULE EXERCISES
Required Exercises
Scenario: SuiteDreams would like to have special discount code processing (called coupons)
applied to customer. Create a field that will hold the coupon code value.
1 Go to Customization > Lists, Records, & Fields > Entity Fields > New.
ID _sdr_coupon_code
Applies To (subtab)
CUSTOMER <checked>
Display (subtab)
SUBTAB Main
Scenario: Create an audit trail of customer data for troubleshooting purposes. The data will
be used to track the sales related information from the customer, like who sold to the
customer and what coupon code value was used, if applicable.
1 Go back to your user event script and comment out our hello world log.
2 In your afterSubmit function, get the record object from your function's context object.
Store this in a customer variable.
Note: You can get the record object using the newRecord property of your
context object.
3 From your customer object, get the values off of the following fields:
⚫ CUSTOMER ID
⚫ Customer EMAIL
⚫ SALES REP Name
⚫ COUPON CODE
Note: Remember to use the right method to extract the values: getValue() or
getText().
4 Create an audit log, to log the information that you've gathered from the previous step.
Test
7 Edit an existing customer record. Make sure that all the fields you're logging are
populated before saving.
9 Go to the Execution Log subtab and verify if the log was generated correctly.
Note: You can view formatted logs by clicking on the View button.
Scenario: As part of the development process, we’ll be looking at how to debug server-side
scripts.
1 In the NetSuite page o to Customization > Scripting > Script Debugger. Look for the,
“Click here to log on to the SuiteScript Debugger domain” and use that to open the
debugger page.
Note: You can only login to one web application server at a time. If you’re logged
in to the debugger domain, you’ll automatically get logged out of the production
domain and vice versa.
3 Click the Debug Existing button to get the list of scripts that you can debug.
IMPORTANT
For a script to appear on the list, the STATUS must be set to Testing and the
OWNER of the script must be the currently logged in user.
4 Choose your SuiteDreams UE Customer script then click the Select and Close button.
This will cause your debugger to wait for you to trigger your script.
5 In another browser tab, Edit any existing customer record and save it to trigger your
script.
IMPORTANT
Don’t close your debugger window. Make sure to open a new record in another
tab/window.
6 Immediately go back to the debugger page and wait for the script to get loaded.
7 Click on the Step Over button to execute your code line by line. Stop at about the third
line into the function.
8 Click the Local Variables subtab. Notice that the all variables that are declared in the
instance is listed here.
9 Add a breakpoint at your log.audit() call by clicking on the space to the right of the line
number.
10 Click the Continue (play icon) button to continue with the execution.
11 Go to the Break Points subtab and remove the breakpoint but clicking the x link.
Note: You can also remove the breakpoint by clicking on the breakpoint icon
beside the line number.
13 Go to the Execution Log subtab. The exercise is complete if you see the logs that you’ve
generated.
MODULE EXERCISES
Required Exercises
Optional Exercises
Scenario: To automate the customer's discounts, SuiteDreams would like to add a checkbox
that controls the coupon code field.
1 Go to Customization > Lists, Records, & Fields > Entity Fields > New.
ID _sdr_apply_coupon
Applies To (subtab)
CUSTOMER <checked>
Display (subtab)
SUBTAB Main
4 Go to the Custom Entity Fields page (Customization > Lists, Records, & Fields > Entity
Fields).
Note: This change prepares the field for the next exercise.
The exercise is complete if you see the APPLY COUPON checkbox on the body section
(above the subtabs) of your customer form.
Scenario: SuiteDreams wants to the customer discount process automated. Users expect the
following behavior for the discount fields:
Answer:
Note: Remember that you need to check for the field that the user will be
changing, not the field that you wish to set.
Answer:
7 Add an if statement to check filter the execution of the script to a specific field.
IMPORTANT
The fieldChanged entry point will trigger regardless of the field that the user
modifies. It's very important to filter the execution so that it only executes when the
user modifies the target field.
Note: You can get the record object using the currentRecord property of the
context object.
This is the same object as the one from the newRecord property. The difference
is that the currentRecord is used in client side script and newRecord for user
event (server side) scripts.
9 From the customer object, get the field reference that refers to the coupon code.
Note: The field object reference can be extracted using the getField() method of
the record object.
IMPORTANT
The field reference object is different from the field value. The reference is used to
manipulate the properties of the field such as enabling/disabling it or making it
optional/mandatory.
11 Go to the return statement and comment out all the functions that you have not used.
ID _sdr_cs_customer
Deployments (subtab)
APPLIES TO Customer
ID _sdr_cs_customer
STATUS Testing
Test
15 Create a new customer record (List > Relationships > Customer > New).
16 Test by checking and unchecking the Apply Coupon field. Make sure that the field
behaves as expected.
BEST PRACTICES
Be cognizant that the script is triggered only when the user clicks on the APPLY
COUPON field. Loading the record will still keep the coupon code field disabled since
it was set to always be disabled. To make sure that it's enabled when there's a
coupon code value, a similar automation must be added in the pageInit function. This
is a common practice with these kinds of automation.
Scenario: Debugging client-side scripts is an important skill to learn for building SuiteScripts.
2 Inside your fieldChanged function, add “debugger;” at the start of the function.
IMPORTANT
Client side scripts are debugged locally using your browser's debugger/developer
tool. The common keyboard shortcut for opening the debugger is <F12> (on
Windows) but it would still depend on your browser.
Note: The debugger should stop the execution of your script at the point where
you've added the debugger statement.
7 Notice the filename displayed for the script. Depending on your browser, you might see
that the script's filename is labeled as VM### (eg. VM1064).
8 Continue the execution of your script by stepping through your code until it finishes.
Scenario: Before submitting coupon code values to the server, it must be validated. Here is
the validation criteria:
⚫ You must enter a COUPON CODE of 5 characters in length when APPLY COUPON is
checked. The validation is performed at the time of submitting the form.
Note: You can use the length property to get the number of characters in a
string.
IMPORTANT
Remember to use the return statement to give a boolean value that would either allow
or prevent a user from saving this record.
Answer:
If APPLY COUPON is checked and length of COUPON CODE is not 5, display an alert
message to the end user regarding this length restriction. Do not submit the form.
Note: You can edit an existing customer record to test instead of creating a new
customer record.
BEST PRACTICES
On saveRecord, validateField, and any entry points that return boolean values; it’s
recommended that you put a return true statement at the end of your function.
This makes sure that your function will always end properly,
Scenario: End users decide they would rather have the coupon code validation occur
immediately upon changing the field. The validation is the same as before:
⚫ You must enter a COUPON CODE of 5 characters in length when APPLY COUPON is
checked.
Note: This is practically the same as the previous exercise but triggered from a
different entry point.
BEST PRACTICES
The validateField entry point is used if for validating individual fields. If used on
multiple fields, it can potentially be annoying for your users. A solution for this is to
validate multiple fields at the same time using the saveRrecord entry point.
If APPLY COUPON is checked and length of COUPON CODE is not 5, display an alert
message to the end user regarding this length restriction. Keep the user from clicking
buttons or editing other fields on the form.
Note: Skip this validation when Apply Coupon is unchecked, otherwise you will
get this validation message as you are disabling Coupon Code and erasing its
contents.
3 Test.
Note: This exercise is intended for you to try using the validateField event. You
would not normally create the same validation for validateField and saveRecord
events.
EXERCISE SOLUTIONS
SUITESCRIPT MODULES
MODULE EXERCISES
Required Exercises
Optional Exercises
Scenario: SuiteDreams likes to provide the best customer service in the industry.
SuiteDreams would like to find an automated way to remind sales reps to follow up with new
customers. This can be done through the automatic creation of task records.
The following are the business requirements for the task record:
Note: Tasks are created in the user interface at Activities > Scheduling > Tasks >
New.
1 Go to your customer user event script and load the record (N/record) module.
Note: Please refer to Appendix A for the syntax for loading modules.
2 In the afterSubmit function, add a condition so that the task record is created only when
new customer records are created. Editing existing customers should not trigger this
section of the script.
Note: Test this initially to see if it works. If you’re confident that it does, feel free
to comment it out so you can test the rest of the script with existing customer.
Doing so may speed up testing for this exercise.
3 Create your task record using the record module’s create method.
Note: Task records can be created in the UI by going to Activities > Scheduling >
Tasks > New.
5 Add a MESSAGE to say, “Please take care of this customer and follow-up with them
soon.”
Note: An error message will be returned if you use the wrong value or case for the
PRIORITY.
Note: For any List/Record fields, use the internal id to set the value. You can get
this from the record’s id property.
8 Assign the task to the SALES REP on the customer record but only if the Sales Rep field
is not empty.
Note: You can use an existing record if you’ve already tested this part previously.
Note: Make sure to check the FILTERS and make sure that all filters are set to
All.
12 Check the field values. The exercise is complete if all the fields are properly set.
Scenario: An Administrator at SuiteDreams decides to create a new task form to be used for
creating tasks generated from sales orders. The new form is to be used as the preferred form
and it requires that a Contact be selected. Contacts that are selectable in the user interface
are those tied to the selected Company:
Note: The script that creates a task record doesn’t require a Contact. We need to
make sure the script continues to run without setting the Contact field.
1 Go to Customization > Forms > Entry Forms, and Customize the Standard Task Form.
2 NAME the new form SuiteDreams Task Form and give it the id _sdr_task.
BEST PRACTICES
Whenever you’re creating a form, make sure that you name it based on the group
who will be using the form. In this case, it’s named Sales Customer Form because it
will be used by the Sales team. If this will be used by the whole company, for
example, you can name it SuiteDreams customer form.
Note: Enabling this forces task records to use the form when editing an existing
or creating a new record.
7 Open a new task form. Confirm that the SuiteDreams Task Form is the selected form in
the CUSTOM FORM field.
8 Give the task a TITLE then immediately Save. What happens and why?
Answer:
Answer:
Internal ID:
12 Create a payload object for the defaultValues property with the property customForm
and the internal id of the Standard Task Form.
13 In your defaultValues object, add the customform property and set it to the internal id
of your Standard Task Form.
IMPORTANT
Take note that the property name is all lowercase. Using the wrong case would cause
the system to not recognize the property.
15 Test your script by creating a new customer record. Your script is complete if the script
ran without errors and if the task record was created.
Scenario: New customers of SuiteDreams should get welcome emails sent to them.
SuiteDreams would also like to have a copy of the email attached to the customer record.
Someone viewing the customer record should be able to see that the email has been
attached.
1 Add the email and runtime modules in your customer user event script.
2 Get a reference to the currently logged in user using the runtime module’s
getCurrentUser method.
Note: You will use this user as the sender of the email.
3 Using the email module, send an email with the following configuration:
Test
IMPORTANT
Make sure that you populate the EMAIL field on the customer record you’ve created
is populated. You will get the error SSS_INVALID_TO_EMAIL or an unexpected error
if the field is empty. This is because the system would not know where to forward the
email that you’re sending.
Also, use a long fictitious email when creating your record. This is to prevent
accidentally sending email messages to real email addresses. For example, do NOT
send to [email protected] as that is a real email address and the owners of the domain
are not happy to receive hundreds of spam daily. What you can do is to use your
account number (Setup > Integration > Web Service Preferences) as your domain
name. This should look something like “[email protected]”
5 View the customer record that you’ve just created, if it isn’t already open.
6 Go to the Communications subtab. Your exercise if complete if you see the email that
your script has sent.
Note: Several of the messages in the system are future dated. Make sure to go
through the different pages to look for the email the script sent.
Scenario: Before using the Promise API, we'll be investigating how it performs when
processing multiple server calls from the client-side. The exercise will have you compare the
performance difference between promise and non-promise calls. Multiple sales order records
will be loaded on pageInit and you will be inspecting the performance using your browser's
developer tools.
Note: This script will be deployed to a sales order record. Also, take note that
records can be renamed in NetSuite. In your training account, the Sales Order
record has been renamed to Order.
2 Create two functions, one for loading records using the promise API and the other for
non-promise calls. Have both functions accept a parameter for the internal id of a
record.
Note: Make sure to put the functions outside of the returned entry point
functions. You will be calling these inside a pageInit Script
3 Go to your non-promise function and load a sales order record using the internal id
passed to the function.
4 After loading the sales order, get the TOTAL amount value and log the information to
the browser console.
5 Go to your promise function and do the same thing as your non-promise function except
use the promise version of record.load().
function promiseCall(id) {
record.load.promise({
// Set the required properties
}).then(
// The salesOrder value is returned by record.load.promise
function(salesOrder){
// Get the total field and log that in the console
}
);
}
6 Create a pageInit function and call your non-promise function about 15-20 times,
loading different records on each call.
7 Add the same amount of calls to your promise functions loading the same records.
Comment out the promise API calls for now.
Test
9 Go to a new sales order form (Transaction > Sales > Enter Orders).
Note: Monitoring network traffic in Chrome is done on the Network tab. If you're
using a different browser, the tab might be labeled differently.
11 Click the XHR button so the Network will only log XMHttpRequests. This allows you to
monitor the performance of calls to the server.
12 Refresh the sales order form and monitor the performance of the requests sent by your
non-promise function.
Note: Looking at this kind of request, you can see the requests are called one
after the other. Multiple requests like this will take a long time to do.
13 Take note of the total amount of time it took for the requests to complete.
14 Comment out the non-promise calls and uncomment the promise calls.
15 Refresh the page again and notice how the network logs have changed.
Note: Comparing this to the previous request, each promise call runs
independently from the main thread. This allows the script to make multiple
server calls without waiting from the previous server call to finish; making this a
more efficient approach.
16 Take note of the total amount of time it took for the promise requests to complete:
BEST PRACTICES
Promise calls are extremely effective when used properly. Make sure to think about
processing multiple threads at the same to get the most out of promises.
EXERCISE SOLUTIONS
8 Give the task a TITLE then immediately Save. What happens and why?
Answer: The record was not saved because the CONTACT field needs to be populated. This
is because the preferred form has the field set as mandatory.
9 Re-execute the script from the previous exercise. What happens and why?
Answer: The script is not working anymore. It’s asking for the contact field to be populated
similar to the NetSuite UI. This is because SuiteScript uses the preferred form when
creating and modifying record objects.
SCRIPTING SUBLISTS
MODULE EXERCISES
Required Exercises
Scenario: SuiteDreams is creating a custom record type to store information about products
that customers often order. This is used by SuiteDreams to automatically generate sales
orders, as well as determine what items might need to be replenished from inventory.
End users do not have access to custom record types directly, but should be able to access
product preferences as a sublist off of the customer record.
PREFERRED QUANTITY Identifies the quantity of the preferred item; this is the
quantity of the item the customer usually places on a sales
order
1 Go to Customization > Lists, Records, & Fields > Record Types > New.
2 Create your new custom record with the following configuration. Unless otherwise
specified, keep all fields as their defaults.
ID _sdr_prod_pref
SHOW ID <checked>
Note: You need to initially save the custom record before you can add custom
fields.
4 Click New Field button from the Fields subtab to create fields for the Product
Preferences record type.
LABEL Customer
ID _sdr_prod_pref_customer
TYPE List/Record
LIST/RECORD Customer
LABEL Item
ID _sdr_prod_pref_item
TYPE List/Record
LIST/RECORD Item
ID _sdr_prod_pref_qty
Note: This attaches the Product Preferences (child) custom record as a sublist to
the Customer (parent) record.
7 Go to the Display subtab and choose Sales in the PARENT SUBTAB field.
9 In the Custom Record Type page, hover over the More link (located at the upper right-
hand corner of the page) then click on the View Records link to view, edit, and create
new product preferences.
10 Create a minimum of two product preferences for each of customers. When selecting
items, select inventory items.
11 To find inventory items, go to Lists > Accounting > Items, making sure the TYPE filter at
the top of the page is set to Inventory Item and the VIEW filter is set to All.
12 Use items that have both a PURCHASE PRICE and an amount listed for AVAILABLE.
Note: You are going to need to scroll to the right to see these field columns.
Future exercises are going to evaluate the PURCHASE PRICE and AVAILABLE
quantity.
CAUTION
When selecting items, make sure that you’re selecting item records instead of the
parent items.
13 Edit one of the customers for which you created a product preference (Lists >
Relationships > Customers). You should see a Product Preferences subtab display as a
child subtab to the Sales subtab.
14 Here is a sample of what you might see when you click on the Product Preferences
subtab of the Sales subtab on a customer record. You should be able to click into each
field in the sublist and edit the values in place.
⚫ Alert the end user to the number of product preferences upon opening a customer
record for editing.
⚫ Default the QUANTITY to 1 when entering a new product preference.
⚫ Apply the following validation to the addition and modification of product
preferences on the customer record:
PREFERRED QUANTITY of a product preference cannot be more than 10.
⚫ Apply the following validation at the time of form submittal:
The total PREFERRED QUANTITY across all product preferences for an individual
customer cannot exceed 25.
1 Alert the end user to the number of product preferences upon opening a customer
record for editing.
What is the correct client entry point for implementing this script?
Answer:
2 Determine the sublist id. For custom record types, it is recmach + the id of the
list/record field you used to link this record as a sublist. What is the sublist id?
Answer:
3 Go to your client-side script deployed on the customer record and add the statement to
get the number of sublist lines.
4 Display the following alert message: This customer has <n> product preferences, where
n = the number of line items
5 Test this alert message before moving onto the next section.
6 Default the PREFERRED QUANTITY to 1 when entering a new product preference (i.e. a
new line item, one where the preferred quantity is empty), otherwise do nothing.
CAUTION
Make sure to wrap your getCurrentSublistValue() statement with a parseInt() or
parseFloat(). When the value is empty, the functions will return a NaN value. You
could use the isNaN() function to do value check.
Here's an example on how it would look like:
var qty = parseInt(value);
if (isNaN(value)) {
// do something
}
7 Use the lineInit entry point for this section of the exercise.
8 Test this line initialization script before moving onto the next section.
Note: The lineInit entry point gets trigged when the user clicks any of the sublist
buttons. When you initially load the record, the sublist value will not default to 1
unless the user clicks on a sublist button. Make sure to remember this behavior
and test by clicking on the sublist button instead of just refreshing the page.
9 Preferred quantity of a product preference cannot be more than the 10. Validation
should occur as a line is being inserted or edited.
What is the correct client event function for implementing this script?
Answer:
11 Alert the end user with the following message when the PREFERRED QUANTITY is
greater than 10: You have selected a preferred quantity that exceeds the limit of 10.
Note: Like the validateField and saveRecord entry points, you must return a
boolean value on this function.
12 Test this validation script before moving onto the next section.
13 The sum of PREFERRED QUANTITY across all product preferences for a customer
cannot be more than 25. Validation should occur at the time of form submittal.
What is the correct client event function for implementing this script?
Answer:
14 Loop through all line items, adding up the PREFERRED QUANTITY. Use a for loop or any
other looping constructs you’re comfortable with.
15 Return the following message when the total preferred quantity exceeds the limit of 25:
The total preferred quantity across all product preferences has exceeded the limit of
25.
IMPORTANT
Remember that sublist line numbers start with 0.
Scenario: Aside from the task record for the sales rep, the script will also be creating a
meeting with new customers. If no sales rep is assigned to a customer, the user should be
prevented from saving the record.
2 Add a beforeSubmit entry point function to the script and get the customer object from
the context.
3 Filter the execution so that it only runs when a new customer record is created.
Note: Comment the code for now to make testing faster but test it later once the
script has been completed.
5 Add a condition so that if the SALES REP field is empty, it would throw an error
message.
6 To display an error, use the throw statement to display a string error message. This
could be as simple as, "throw 'Save failed. Please make sure that the Sales Rep
field is not empty.';"
Company <customer>
Note: Use the SuiteScript Records Browser to get the ids for the event record.
The COMPANY field is under the Related Records subtab of the event form.
IMPORTANT
When creating the record object, make sure to set the isDynamic property to true.
This is necessary since we're dynamically accessing sublist values.
Test
10 Test the script by creating a new customer record with the sales rep assigned.
11 Go to the list of events, Activities > Scheduling > Events, and verify that the event was
created.
Note: Make sure that all events are listed by setting the FILTERS values are set
to All.
CAUTION
When selecting a customer for testing, make sure to use a customer's SALES REP is
not the currently logged in user. Doing so will cause the script to throw an error since
the user is already in the event as an organizer.
EXERCISE SOLUTIONS
1 What is the correct client entry point for implementing this script?
Answer: Page Init since you want to alert the user upon loading the page.
2 Determine the sublist id. For custom record types, it is recmach + the id of the
list/record field you used to link this record as a sublist. What is the sublist id?
Answer: recmachcustrecord_sdr_prod_pref_customer.
9 What is the correct client event function for implementing this script?
13 What is the correct client event function for implementing this script?
Answer: Save Record since you need to validate upon saving the record.
SEARCHING IN NETSUITE
MODULE EXERCISES
Required Exercises
Scenario: SuiteDreams would like to determine when there may be shortages for products
preferred by their customers. Any product preference where the Preferred Quantity is
greater than or equal to 2 is indicative of a potential shortage if these product preferences
become sales orders.
SuiteDreams is only interested right now in determining product shortages where Customer
Subsidiary is in HEADQUARTERS : AMERICAS : US – West.
1 Add or modify product preference records so that a search will return results when
based on the filters described in the Scenario section.
Note: You can find customers in the required subsidiary by going to Lists >
Relationships > Customers.
3 Select the appropriate Search Type or record on the New Saved Search page.
Answer:
ID _sdr_prod_shortages
Note: Leave other fields in the main area of the form as their defaults.
6 In the Criteria’s Standard subtab, configure your filters based on the following:
FILTER DESCRIPTION
9 In the Columns subtab, return the following fields in the search results.
FIELD
Customer
Customer’s Email
Customer’s Subsidiary
Item
FIELD
Preferred Quantity
BEST PRACTICES
You can also view the results using the Preview button but remember that the saved
wouldn’t be saved. It would be safer to keep saving your results specially for searches
with multiple criteria.
IMPORTANT
If you’re not getting any results, check your product preference record list. Make sure
that you have at least one record that matches the criteria.
Scenario: SuiteDreams plans to execute the Product Shortages search as part of some
periodic processing through scheduled scripts, and then set up support cases based on the
search results.
This exercise is a very first step by developers to test out the execution of the saved search
from within server-side JavaScript.
Note: This script will be used in the next module’s exercise. For now, we’ll be
running all scripts using the Script Debugger.
4 Using the load method, load the saved search using the search module. This will return a
Search (search.Search) object.
BEST PRACTICES
The load method accepts a saved search's internal id or the script id as a parameter.
Since the internal id will change when the search is moved from one account to
another, sandbox to production for example, it's best to always use the script id.
5 Execute the search using the run() method of the Search object then use the
getRange() method on the resulting object to get the first 1,000 results.
Note: The getRange() method needs a payload object with two properties, start
and end. These properties define the index numbers for the results. To get only
10 results use, 0 as a start value and 9 as an end value.
6 Go to Customization > Scripting > Script Debugger and login to the debugger domain.
7 Copy the codes from your script file to the debugger's editor window.
Note: The require statement syntax is very similar to the define statement. The
only difference is that the define statement returns an object and the require
doesn’t.
9 Set the API VERSION to 2.0 and click the Debug Script button.
10 Put a breakpoint at the statement that returns the search result and click the Continue
(play icon) button.
11 Go to the Local Variables subtab and check the search result values. It should still be
empty at this point.
12 Click the Step Over button to execute the search and check the results again. Notice
that the Local Variables subtab is empty.
Note: Executing the last line of the code ends the debugging session which
prevents you from inspecting the contents of the variable from the last call.
14 Debug the session again and pause the execution at your stopper line.
15 Inspect the search result and verify that the result matches the execution in the
NetSuite UI.
Scenario: SuiteDreams has determined that searching for a product shortage may need to be
a little more dynamic in terms of the filtering that is required. To support this going forward;
SuiteDreams is having its developers convert the current saved search into a manually
created search from within the JavaScript.
1 Prepare your search by knowing what you’re going to use in your script. Use the
following as a guide
2 Plan your search filter configuration. Determine the IDs you’re going to use for the field,
join, operator and value.
Note: Remember to use the SuiteScript Records Browser to get the appropriate
IDs. For IDs belonging to your custom record, use the IDs in the custom record
definition.
Also, the internal IDs for the subsidiary fields is listed in Setup > Company >
Subsidiaries.
Field Join
4 Go to your IDE to start building your search. First comment out search.load statement
from the previous exercise.
5 Just before the run method call, create your search.Search object. This is similar to what
you’ve used in the previous exercise but this time use the create method of the search
module.
Note: In this step we need to pass an object with three properties: the search’s
record type, an array of search filters, and an array of search columns.
6 For your search filters, you need to build an array of search.Filter objects. You can
create this object using the search module’s createFilter() method.
Note: Use the IDs that you’ve gathered in the earlier steps.
7 Building a search column array is similar to the search filters. Use the createColumn()
method of the search module.
Note: You can get the internal ids of subsidiaries at Setup > Company >
Subsidiaries.
8 Go back to the Script Debugger page and copy your script to the Editor window.
Note: Remember that you should be using the require function to use the
debugger.
9 Debug the script and go to the Local Variables subtab to inspect your search result.
10 Your script is complete if it’s returning the same values as the saved search you’ve
created in the previous exercise.
Scenario: Complete the product shortage search by logging the results of the search.
2 After your call to run the search, add a for loop to iterate through the results.
3 In each element of the array, extract the field values by using the either the getValue()
or the getText() method.
4 Once you’ve gotten your field values, log them using the log.debug() method.
5 Go back to the Script Debugger and copy the script to the editor.
6 Run the search completely. Your script is complete if the Execution Log subtab displays
all the information that you’ve logged in your script.
EXERCISE SOLUTIONS
4 Select the appropriate Search Type or record on the New Saved Search page.
2 Plan your search filter configuration. Determine the IDs you’re going to use for the field,
join, operator and value.
Field Join
custrecord_sdr_prod_pref_customer <none>
email custrecord_sdr_prod_pref_customer
subsidiary custrecord_sdr_prod_pref_customer
custrecord_sdr_prod_pref_item <none>
custrecord_sdr_prod_pref_qty <none>
quantityavailable custrecord_sdr_prod_pref_item
MODULE EXERCISES
Required Exercises
Optional Exercises
Scenario: SuiteDreams wants the existing product shortage search to run on a scheduled
basis.
1 Go back to the IDE and edit the script from the previous exercise by converting the
require statement back to a define statement.
2 Add the two required SuiteScript 2.0 annotations for the script.
Note: This will allow your script to be triggered as a scheduled script entry point.
5 Create a script record for your script with the following configuration:
ID _sdr_ss_product_shortages
6 Hover over the Save button and click on Save and Deploy.
Note: You can also use the Deployment subtab to configure your deployment
record. For this exercise though, we’ll be using the deployment record page to
get more details.
ID _sdr_ss_product_shortages
BEST PRACTICES
When creating scheduled scripts, execute the script immediately to check if
everything is working. Once you’re sure that the script is working fine, then go ahead
and set the schedule that you need.
9 Edit the deployment record again then hover over the Save button then click on
Save and Execute. This will redirect you to the Scheduled Script Status page.
Note: If you accidentally clicked away from the page, you can go back by going to
Customization > Scripting > Scheduled Script Status.
12 Go to the Execution Log subtab and verify if the logs where triggered successfully.
The exercise is complete if all the search results were logged.
Scenario: Right now SuiteDreams has to look through every execution log entry from the
Product Shortages search to determine whether a product shortage warrants additional
action. What they want is to have the system automatically generate a support case for each
product shortage search result where the available quantity of an item is less than the
preferred quantity on a product preference. The search parameters should not be changed.
This should be additional processing performed on the search results.
1 Create a support case (Lists > Support > Cases > New) to familiarize yourself to the
record, paying attention to the mandatory fields.
2 Go to the scheduled script from the previous exercise and after the logging statement
(inside the for loop), add a condition to support the exercise scenario.
Also, make sure to surround your quantity values with parseInt() to make sure
that you're getting native number values.
Note: Use the SuiteScript Records Browser to get the field IDs.
Test
6 Update the preferred quantity on some of your product preferences so they will exceed
the available quantity. If you enter 999 for preferred quantity, then this should exceed
the available quantity for most items.
Note: If you configure the product preferences directly from the customer
record, you will need to update the validation from one of the previous exercises
to allow for a higher level of preferred quantity, or you can remove the
validation entirely.
MODULE EXERCISES
Required Exercises
Note: To keep the exercise simple, we'll be logging the values instead of creating an
actual report.
1 Go to the NetSuite UI and create a Transaction search (List > Search > Saved Searches >
New).
ID _sdr_payments
FILTER DESCRIPTION
Type is Payment
Columns : FIELD
Name
Status
Amount Paid
Note: We're creating a saved search instead of a script search to use the
reference object format for the map/reduce.
3 Save & Run the search to check if you're getting the right results.
6 In the getInputData() return an object reference pointing to the saved search you've
created.
Note: An object reference is simply a payload object that has type and id. For
example:
return {
type : 'search',
id : 1234
}
8 Extract the search result value from the map's context object. This is stored in the value
property.
Note: Remember that the map stage processes a single search result value per
invocation. You need not loop through the results as you would in other script
types.
9 Log resulting value property of the context object to inspect the JSON string.
Initial Test
11 Create a script record and click Save and Deploy to create a deployment record.
Note: Take note of the ID that you use for the deployment record.
12 On the Script Deployment page, specify and ID then Save the record.
Note: Feel free to initially modify the saved search to get fewer results. This
would make the execution faster.
13 Edit the deployment record again and execute the map/reduce script.
14 Once the execution is complete, go to the Execution Log and take note of the JSON
structure.
15 Go back to the script and convert the JSON string to a JavaScript object using
JSON.parse().
Note: Remember that the system will be returning a JSON string value. Using the
JSON.parse() function makes the value easier to handle.
16 Write a key/value pair back into the context using the write() method of the context
object. Use the customer name as a key and the status & amount as the value.
18 Extract the array of values that was associated to the key in the previous stage. You can
get this from the values parameter of the reduce's context object.
Note: Similar to map function, the reduce function processes each individual
key/value pair so there's no need to iterate through the results to get one pair.
19 Create a variable that will hold the deposited and undeposited values. Initialize those
values to 0.
20 Loops through the values array, extract one element of the values array and put that in
a variable.
Note: Since you're using an object as a value for your key/value pair, you need to
convert that individual element from a JSON string to JavaScript using
JSON.parse().
21 Add a condition to check the values to total all deposited and undeposited amounts.
22 Log both the name and the combined totals for each customer.
Test
25 Click the Details link to view the status of execution of all stages.
26 Go back to the script or deployment record's Execution Log subtab. The exercise is
complete if the invoice data is properly logged.
SCRIPT PARAMETERS
MODULE EXERCISES
Required Exercises
Optional Exercises
Scenario: Display a confirmation message when saving a customer and partner record. The
message should mention which record the user is trying to save.
IMPORTANT
This exercise is an example of how to pass values from deployment level script
parameters to the script. Normally if you need to get the type of a particular record,
you would be getting that from the type parameter of your record object.
1 Create a new client script file and add the runtime module as a dependency.
2 Create a saveRecord function, add an alert statement that would display the message
mentioned in the scenario.
3 Upload the file to the File Cabinet and create a script record.
5 Click the Parameters subtab then the New Parameter button to create the script
parameter.
ID _sdr_save_record_type
PREFERENCE <Blank>
9 Open both the Customer and Partner deployment records in separate subtabs.
11 In the Paremeters subtab, set the SAVE CONFIRMATION MESSAGE – RECORD TYPE
field to Customer then Save.
12 Next go to the Partner deployment record and do the same thing, this time setting the
field to Partner.
14 In your saveRecord function just above your confirm statement, get the script object
using the getCurrentScript() method of the runtime module.
15 Once you have your script object, get the script parameter value using the script object’s
getParameter() method.
Note: The payload object for the getParameter() method has one property,
name, which accepts the id of the script parameter.
16 Edit your confirm statement so that the message incorporates the value from the script
parameter.
Test
17 Edit an existing customer record then save it. Check that the proper message is
displayed.
18 Also edit a partner record and check the message. The exercise is complete if the
confirm message adapts to the record being saved.
Scenario: The prompting of an "are you sure" message upon saving a customer, partner, or
vender should be configurable on a per user basis.
ID _sdr_save_confirmation
PREFERENCE User
Modify Script
3 Go to back to your script and navigate to the lines that you’ve just edited.
4 Get the value of the save confirmation script parameter and use the value to support
this requirement:
If the DISPLAY SAVE CONFIRMATION field is checked, display the confirmation message;
otherwise, continue saving without displaying the message.
Test
5 Edit a customer record and save. Confirm that the message still displays.
7 In the Custom Preferences subtab, uncheck the DISPLAY SAVE CONFIRMATION field
then Save.
8 Edit and save a customer record again. The exercise is complete if the confirmation does
not appear when a customer or partner record is saved.
Scenario: SuiteDreams requests that the payment summary be specific to a customer. And to
automate the process, it would be automatically triggered when a customer record is saved.
Note: This exercise continues the exercise from the map/reduce module and
covers how to pass values from one script to another using script parameters.
This process can also be applied to other script types like scheduled scripts, and
csv import.
4 Copy the map/reduce's scriptId and deploymentId. Set these values as property values
to your task object.
5 Go to the map/reduce's script record and create a free-form text parameter to hold the
internal id of the customer. Take note of the script parameter id.
6 In your user event script, pass the customer id as a parameter in the task object. The
parameter payload is a key/value pair with the script parameter ID as the key.
IMPORTANT
Make sure to use the script parameter id exactly or it will not work.
7 Use the task object's submit() method to execute the map/reduce script.
10 Using the runtime method, load the map/reduce's script object and get the value of the
script parameter.
11 Add the customerId from the script parameter to the search so only invoices from that
particular customer will be processed.
IMPORTANT
Since the search is loaded from the UI, you need to recreate the search in the script
to incorporate the customer id passed from the user event script.
Test
The exercise is complete if the invoice was logged for the saved customer.
EXERCISE SOLUTIONS
MODULE EXERCISES
Required Exercises
Optional Exercises
Scenario: Most people that enter sales orders for SuiteDreams end up needing to place some
notations onto the related customer record. To speed this up; a workflow is created to
automatically move the end user to the customer record after a sales order is submitted.
Define Workflow
Basic Information
ID _sdr_process_sales_order
Event Definition
ON CREATE <checked>
ON UPDATE <checked>
Note: Alternatively, you can also click on the pencil icon in the State subtab.
6 While State 1: Entry is still selected, click on the + New Action button at the lower right
hand corner of the screen.
FIELD Entity
Test
9 Edit your Sales Order script and undeploy it by unchecking the DEPLOY option under the
Deployments subtab.
Note: The previous script can slow down your testing and needs to be disabled.
10 Open an existing sales order or create a new one (Transactions > Sales > Enter Orders).
11 Save the sales order and the end user should be automatically taken to the customer
record defined in the Customer field of the sales order.
The exercise is complete if you were automatically redirected to the customer record
that is on the sales order.
Scenario: Update the related customer record with a notation about the sales order, and
then navigate the end user to that record (as is being done currently).
1 Create script file and name it sdr_wf_update_customer.js. Load the record and runtime
modules as a dependency.
3 To start, add the lines that will get a script parameter. Use the ID,
custscript_sdr_order_date.
Note: We’ll be creating this later after we create the script record.
4 Get the sales order record object from your entry point’s context object.
Note: This is stored in the same property as your user event scripts.
Note: Don’t forget to use the SuiteScript Records Browser to get the proper ids.
Note: You can append \n to place a carriage return into your string.
7 Get the internal id of the customer on the sales order record. Use this to load the
customer record object.
8 Once you have the customer object, update the COMMENTS field with the value from
your notes variable.
9 Create script and deployment record for workflow action script. Use the following
values:
Script Record
ID _sdr_wf_update_customer
BEST PRACTICES
The name that you use in your workflow action’s script record will be displayed in the
list of action on the workflow. Using your normal naming convention for scripts can
potentially confuse your workflow users.
To help with the use the following format for your names: <verb> + <description>. For
example, “Send email”, “Reset form values”, or “Hide related record”.
ID _sdr_order_date
TYPE Date
PREFERENCE <blank>
Note: Make sure that the ID you use here and your script are the same,
otherwise you will get an error message.
APPLIES TO Order*
ID _sdr_wf_update_customer
11 Go back to your Process Sales Order workflow and click on State 1: Entry in the
diagram.
12 Click the + New Action button and look for the script that you created.
Note: Remember that it will use the name that’s in your script record.
14 Scroll down to the Parameters section and choose Date in the VALUE FIELD column for
the Order Date field. This copies the date from the sales order and passes it to the script
parameter so the script would be able to use it.
Test
16 Test the workflow by opening an existing sales order or creating a new one.
17 Save the sales order. The exercise is complete if you see the notes from your script
displayed in the COMMENTS field of your customer.
Note: After you’re done, disable the workflow by setting it as inactive. This
prepares the account for the next module.
CAUTION
If you don’t see the Comments get updated, it may just be because the browser did
not load the latest copy of the page. Open the customer in another browser tab to
check if the record was updated.
Scenario: The update to the related customer record could potentially fail, though unlikely.
Workflows can be designed to properly handle failures such as this, or from any other custom
action. To see how this works, the workflow is going to be modified to branch out to one of
two different end states depending upon the success of the custom action.
1 Go back to your IDE and edit your script. Have it return “SUCCESS” if the customer
record was properly updated and “FAILED” if it wasn’t.
Note: The save() method returns the internal id of the successfully saved record.
You can use this as a condition.
3 In the Parameters subtab, set the RETURN TYPE to Free-Form Text. This tells the
system to expect the script to return a value.
6 Click on State 1: Entry then select the Fields button. At the bottom of the page, click
+ New State Field button.
Note: This field will hold the value that’s returned by the script.
ID _sdr_customer_update_status
9 Click on the Actions button and edit the Update Customer action.
10 Scroll down and look for the STORE RESULTS IN field. Set this to the state field you’ve
just created.
13 Call one State 2: Success and State 3: Failure for the other.
Note: You can re-arrange the workflow states in any direction you choose.
14 Add a transition from State 1 to State 2 by dragging the transition handles (half circle)
from one state to the other.
Note: Similar to the state, you can also click on the pencil icon in the Transition
pane while it’s highlighted to edit it.
16 Hover over the CONDITION field and click the open button that appears beside it. Use
this for your condition:
VALUE SUCCESS
18 Repeat the process with State 3, from creating the transition and the condition, but this
time use the value FAILED for your condition.
Test
19 Open an existing sales order or create a new one then Save it.
Note: You can re-open the record using the Recent Records options.
22 Check the log entries. The exercise if complete if the workflow transitioned from State 1
to State 2 (or State 3 depending on the status).
Note: After you’re done, disable the workflow by setting it as inactive. You can
do this by clicking on the pencil icon in the Workflow subtab.
NETSUITE PAGES
MODULE EXERCISES
Required Exercises
EXERCISE DIAGRAM
IMPORTANT
Here’s an illustration on what you will be doing in the following exercises.
For the first part, the user is automatically directed to the suitelet as soon as the sales order
record is saved. The user will then update the FINANCING PRICE field through the suitelet.
The use would then be saving the information by submitting the suitelet. The suitelet would
then redirect the user back to the sales order record to verify if the financing price was
updated.
Scenario: A sales order financing program is added to sales orders through creation of a form
Suitelet.
When an order is submitted, users are redirected to a custom page where they will enter the
financing information. They will then submit that information to the system and get
redirected back to the order. The system will be updating the finance information in the
background.
Note: This is the first of four exercises to configure the Suitelet. This exercise creates
a form, adds some help text, adds a button, and configures the Suitelet to be
selectable from the menu.
3 Create your base form object by using the ui/serverWidget module’s createForm()
method. This will return a serverWidget.Form object.
5 Before adding the fields, it’s important to remember the id naming convention for
suitelets.
What system prefix should be used for field IDs when creating fields on form objects?
Answer:
6 Add fields to the form by using the addField() method on your form object. Use the
following configuration:
label Please assign a price to the financing of this sales order, then click Submit
Financing
type FieldType.HELP
Note: Please see the Appendix for more information about the Hungarian Notation.
7 Add a submit button to your form to allow the user to send information back to the
server. Set the title to Save Finance Info.
8 Render the page by writing it to response. This is done by passing the form object to the
writePage() method of the response object as a parameter.
Note: The response object can be obtained from the context object through a
property by the same name.
ID _sdr_sl_salesorder_finance
ID _sdr_sl_salesorder_finance
SECTION Setup
CATEGORY Custom
Note: Initially, the suitelet will be accessed through this link for faster testing.
Later we’ll be redirecting to the suitelet directly from the sales order form.
Test
14 Select the Suitelet from the Setup > Custom menu. Alternatively, you can also click on
the link in the URL field of your script deployment. It should look similar to the
following:
Note: If you do not see the link in the menu, try clearing cache using CTRL + F5 or
going into your browser options. You may also need to log off and back on.
Scenario: This exercise causes the end user to be redirected to this Suitelet upon submitting
the sales order. The following sales order information is displayed on the Suitelet:
• ORDER #
• CUSTOMER
• TOTAL
Note: Turn off the workflow from the previous exercise if you haven’t already done
so as it can interrupt the execution of the script.
1 Create a user event script for the sales order record (sdr_ue_order.js) and add the
redirect module as a dependency.
2 Get a copy of your sales order object and extract the values off the following fields:
• ORDER #
• CUSTOMER
• TOTAL
3 Using the redirect module’s toSuitelet() method, send the user to your custom page.
Note: The toSuitelet() method needs an object with three properties: the
scriptId, deploymentId, and parameter list. The parameter list is an object with
key/value pairs that you’ll be sending to your suitelet.
BEST PRACTICES
When creating your parameter object, it’s recommended that you use custparam to
prefix your parameter names. For example, your parameter might look like:
{ custparam_sdr_name : 'Mel',
custparam_sdr_id : 23
}
While using custparam to prefix your parameters is not required but it’s highly
recommended. You script will most likely break if it collides with system generated
URL parameters or with parameters from other scripts.
4 Create a script record for your user event script. Use the following configuration:
ID _sdr_ue_salesorder
5 Deploy the script to the Order record and give it the ID _sdr_ue_salesorder.
Test Redirection
10 Extract the values that you’ve passed from your user event script by accessing the
parameter object from your request object. For example if you passed a parameter
named custparam_sdr_name, you can access it like this:
context.request.parameters.custparam_sdr_name.
Note: Like the response object, you can also access the request object from your
function’s context object.
Note: These fields will store the parameter values from your user event script.
Label Type
FieldType.TEXT
Order #
FieldType.TEXT
Customer
FieldType.CURRENCY
Total
12 Use the field object’s defaultValue property to assign the values you’ve extracted from
the request.
13 Change the fields to inline so the user won’t think that the fields can be edited. This can
be done using the field object’s updateDisplayType() method. Set it to
serverWidget.FieldDisplayType.INLINE.
Retest Redirection
14 Repeat the test from earlier in this module. This suitelet should contain sales order data,
similar to the following:
Note: Order # for new records is not available during after submit of the sales
order user event script. It displays on the suitelet as To Be Generated. A
workaround is to load the sales order from your suitelet and get the order
number from there instead.
Scenario: This exercise causes the end user to be redirected back to the sales order upon
submittal of the suitelet. Summary of the additional processing:
The suitelet is modified to differentiate between GET and POST request processing. Existing
suitelet processing plus entry of the financing price is to occur during the GET request.
Update of the sales order and redirection back to it is to occur within the POST request.
1 Create a Financing Price transaction body field (Customization > Lists, Records, and
Fields > Transaction Body Field > New). Use the following configuration:
ID _sdr_financing_price
TYPE Currency
Applies To (subtab)
SALE <checked>
Display (subtab)
SUBTAB Main
2 Open a sales order record and verify that the FINANCING PRICE field was added to the
form.
4 Get the FINANCING PRICE field value and pass it to the request like what you’ve with
the other fields.
5 Get the sales order’s internal ID and pass that over to the request as well.
Note: We’ll be using this id later in the exercise to update the sales order from
the suitelet.
6 Edit your suitelet and add the FINANCING PRICE field to the form.
7 Get the FINANCING PRICE value from the request and set that as a default value.
Note: Don’t set the field as inline because the users will be editing that value.
Empty fields are returned as undefined so make sure to check for that when
setting the value.
8 Add the sales order id to the form and process it similar to the FINANCING PRICE field.
Set the display type to HIDDEN.
Note: By determining how the user is sending the request, we’ll be able to filter
the execution of our suitelet. If the user sends a GET request (script redirections
are GET requests) then the suitelet will be displaying the form. If the user sends a
POST request (form submissions are POST requests) then the suitelet will be
updating the sales order and will redirect back to the sales order.
10 In your suitelet script, add an if statement to check if the request if it is a GET request.
Note: This value can be tested against the method property of your request
object. Use the string “GET” for your condition.
11 Move all form related statements inside your if statement so that the form will be
displayed if the user is sending a GET request.
Note: This includes processing the parameters and writing to the response.
12 Add an else block. This is where the form submission will be processed.
To load a record, which SuiteScript API module would you need to include in your
script?
Answer:
14 Load the sales order record using the id from the request.
CAUTION
Since you’re processing a POST request, that means your data is coming from your
form, not from your GET request parameters (passed from the user event script).
Getting the data from the form is similar to GET request. You will be extracting the
values from the parameters property of your request object. The difference is that
you’ll be using the ids of the form fields. Again, this is because you’re processing the
form that you’ve submitted.
15 Set the financing price value on your sales order from the updated value that the user
entered on the suitelet.
17 Redirect the user from the suitelet back to the sales order. This is done using the
redirect module’s toRecord() method.
Note: Use the Type enum from the record module to set which record the user
will be redirected to.
Test
18 Perform the same test that you’ve done previously. The exercise is complete if the user
was able to update the financing price field from the suitelet and get redirected back to
the sales order to confirm that the field was updated.
EXERCISE SOLUTIONS
4 What system prefix should be used for field IDs when creating fields on form objects?
Answer: custpage
Note: The internal ID must be in lowercase, contain no spaces, and include the
prefix custpage if you are adding the field to an existing page. For example, if you
add a field that appears as Purchase Details, the field internal ID should be
something similar to custpage_purchasedetails or custpage_purchase_details.
12 To load a record, which SuiteScript API module would you need to include in your
script?
WEB SERVICES
MODULE EXERCISES
Required Exercises
⚫ End users must have immediate validation feedback in the same way as the current
coupon code validation regarding its length
⚫ The set of valid coupon codes is not to be exposed in the browser (i.e. in the html of
the web page). This is for security reasons.
The solution to SuiteDream’s requirements is to embed the business logic inside of a RESTlet
and then call the RESTlet from the client side script. The solution is implemented in this
exercise.
Create RESTlet
3 Create a get() function and create a variable that would accept a coupon code value
from the url parameter. Assume that the parameter is named custparam_couponcode.
Note: URL parameters in RESTlets are passed directly to the function. Unlike
suitelets where parameters are extracted from the request object, RESTlets
would take in parameters with this format requestParams.myParameter.
4 Add a condition that would return the string "valid" if the coupon code is equals to
ABC12 and "invalid" if it's not.
6 Go to your customer client script and add the https and url modules as dependencies.
Note: Use the saveRecord entry point if you were not able to do the validation
on the validateField entry point.
8 Add an if statement to replace the commented out validation so that it triggers only
when the APPLY COUPON CODE is checked and the COUPON CODE field is populated.
9 Inside the if statement, get the RESTlet URL using the url modules resolveScript()
method.
Note: Pass the scriptId and deploymentId as parameters to dynamically get the
RESTlet URL using resolveScript().
BEST PRACTICES
While you can use hardcoded RESTlet and suitelet URLs from your script, it is bad
practice to do so. These URLs change when transferred from one account to another
(i.e., sandbox to production). Hardcoding URLs of any kind is not future proof and
should be avoided.
10 Call the RESTlet by using the https module's get() method. To pass parameters simply
append the parameter value to the URL. Use the name custparam_couponcode.
Note: Get requests use values passes values through the URL. To do this add, an
ampersand and the key/value pair at the end of the URL. For example,
url +"&key=value".
11 The get method will return a response object. You can get the returned string from the
RESTlet from the body property of the response.
12 Add a condition that validates the response. If the response is invalid, then display an
alert message so the user is aware that the coupon code is invalid.
IMPORTANT CONSIDERATIONS
Module Overview
Before closing the course out, it’s there are a few important considerations that we need to
think of before, during and after development.
In this module we’ll cover the SuiteScript development life cycle; what SuiteScript
governance is; and how to optimize your code by using the Application Performance
Management tool. Lastly we’re going to look at packaging your customizations up using the
SuiteBundler tool.
Module Objectives
Upon completion of this module, you will be able to:
Note: Sandbox accounts are purchased separately. For more information about it,
please talk to your local NetSuite sales representative.
Error Handling
Handling errors in NetSuite is the same as any other JavaScript API; exceptions are caught
and handled using the try/catch block.
Exceptions in NetSuite are returned as JSON strings. The string should then be converted into
a plain JavaScript Object (POJO) to easily extract the values off it.
There are three possible exception objects in SuiteScript: standard exceptions, SuiteScript
errors, and UserEvent errors. All these error objects have the standard name and message
properties. SuiteScript errors have additional properties such as the cause of the error and
the stack trace. UserEvent errors have even more information that related to which record
caused the error and which event the error happened.
Developers can also create their own error handling system by creating custom error objects.
SuiteScript Governance
Another important thing to remember when developing SuiteScript is script governance. So
what is script governance? NetSuite regulates the execution of scripts to make sure that no
accounts would use up the resources of a server, intentional or accidental. The server does
this by allocating a specific number of units to a script type. A user event script, for example,
is assigned 1,000 units and a scheduled script with 10,000 units.
Specific SuiteScript statements "cost" a certain number of units per execution. For example, a
record.load() call to get an employee record costs 5 units and saving changes made to the
record would cost 10 units.
IMPORTANT | CAUTION
Scripts that run out of units during execution will automatically be stopped by the
system. It is very important that you are aware of this and always compute for the
number of units that your script consumes.
Also consider the amount of records that your script processes. Once the script is
deployed to production, your script would most likely consume more units that you
might have expected.
There are four graphs in the APM to measure the response time, throughput, user event and
workflow execution times, and histogram of the response time.
The Response Time graph displays how much time is spent processing the record on the
client (browser execution time), the network (load time from client to the server), and server
(time spent processing the record). This is useful in determining where the slowdown is
happening. Optimizing the script might not help if the slowdown is caused by a slow
computer or a slow internet connection.
The Throughput graph displays the number of record instances and the number of users over
a period of time. This allows you to determine how many records are accessed by how many
people. Start optimizing the high volume records to provide the most impact.
The User Event and Workflow graph shows the time it takes to execute user event scripts
and server workflows on a record.
Lastly, the Histogram shows the record instances grouped by response time. This helps
developers know if the high response time was only caused by an anomaly.
SuiteBundler
SuiteBundler is a tool used to transfer customizations from one account to another. It can be
used to package up a variety of customizations including scripts, custom fields, custom
records, searches, etc.
SuiteBundler is particularly useful for people who need to create customizations for their
own customers or transfer customizations from their sandbox account to their production
account.
BEST PRACTICES
The SuiteBundler tool is a very effective tool for SuiteApp creation and distribution.
Developers can add a Terms of Service page that users must agree on before
installing a bundle. Documentation can also be attached to the bundle so admin and
users would know how to use the bundle they installed. Customizations packed up in
a bundle can also be locked so to hide the implementation code, protecting valuable
intellectual property.
There's more to the SuiteBundler tool than just packaging up customizations so it's
highly recommended that you look into and practice using the tool more.
IMPORTANT
When creating SuiteScripts, or any application for that matter, it's never a good idea
to hardcode values that can potentially change. If there's a way to get values, like
internal ids, dynamically then it should be done.
Another alternative is to use the script id instead of the internal id as the script id will
not change unless it is explicitly changed by an admin.
Note: This course assumes that the learner is already familiar with JavaScript
programming and will not go into the intricacies of the language. The guide is
only covers a high-level overview of the syntaxes.
JavaScript Objects
One thing you have to understand in JavaScript is that everything are objects. An object is an
object, an array is an object, even a function is an object. While you wouldn’t need to worry
about that in this course, it’s a good thing to keep that concept in mind whenever you’re
developing.
With SuiteScript 2.0, you’ll be using anonymous object for several things so it’s important
that you know how to create objects. The quick way to create an object is:
var object = {
property1 : value1,
property2 : value2
// and so on
};
define(function () {
return {
entryPoint : function (context) {
// Do something
}
}
});
When using the define statement, make sure to use the right name based on the entry point
that you want to trigger. If a wrong entry point name is used, it will be not be triggered or
may cause errors in some cases.
JavaScript is a loosely-typed language. This means that variables created in your script is not
given a type. For people coming from typed languages like C# or Java, this might be confusing
since you can’t quickly determine the type of a variable. Some developers deal with this using
the Hungarian Notation. This naming convention prefixes the data type before the variable
name. Here are a few examples:
⚫ intTotal (integer)
⚫ stName (string)
⚫ bFlagged (boolean)
⚫ recSalesOrder (record object)
The JavaScript development community is divided regarding this issue with most favoring the
regular variable naming convention. Whatever convention you use though; you need to make
sure that you stick to it so that you maintain consistency. Inconsistent naming conventions
tend to make the code harder to read and therefore harder to maintain.
Client Notifications
There are two statements you can use to display a message to the user in the client side, the
alert and confirm statements.
alert()
The alert() statement is used to display a simple message on the screen. This is useful if you
want to display information on the screen that the user doesn’t need to take action on.
alert('message');
confirm()
The confirm() statement is also displays information on the screen, similar to the alert()
statement, but it also asks the user to take an action.
These messages display two button, Ok and Cancel. Clicking Ok will return a true and a Cancel
will return false. This is perfect for instances where you want to ask your users if they want to
continue.
if (!someValue) {
// do something if someValue is falsey
}
Take note of the ! (not operator) in the first example. Without this, the if statement will
execute if the value is not falsey; which is fine if that’s what you need.
For Loop
The for loop syntax in JavaScript is practically the same as other object oriented programming
languages such as Java or C#.
The int variable represents your counter and the array.length tells the loop how many
times it would repeat.
The N\record is the path to the module that you want to load and the record parameter is
that module object that you’ll be using in your script. To load another module, just add
another module path and another parameter to hold your module object. So to add the email
module, you’ll:
Note: The order of variables should correspond to the order of module paths that
you’ve added. If you’ve loaded the record module path first, your variable for the
record module object should be declared first and so on.
Module Annotations
define(['N/record'],
/**
* @param {record} record
*/
function(record) {
The first value defines the name of the module that you want to load without the 'N/'. To
load the N/email module, it will be {email}. The next value refers to the name you're using to
store the module. Typically, module variables use the same name as the module.
Search Expressions
Search expressions allow developers to quickly define search filters and columns when
creating a script search. To use search expressions for search filters, use: