SuiteScript 2.x
Automations that hold up under volume.
- Map/Reduce and Scheduled
- Suitelets and Client Scripts
- User Events and validations
Rodolfo Guerra · NetSuite Specialist
I have worked with NetSuite every day for 7 years. Here I share practical notes on development, queries and administration: what works, and the mistakes worth avoiding.
I’m Rodolfo Guerra. I started as a NetSuite administrator at a services company, and today I lead the NetSuite product at a multi-subsidiary company.
My work sits between the business and the system. I listen to what a team needs, turn it into configuration or code, and make sure it keeps working as volume grows.
I built this lab to organize what I learn and share it. Every note comes from a real case.
What I work on day to day.
Automations that hold up under volume.
Clear answers from the data.
A tidy, secure system.
Short notes, each based on a real case. Open one to see the details.
A Scheduled Script that processes everything in one run is fine with a few records. When volume grows, the trouble starts.
getInputData, map, reduce and summarize.summarize you review what failed and why.Rule of thumb: Scheduled for short jobs with predictable volume. Map/Reduce for many records or growing volume. When in doubt, start with Map/Reduce.
Before: the same payment was keyed into NetSuite, online banking, a spreadsheet and an email to the vendor. Four places, four chances for error.
Heads-up: not everything comes out of the box. The Electronic Bank Payments SuiteApp gives you your country’s standard templates, batch approval and reversals. These need the Electronic Bank Payments license: custom templates, automatic bill processing, multi-currency, APIs and parallel processing (the last one also requires SuiteCloud Plus).
| Mistake | What happens | How to avoid it |
|---|---|---|
| No External ID | Re-importing duplicates everything | “Add or Update” mode + External ID |
| List values that don’t match | “Cash” ≠ “Cash - 0 days” | Text identical to NetSuite’s |
| Dates in another format | Dates rejected or misread | Your user’s date format |
| Broken accents | “Perú” instead of “Perú” | File and import in UTF-8 |
| Testing with the whole file | 500 records to fix | 5 rows first, then the rest |
Bonus: save the field mapping. The next import takes minutes.
A “small” change in production can break:
It costs one more day. A production error costs several.
An external system sends order A-1042 and NetSuite creates the sales order. The response never arrives because of a network drop. The system retries… and without protection, a second order is created.
The fix is to make retries safe (idempotency). Use the external system’s identifier as the External ID: NetSuite won’t allow two records of the same type with the same External ID.
// RESTlet (SuiteScript 2.1): look up before creating
const existing = query.runSuiteQL({
query: 'SELECT id FROM transaction WHERE externalid = ?',
params: [payload.orderId]
}).asMappedResults()[0];
if (existing) return { id: existing.id, duplicate: true };
const so = record.create({ type: record.Type.SALES_ORDER });
so.setValue({ fieldId: 'externalid', value: payload.orderId });
If the order already exists, you return the same one. The External ID is the second safeguard: even if two requests arrive at once, NetSuite rejects the duplicate.
BUILTIN.DF turns an ID into the text you see on screen: the customer, sales rep or location name.
SELECT
t.tranid AS order_no,
BUILTIN.DF(t.entity) AS customer,
BUILTIN.DF(t.employee) AS sales_rep,
BUILTIN.DF(tl.location) AS location
FROM transaction t
JOIN transactionline tl
ON tl.transaction = t.id
WHERE t.type = 'SalesOrd'
AND tl.mainline = 'T'
AND t.status IN ('E', 'F')
For display, not for filtering. In the WHERE clause always use the ID or code: it’s more efficient and doesn’t depend on how the text is spelled.
If you work with NetSuite and want to discuss any of these notes, message me on LinkedIn.
Go to my LinkedIn ↗