Введение в SuiteScript и архитектуру NetSuite

Добро пожаловать в мир SuiteScript! SuiteScript — это мощный набор инструментов разработки, основанный на языке JavaScript, который позволяет расширять стандартный функционал системы NetSuite. По сути, SuiteScript превращает NetSuite из простого SaaS-решения в полноценную платформу для разработки корпоративного ПО. С его помощью вы можете автоматизировать бизнес-процессы, создавать новые типы записей, изменять поведение интерфейса и интегрировать систему со сторонними сервисами.

Архитектура NetSuite построена на многоарендной (multi-tenant) облачной модели. Это означает, что многие пользователи делят общие вычислительные ресурсы, но их данные строго изолированы. SuiteScript работает внутри этой инфраструктуры, предоставляя доступ к API NetSuite. Важно понимать, что код исполняется на стороне сервера (Server-side) или в браузере пользователя (Client-side), в зависимости от типа скрипта. Основной целью разработки в NetSuite является минимизация ручного ввода данных и исключение человеческих ошибок через жесткую логику автоматизации.

Прежде чем приступать к коду, необходимо разобраться в иерархии объектов. В NetSuite всё вращается вокруг «Записей» (Records). Записью может быть Заказ клиента (Sales Order), Счёт (Invoice) или даже профиль сотрудника. Для взаимодействия с этими записями SuiteScript предоставляет модули. В современной версии SuiteScript 2.x используется модульная система, где вы импортируете только те функции, которые вам необходимы, что значительно повышает производительность и читаемость кода.

Существует несколько основных типов скриптов, каждый из которых предназначен для своего сценария. Для наглядности рассмотрим их в следующей таблице:

Тип скрипта Место исполнения Триггер запуска Основная цель
Client Script Браузер События интерфейса (изменение поля) Валидация данных в реальном времени
User Event Script Сервер Сохранение/Загрузка записи Автоматизация после создания записи
Scheduled Script Сервер Расписание или вручную Массовая обработка данных
Map/Reduce Script Сервер Расписание или вручную Обработка огромных массимов данных
Suitelet Сервер URL-запрос (HTTP) Создание собственных страниц/форм

Для того чтобы начать писать на SuiteScript 2.x, вам нужно понимать структуру модуля. Каждый скрипт начинается с определения JSDoc-аннотации, которая помогает среде разработки понимать типы данных, и использования функции define(). Эта функция позволяет объявлять зависимости, которые ваш скрипт будет использовать. Например, модуль N/record используется для работы с данными, а N/log — для вывода отладочной информации в системный лог.

Рассмотрим простой пример User Event скрипта, который автоматически устанавливает значение в текстовое поле при создании записи.


/**
 * @NApiVersion 2.x
 * @NScriptType UserEventScript
 */
define(['N/record', 'N/log'], function(record, log) {
    function beforeSubmit(context) {
        if (context.type === context.UserEventType.CREATE) {
            var newRecord = context.newRecord;
            newRecord.setValue({
                fieldId: 'custbody_welcome_message',
                value: 'Добро пожаловать в NetSuite!'
            });
            log.debug('Success', 'Welcome message set successfully');
        }
    }
    return {
        beforeSubmit: beforeSubmit
    };
});

В данном коде мы сначала импортируем модули N/record и N/log. Функция beforeSubmit срабатывает непосредственно перед тем, как запись будет сохранена в базе данных. Мы проверяем, что запись именно создается (context.UserEventType.CREATE), затем получаем объект новой записи через context.newRecord и с помощью метода setValue записываем строку в поле с идентификатором custbody_welcome_message. В конце мы выводим сообщение в лог для проверки работы.

Данный подход с использованием beforeSubmit был выбран потому, что он позволяет изменять данные до их окончательного сохранения в БД. Если бы мы использовали afterSubmit, нам пришлось бы заново загружать запись, изменять её и сохранять еще раз, что создало бы лишний запрос к базе данных и могло бы вызвать бесконечный цикл рекурсии. Работа с объектом context.newRecord в beforeSubmit является наиболее эффективным способом модификации полей при создании.

Другим важным аспектом является работа с Client Scripts. Они позволяют взаимодействовать с пользователем прямо в браузере. Рассмотрим пример скрипта, который проверяет значение поля при его изменении.


/**
 * @NApiVersion 2.x
 * @NScriptType ClientScript
 */
define(['N/ui/dialog'], function(dialog) {
    function fieldChanged(context) {
        if (context.fieldId === 'custbody_discount_rate') {
            var record = context.currentRecord;
            var value = record.getValue({ fieldId: 'custbody_discount_rate' });
            if (value > 50) {
                dialog.alert({
                    title: 'Внимание!',
                    message: 'Скидка более 50% требует одобрения менеджера.'
                });
            }
        }
    }
    return {
        fieldChanged: fieldChanged
    };
});

В этом примере используется модуль N/ui/dialog для вывода всплывающего окна. Функция fieldChanged срабатывает каждый раз, когда пользователь меняет любое поле в форме. Мы фильтруем события по fieldId, чтобы реагировать только на изменение поля скидки. Если введенное значение превышает 50, пользователю выводится предупреждающее сообщение. Это происходит мгновенно, не дожидаясь нажатия кнопки «Сохранить».

Выбор Client Script в данном случае обусловлен необходимостью мгновенной обратной связи (UX). Если бы мы реализовали эту проверку на сервере (User Event), пользователь узнал бы об ошибке только после попытки сохранить форму и перезагрузки страницы, что крайне неудобно. Использование N/ui/dialog делает интерфейс более интерактивным и снижает вероятность ввода некорректных данных.

Common Mistakes: Новички часто путают context.newRecord (в User Event) и context.currentRecord (в Client Script). Попытка использовать currentRecord на сервере приведет к ошибке исполнения. Еще одна частая ошибка — забывать возвращать объект с именами функций (например, return { beforeSubmit: beforeSubmit };) в конце модуля define. Без этого NetSuite не будет знать, какую функцию вызывать при наступлении события, и скрипт просто не запустится. Также часто забывают проверять context.type, из-за чего логика «создания» срабатывает и при «редактировании», что может затереть важные данные.

Real-World Use Case: В реальных проектах такая архитектура используется для автоматизации цепей поставок. Например, в крупных компаниях при создании заказа (Sales Order) SuiteScript может автоматически проверять остатки на разных складах по всему миру, рассчитывать оптимальный маршрут доставки и автоматически создавать запись о передаче товара (Item Fulfillment), если товар в наличии. Это избавляет операторов от необходимости вручную переключаться между пятью разными вкладками и формами.

Try It: Создайте структуру Client Script, которая отслеживает изменение поля "Количество" (quantity) в строке заказа. Если количество больше 100, скрипт должен вывести в консоль браузера сообщение "Оптовый заказ обнаружен!". Помните о правильном импорте модулей и возврате объекта с функцией.

Knowledge Check

Register to answer these questions interactively and have your exam graded.

  1. На каком языке программирования основан SuiteScript?
    • Java
    • Python
    • JavaScript
    • C#
  2. Какой тип скрипта лучше всего подходит для создания собственных страниц или форм, доступных по URL-запросу?
    • User Event Script
    • Suitelet
    • Client Script
    • Scheduled Script
  3. В чем основное различие между Client Script и User Event Script с точки зрения места исполнения?
    • Client Script выполняется на сервере, User Event — в браузере
    • Оба выполняются на сервере
    • Оба выполняются в браузере
    • Client Script выполняется в браузере, User Event — на сервере
  4. Какая функция используется в SuiteScript 2.x для объявления зависимостей и модулей?
    • require()
    • import()
    • define()
    • init()
  5. Почему для модификации полей при создании записи рекомендуется использовать событие beforeSubmit в User Event скрипте, а не afterSubmit?
    • beforeSubmit работает быстрее в браузере
    • afterSubmit не имеет доступа к объекту записи
    • beforeSubmit позволяет изменить данные до сохранения в БД, избегая лишних запросов и рекурсии
    • afterSubmit работает только с Client Script
  6. Какая из перечисленных ошибок является типичной для новичков при разработке на SuiteScript?
    • Использование JSDoc-аннотаций
    • Попытка использовать context.currentRecord в серверном скрипте
    • Импорт модуля N/log для отладки
    • Использование функции setValue для изменения полей