JavaScript из кода 1С:Предприятия: ScriptControl, Node.js и поле HTML

· 10 мин

Содержание

Встроенный язык 1С не умеет исполнять JavaScript сам: метод Выполнить() понимает только код на языке 1С. Запустить JavaScript в 1С:Предприятии можно четырьмя путями: через COM-объект MSScriptControl.ScriptControl (движок JScript в Windows), через внешний скрипт cscript или node, запущенный из 1С, через поле HTML-документа на управляемой форме и через HTTP-запрос к отдельному сервису на Node.js.

Выбор зависит от того, где выполняется код (клиент или сервер), какая разрядность у процесса 1С и какой JavaScript нужен: старый JScript уровня ES3 или современный с let, стрелочными функциями и npm-пакетами. Ниже по каждому способу рабочий пример и ограничения, о которые обычно спотыкаются.

Как 1С взаимодействует с JavaScript: четыре схемы

Все способы делятся на две группы. В первой JavaScript исполняется внутри процесса 1С или рядом с ним на той же машине. Во второй — в отдельном процессе, с которым 1С обменивается данными.

  • 🧩 ScriptControl — JScript выполняется прямо в процессе 1С через COM. Быстро, без файлов, но только Windows и только 32-битный процесс.
  • 📄 Внешний скрипт — 1С пишет данные во временный файл или командную строку, запускает cscript.exe или node.exe и читает результат. Работает с любым JavaScript, но каждый вызов — это новый процесс.
  • 🖥️ Поле HTML-документа — JavaScript живёт в странице, встроенной в форму. Нужен для интерфейса: графики, карты, редакторы. Исполняется только на клиенте.
  • 🌐 HTTP-сервис на Node.js — постоянно запущенный сервер принимает JSON от 1С и отвечает JSON. Самая надёжная схема для серверного кода и тяжёлых библиотек.

COM-объекты доступны только там, где 1С работает под Windows: толстый и тонкий клиент, сервер 1С на Windows, внешнее соединение. В веб-клиенте и на сервере под Linux COM нет, там остаются поле HTML и HTTP.

Запуск JavaScript через COM-объект ScriptControl

MSScriptControl.ScriptControl — компонент Windows (файл msscript.ocx), который исполняет VBScript и JScript. Из 1С он создаётся как обычный COM-объект.

Скрипт = Новый COMОбъект("MSScriptControl.ScriptControl");
Скрипт.Language = "JScript";
Скрипт.AllowUI = Ложь;      // запрет окон alert и диалогов
Скрипт.Timeout = 5000;      // лимит выполнения, мс

Скрипт.AddCode("function add(a, b) { return a + b; }");

Сумма = Скрипт.Run("add", 2, 3);            // 5
Степень = Скрипт.Eval("Math.pow(2, 10)");   // 1024

Три метода закрывают почти все задачи:

МетодЧто делаетКогда использовать
AddCode(Текст)Загружает функции и переменные в контекстОдин раз в начале, затем вызывать функции
Run(Имя, Пар1, Пар2…)Вызывает функцию по имени с параметрамиОсновной способ передать данные из 1С
Eval(Выражение)Вычисляет выражение и возвращает значениеКороткие формулы, проверки
ExecuteStatement(Текст)Выполняет оператор без возврата значенияИзменение глобальных переменных скрипта

Ограничения JScript

Движок ScriptControl — это JScript, близкий к стандарту ES3. Код из современных примеров в нём не заработает:

  • ❌ нет let, const, стрелочных функций, шаблонных строк, классов;
  • ❌ нет встроенного объекта JSON — сериализацию придётся писать вручную или подключать готовую реализацию через AddCode;
  • ❌ нет require и npm-модулей;
  • ✅ есть Math, RegExp, Date, строковые и массивные методы ES3.

Простые типы (Число, Строка, Булево) передаются в обе стороны без преобразований. Массив JavaScript в 1С приходит как COM-объект, а не как Массив. Проще всего вернуть из функции строку с разделителями и разобрать её через СтрРазделить().

Главная ловушка: разрядность

msscript.ocx существует только в 32-битной версии. В 64-битном клиенте или на 64-битном сервере 1С конструктор падает с ошибкой:

Ошибка при вызове конструктора (COMОбъект): -2147221005(0x800401F3): Недопустимая строка с указанием класса

Варианты решения:

  1. Запускать код в 32-битном клиенте 1С, если он выполняется на клиенте (&НаКлиенте).
  2. На сервере перейти на внешний скрипт или HTTP-сервис из следующих разделов.
  3. Использовать сторонние 64-битные реализации ScriptControl — это внешний компонент, его нужно регистрировать на каждой машине и проверять у себя.

ScriptControl исполняет код с правами процесса 1С. Строку, которую ввёл пользователь, нельзя склеивать с текстом для AddCode или Eval: это прямой путь к выполнению чужого кода. Передавайте данные только параметрами Run.

Вызов внешних JS-скриптов через WScript.Shell и Node.js

Если нужен современный JavaScript или код уже лежит в файле, 1С запускает его как внешнюю программу. Под Windows это делают через COM-объект WScript.Shell.

Пример: скрипт Node.js с результатом в файле

Скрипт C:\scripts\calc.js читает входной JSON, считает и пишет ответ:

// node calc.js вход.json выход.json
const fs = require('fs');
const [, , inFile, outFile] = process.argv;

const data = JSON.parse(fs.readFileSync(inFile, 'utf8'));
const result = { total: data.items.reduce((s, x) => s + x.price * x.qty, 0) };

fs.writeFileSync(outFile, JSON.stringify(result), 'utf8');
process.exit(0);

Код 1С готовит файлы, запускает Node.js, ждёт завершения и читает ответ:

ФайлВход = ПолучитьИмяВременногоФайла("json");
ФайлВыход = ПолучитьИмяВременногоФайла("json");

Позиция = Новый Структура("price, qty", 150, 4);
Позиции = Новый Массив;
Позиции.Добавить(Позиция);

Запись = Новый ЗаписьJSON;
Запись.ОткрытьФайл(ФайлВход, "UTF-8", Ложь);
ЗаписатьJSON(Запись, Новый Структура("items", Позиции));
Запись.Закрыть();

Команда = СтрШаблон("node ""C:\scripts\calc.js"" ""%1"" ""%2""", ФайлВход, ФайлВыход);
Шелл = Новый COMОбъект("WScript.Shell");
КодВозврата = Шелл.Run(Команда, 0, Истина);   // 0 — без окна, Истина — ждать

Если КодВозврата = 0 Тогда
    Чтение = Новый ЧтениеJSON;
    Чтение.ОткрытьФайл(ФайлВыход, "UTF-8");
    Ответ = ПрочитатьJSON(Чтение);
    Чтение.Закрыть();
    Сообщить(Ответ.total);   // 600
КонецЕсли;

УдалитьФайлы(ФайлВход);
УдалитьФайлы(ФайлВыход);

Третий параметр Ложь в ОткрытьФайл у ЗаписьJSON отключает BOM. Без этого JSON.parse в Node.js споткнётся о невидимый символ в начале файла.

Почему через файлы, а не через вывод консоли

У WScript.Shell есть метод Exec, который даёт доступ к StdOut. У него два недостатка:

  • он всегда показывает окно консоли, скрыть его нельзя;
  • вывод читается в OEM-кодировке Windows, и кириллица из Node.js превращается в набор символов.

Файлы в UTF-8 снимают обе проблемы. Для чистого JScript без Node.js работает та же схема, только команда другая: cscript //nologo //E:jscript "C:\scripts\calc.js".

Тот же запуск без COM делает метод платформы ЗапуститьПриложение(Команда, Каталог, ДождатьсяЗавершения, КодВозврата). Он не зависит от WScript.Shell, но окно консоли при запуске может мелькнуть.

Пропишите в команде полный путь к node.exe, если служба сервера 1С работает под отдельной учётной записью: переменная PATH у неё может не содержать каталог Node.js. Скрипт и временный каталог должны быть доступны этой учётной записи.

JavaScript в поле HTML-документа на форме

Когда JavaScript нужен для интерфейса — нарисовать график, показать карту, подключить редактор — его кладут в HTML-страницу и выводят на форме. На управляемой форме это реквизит типа Строка и элемент с видом «Поле HTML-документа».

Страницу с функцией формируют в коде и присваивают реквизиту:

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ТекстHTML = "<html><head><script>
    |function showTotal(sum) {
    |  document.getElementById('out').innerText = 'Итого: ' + sum;
    |}
    |</script></head><body><div id='out'></div></body></html>";
КонецПроцедуры

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

&НаКлиенте
Процедура ТекстHTMLДокументСформирован(Элемент)
    Элементы.ТекстHTML.Документ.defaultView.showTotal(600);
КонецПроцедуры

Свойство Документ возвращает DOM страницы, defaultView — её окно, через него доступны глобальные функции JavaScript. Обратная связь из JavaScript в 1С идёт через событие ПриНажатии: скрипт программно «кликает» по скрытому элементу, а в 1С параметр ДанныеСобытия показывает, какой элемент сработал.

Движок поля HTML зависит от версии платформы и операционной системы: это может быть встроенный WebKit или компонент Internet Explorer. Проверяйте скрипт на той платформе, где работают пользователи. В веб-клиенте страница исполняется браузером пользователя.

Сравнение методов интеграции 1С и JavaScript

СпособГде работаетВерсия JSСкорость вызоваГлавный риск
ScriptControlWindows, 32-битный процессJScript (ES3)Высокая, без запуска процессовНет 64-битной версии
WScript.Shell + nodeWindows, клиент и серверЛюбая, npmНизкая: новый процесс на вызовПрава учётной записи службы
Поле HTML-документаТолько клиент, включая вебЗависит от движкаВысокаяРазное поведение движков
HTTP к Node.jsЛюбая ОС, клиент и серверЛюбая, npmСредняя, процесс уже запущенНужно держать сервис запущенным
Какая у вас задача?

HTTP-обмен 1С с сервисом на Node.js

Для серверного кода это самая устойчивая схема: Node.js запущен постоянно, 1С отправляет POST с JSON и получает ответ. Работает одинаково на Windows и Linux, не зависит от COM и разрядности.

Минимальный сервис без сторонних пакетов:

const http = require('http');

http.createServer((req, res) => {
  if (req.method !== 'POST' || req.url !== '/calc') {
    res.writeHead(404);
    return res.end();
  }
  let body = '';
  req.on('data', chunk => { body += chunk; });
  req.on('end', () => {
    try {
      const data = JSON.parse(body);
      const total = data.items.reduce((s, x) => s + x.price * x.qty, 0);
      res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
      res.end(JSON.stringify({ total }));
    } catch (e) {
      res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
      res.end(JSON.stringify({ error: e.message }));
    }
  });
}).listen(3000, '127.0.0.1');

Вызов из 1С (можно на сервере):

Позиции = Новый Массив;
Позиции.Добавить(Новый Структура("price, qty", 150, 4));

Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Новый Структура("items", Позиции));
ТекстЗапроса = Запись.Закрыть();

Соединение = Новый HTTPСоединение("127.0.0.1", 3000, , , , 10);   // таймаут 10 с
Запрос = Новый HTTPЗапрос("/calc");
Запрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Запрос.УстановитьТелоИзСтроки(ТекстЗапроса, КодировкаТекста.UTF8,
    ИспользованиеByteOrderMark.НеИспользовать);

Ответ = Соединение.ОтправитьДляОбработки(Запрос);

Если Ответ.КодСостояния = 200 Тогда
    Чтение = Новый ЧтениеJSON;
    Чтение.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
    Результат = ПрочитатьJSON(Чтение);
    Чтение.Закрыть();
    Сообщить(Результат.total);   // 150 × 4 = 600
Иначе
    ВызватьИсключение СтрШаблон("Сервис вернул код %1: %2",
        Ответ.КодСостояния, Ответ.ПолучитьТелоКакСтроку());
КонецЕсли;

Чтобы сервис не падал вместе с консолью, его запускают как службу: в Windows через NSSM или аналог, в Linux через systemd. Если Node.js стоит на другой машине, добавьте HTTPS (ЗащищенноеСоединениеOpenSSL в параметрах HTTPСоединение) и проверку токена в заголовке.

Если обмен идёт в обратную сторону — Node.js сам обращается к 1С, — в конфигурации публикуют HTTP-сервис или используют стандартный OData-интерфейс. Это отдельная настройка публикации базы на веб-сервере.

Ошибки запуска и защита данных

Диагностика: выберите симптом

Любой вызов внешнего кода оборачивайте в Попытка … Исключение и пишите ОписаниеОшибки() в журнал регистрации. Пользователь должен видеть понятное сообщение, а не текст COM-исключения.

Правила безопасности

  • 🔒 Не собирайте командную строку и код JavaScript из пользовательского ввода. Данные — только через параметры функции, файл или тело запроса.
  • 🔒 Сервис Node.js на той же машине слушает 127.0.0.1, а не все интерфейсы. Снаружи он доступен только через HTTPS и с токеном.
  • 🔒 Ставьте таймауты везде: Timeout у ScriptControl, последний параметр HTTPСоединение. Зависший скрипт не должен держать сеанс 1С.
  • 🔒 Удаляйте временные файлы сразу после чтения: в них могут оказаться персональные данные или суммы.
  • 🔒 Во внешних обработках, подключённых в безопасном режиме, создание COM-объектов и запуск программ запрещены. Если в базе включены профили безопасности, нужное разрешение выдаёт администратор.

Частые вопросы

Можно ли выполнить JavaScript методом Выполнить() в 1С?
Нет. Выполнить() и Вычислить() работают только с кодом на встроенном языке 1С. Для JavaScript нужен внешний движок: ScriptControl, Node.js или браузерный движок поля HTML.
Работает ли ScriptControl на сервере 1С под Linux?
Нет. Это компонент Windows, а COM на Linux отсутствует. На Linux используйте Node.js: запуск скрипта или HTTP-сервис.
Можно ли подключить npm-пакет к коду 1С напрямую?
Нет, только через Node.js. Пакет устанавливают в папку скрипта или сервиса, а 1С вызывает этот скрипт либо отправляет запрос сервису.
Чем JScript в ScriptControl отличается от JavaScript в браузере?
Это старая реализация уровня ES3: без let, const, стрелочных функций, классов и объекта JSON. Код из современных примеров приходится переписывать на var и обычные функции.
Как вернуть из JavaScript в 1С несколько значений?
Надёжнее всего — строкой JSON. В 1С её разбирают через ЧтениеJSON и ПрочитатьJSON() в структуру. Для ScriptControl без встроенного JSON подойдёт строка с разделителем и СтрРазделить().