Sign Up

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

Have an account? Sign In

Have an account? Sign In Now

Sign In

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

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

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

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

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

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

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

Sign InSign Up

The NetSuite Pro

The NetSuite Pro Logo The NetSuite Pro Logo

The NetSuite Pro Navigation

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

Mobile menu

Close
Ask A Question
  • Home
  • About Us
  • Tutorials
    • NetSuite Scripting
    • Advanced PDF Templates in NetSuite
  • Blog
  • Contact Us
Home/ NetSuite Scripting/Real-World Script Examples (NetSuite)

Real-World Script Examples (NetSuite)

How to Use These

  • All examples are SuiteScript 2.1.
  • Replace placeholder IDs (saved search IDs, folder IDs, custom field IDs).
  • Test in sandbox; enable debug logging via script parameters when provided.

1) Map/Reduce — Bulk Update Invoices (set custom form + memo)

Use when: thousands of invoices need a field change safely.

/**
 * @NApiVersion 2.1
 * @NScriptType MapReduceScript
 * Bulk update invoice custom form + memo (governance-safe)
 */
define(['N/search','N/record','N/runtime','N/log'], (search, record, runtime, log) => {

  const PARAM_FORM = 'custscript_target_customform'; // integer
  const PARAM_MEMO = 'custscript_memo_text';        // string
  const PARAM_SEARCH = 'custscript_invoice_search'; // saved search id

  const getInputData = () => {
    try {
      const searchId = runtime.getCurrentScript().getParameter(PARAM_SEARCH);
      return search.load({ id: searchId });
    } catch (e) {
      log.error('getInputData', e); throw e;
    }
  };

  const map = ctx => {
    try {
      const row = JSON.parse(ctx.value);
      ctx.write(row.id, { id: row.id }); // group by record id
    } catch (e) { log.error('map', e); }
  };

  const reduce = ctx => {
    const formId = Number(runtime.getCurrentScript().getParameter(PARAM_FORM));
    const memoTxt = String(runtime.getCurrentScript().getParameter(PARAM_MEMO) || '');
    try {
      const invoiceId = ctx.key;
      const inv = record.load({ type: record.Type.INVOICE, id: invoiceId });
      if (formId) inv.setValue({ fieldId:'customform', value: formId });
      if (memoTxt) inv.setValue({ fieldId:'memo', value: memoTxt });
      const id = inv.save({ enableSourcing: false, ignoreMandatoryFields: true });
      log.audit('Updated Invoice', id);
    } catch (e) { log.error(`reduce(${ctx.key})`, e); }
  };

  const summarize = (sum) => {
    log.audit('Summary', { map: sum.mapSummary, reduce: sum.reduceSummary });
  };

  return { getInputData, map, reduce, summarize };
});

Notes

  • Pass parameters at deployment: saved search of target invoices, target custom form ID, memo text.
  • MR auto-resumes; no manual yielding needed.

2) User Event — Validate Lines & Prevent Duplicate Items

Use when: you want to block common data entry mistakes.

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 * Prevent duplicate items & ensure quantity > 0 on Sales Orders
 */
define(['N/error','N/log'], (error, log) => {
  const beforeSubmit = (ctx) => {
    try {
      const rec = ctx.newRecord;
      if (rec.type !== 'salesorder') return;

      const seen = new Set();
      const count = rec.getLineCount({ sublistId:'item' });
      for (let i=0; i<count; i++) {
        const item = rec.getSublistValue({ sublistId:'item', fieldId:'item', line:i });
        const qty  = Number(rec.getSublistValue({ sublistId:'item', fieldId:'quantity', line:i }) || 0);
        if (seen.has(item)) {
          throw error.create({ name:'DUP_ITEM', message:`Duplicate item at line ${i+1}.` });
        }
        if (qty <= 0) {
          throw error.create({ name:'BAD_QTY', message:`Quantity must be > 0 at line ${i+1}.` });
        }
        seen.add(item);
      }
    } catch (e) { log.error('beforeSubmit', e); throw e; }
  };
  return { beforeSubmit };
});

Notes

  • Deploy on Sales Order beforeSubmit.
  • Keep validations lightweight to avoid timeouts.

3) Client Script — “Fill Down” Helper for Sublist Fields

Use when: users repeat the same value across many lines.

/**
 * @NApiVersion 2.1
 * @NScriptType ClientScript
 * Button handler to copy current line's Department to all lines
 */
define(['N/currentRecord','N/ui/dialog'], (currentRecord, dialog) => {
  const copyDepartment = async () => {
    try {
      const rec = currentRecord.get();
      const sublistId = 'item';
      const line = rec.getCurrentSublistIndex({ sublistId });
      const dept = rec.getCurrentSublistValue({ sublistId, fieldId:'department' });
      if (!dept) return dialog.alert({ title:'Info', message:'No department to copy.' });

      const count = rec.getLineCount({ sublistId });
      for (let i=0; i<count; i++) {
        rec.selectLine({ sublistId, line:i });
        rec.setCurrentSublistValue({ sublistId, fieldId:'department', value: dept });
        rec.commitLine({ sublistId });
      }
      await dialog.alert({ title:'Done', message:`Copied to ${count} line(s).` });
    } catch (e) { console.log('copyDepartment', e); }
  };
  return { copyDepartment };
});

Notes

  • Add a custom button via UE afterLoad that calls copyDepartment().
  • Improves UX; zero server calls.

4) Suitelet — Email Statements (Queue Map/Reduce + Status)

Use when: you want a safe, scalable “Send Statements” tool.

/**
 * @NApiVersion 2.1
 * @NScriptType Suitelet
 * Start MR to email statements; show taskId
 */
define(['N/ui/serverWidget','N/task','N/log'], (ui, task, log) => {
  const onRequest = (ctx) => {
    try {
      if (ctx.request.method === 'GET') {
        const f = ui.createForm({ title:'Email Statements' });
        f.addField({ id:'custpage_cust_search', type:'select', label:'Customer Search', source:'search' });
        f.addSubmitButton({ label:'Start' });
        return ctx.response.writePage(f);
      }
      // POST: start MR
      const t = task.create({ taskType: task.TaskType.MAP_REDUCE, scriptId:'customscript_email_stmt_mr', deploymentId:'customdeploy_email_stmt_mr' });
      const taskId = t.submit();
      ctx.response.write(`Started. Task ID: ${taskId}`);
    } catch (e) { log.error('Suitelet', e); ctx.response.write(`Error: ${e.message}`); }
  };
  return { onRequest };
});

Notes

  • MR handles email generation + sending; Suitelet stays fast.
  • Add a tiny status Suitelet if you want the UI to poll progress.

5) RESTlet — Create or Update Customer (Upsert)

Use when: integrating with an external system simply and securely.

/**
 * @NApiVersion 2.1
 * @NScriptType Restlet
 * Upsert Customer by externalId; returns internalId
 */
define(['N/record','N/search','N/log'], (record, search, log) => {
  const post = (body) => {
    try {
      const extId = String(body.externalId || '').trim();
      if (!extId) throw new Error('externalId required');

      const existing = findByExternalId(extId);
      const rec = existing
        ? record.load({ type: record.Type.CUSTOMER, id: existing })
        : record.create({ type: record.Type.CUSTOMER, isDynamic: true });

      if (!existing) rec.setValue({ fieldId:'externalid', value: extId });
      if (body.companyName) rec.setValue({ fieldId:'companyname', value: body.companyName });
      if (body.email)       rec.setValue({ fieldId:'email', value: body.email });

      const id = rec.save({ enableSourcing: true, ignoreMandatoryFields: false });
      return { ok: true, internalId: id };
    } catch (e) { log.error('REST Upsert', e); return { ok:false, message: e.message }; }
  };

  function findByExternalId(externalId){
    const s = search.create({ type:'customer', filters:[['externalidstring','is',externalId]], columns:['internalid'] });
    const r = s.run().getRange({ start:0, end:1 })[0];
    return r && r.getValue('internalid');
  }
  return { post };
});

Notes

  • Secure with token-based auth or OAuth 2.0.
  • Validate inputs; never trust external sources.

6) Scheduled Script — SFTP Inventory Import (JSON → Item)

Use when: nightly feeds update on-hand/price from a 3PL/ERP.

/**
 * @NApiVersion 2.1
 * @NScriptType ScheduledScript
 * Pull JSON from SFTP and update inventory item fields
 */
define(['N/sftp','N/file','N/record','N/runtime','N/log'], (sftp, file, record, runtime, log) => {

  const execute = () => {
    try {
      const conn = sftp.createConnection({
        url: 'sftp.partner.com',
        username: 'ftp_user',
        passwordGuid: 'custsecret_sftp_pwd_guid',
        hostKey: 'AAAAB3NzaC1yc2EAAAADAQABAAABAQ...' // exact host key
      });
      const dir = '/inbound/inventory';
      (conn.list({ path: dir }) || []).forEach(f => {
        if (!f.name.endsWith('.json')) return;
        try {
          const txt = conn.download({ directory: dir, filename: f.name }).getContents();
          const items = JSON.parse(txt);
          items.forEach(updateItemSafe);
          archive(txt, f.name);
        } catch (e) { log.error(`File ${f.name}`, e); }
      });
    } catch (e) { log.error('SFTP execute', e); }
  };

  function updateItemSafe(it){
    try {
      const rec = record.load({ type:'inventoryitem', id: it.internalId });
      if (it.price != null) rec.setValue({ fieldId:'baseprice', value: Number(it.price) });
      if (it.qty   != null) rec.setValue({ fieldId:'custitem_integration_qty', value: Number(it.qty) });
      rec.save({ enableSourcing:false, ignoreMandatoryFields:true });
    } catch (e) { /* log and continue */ }
  }

  function archive(contents, name){
    try {
      file.create({ name:`processed_${name}`, fileType:file.Type.JSON, contents, folder: 12345 }).save();
    } catch (e) {}
  }

  return { execute };
});

Notes

  • Keep per-file batches small; consider MR for very large feeds.
  • Use Secrets Manager for credentials.

7) Custom GL Plug-in — Redirect Revenue by Subsidiary

Use when: accounting wants specific revenue accounts per subsidiary/channel.

/**
 * @NApiVersion 2.x
 * @NScriptType CustomGLPlugin
 * Redirect credit lines based on subsidiary → custom account
 */
define(['N/log'], (log) => {
  function CustomGL() {}
  CustomGL.prototype.generateLines = function(context){
    try {
      const trx = context.transactionRecord;
      const sub = trx.getValue({ fieldId:'subsidiary' });
      context.standardLines.iterator().each(line => {
        if (line.creditAmount > 0 && sub == 2) {
          // neutralize original
          line.setCreditAmount(0);
          // add custom credit
          const l = context.lines.addNewLine();
          l.setAccountId(1234); // target revenue account
          l.setCreditAmount(line.creditAmount);
          l.setMemo('Redirected revenue (Subsidiary 2)');
        }
        return true;
      });
    } catch (e) { log.error('generateLines', e); }
  };
  return CustomGL;
});

Notes

  • Balance debits/credits; test on sample invoices.
  • Avoid double-posting—always neutralize if replacing lines.

8) Utility Module — Safe Logging, Backoff & Lookup

Use when: you want consistency across scripts.

/**
 * @NApiVersion 2.1
 * @NModuleScope Public
 * Utils: logSafe, backoff, lookup
 */
define(['N/search','N/log'], (search, log) => {

  const logSafe = (title, obj) => {
    try { log.debug(title, JSON.stringify(obj).slice(0, 1000)); }
    catch (_) { log.debug(title, '[unserializable]'); }
  };

  async function backoff(fn, tries=5, base=300){
    for (let i=0;i<tries;i++){
      try { return await fn(); }
      catch (e){ if (i===tries-1) throw e; await wait(base * Math.pow(2,i)); }
    }
  }
  const wait = (ms) => new Promise(r => setTimeout(r, ms));

  const lookupIdByText = (type, field, text) => {
    const s = search.create({ type, filters:[[field,'is',text]], columns:['internalid'] });
    const r = s.run().getRange({ start:0, end:1 })[0];
    return r && r.getValue('internalid');
  };

  return { logSafe, backoff, lookupIdByText };
});

Notes

  • Import this small module in your projects to standardize behaviors.

Deployment & Governance Tips (Quick)

  • User Event: keep logic light; offload heavy work to MR.
  • Suitelet: return fast; use JSON endpoints + client polling for long tasks.
  • MR: group by record/document; write in reduce; summarize outcomes.
  • SFTP: verify host key; test with tiny files; handle empty listings.
  • RESTlet: sanitize inputs, enforce auth, return compact JSON, avoid heavy DB loops.
  • Custom GL: neutralize replaced lines; add rich logging in sandbox.
Share
  • Facebook

Sidebar

Ask A Question

Stats

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

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

    • 1 Answer
  • admin

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

    • 1 Answer
  • admin

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

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

Top Members

Rocky

Rocky

  • 1 Question
  • 21 Points
Begginer
admin

admin

  • 5 Questions
  • 2 Points

Trending Tags

clientscript netsuite scripting suitescript

Explore

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

Footer

© 2025 The NetSuite Pro. All Rights Reserved