Вызов функций во встроенном языке 1С 8.3: от обычного вызова до кода из строки

· 9 мин

Содержание

Чтобы выполнить функцию в 1С, её вызывают по имени со скобками и присваивают результат переменной: Итог = СуммаСНДС(1000, 20);. Если имя функции известно только во время работы программы, его собирают в строку и передают в Вычислить() — метод вернёт значение. Процедуры и несколько операторов подряд выполняют через Выполнить(), но она ничего не возвращает.

Дальше всё упирается в контекст: где выполняется код (клиент или сервер), откуда взята функция (модуль формы, общий модуль, внешняя обработка) и под какими правами. Примеры написаны для платформы 8.3 и управляемых форм, они одинаково работают в типовых и самописных конфигурациях. Имя модуля МойМодульСервер условное — подставьте свой общий модуль.

Прямой вызов и вызов функции по имени из строки

Обычный вызов — самый быстрый и надёжный. Компилятор проверяет имя ещё до запуска, ошибка в названии видна сразу при синтаксическом контроле.

// Общий модуль МойМодульСервер (флаги: Сервер)
Функция СуммаСНДС(Сумма, Ставка) Экспорт
    Возврат Сумма + Окр(Сумма * Ставка / 100, 2);
КонецФункции

// Вызов из другого серверного кода
Итог = МойМодульСервер.СуммаСНДС(1000, 20); // 1200

Что нужно для вызова из другого модуля:

  • 🔑 ключевое слово Экспорт после списка параметров;
  • 📦 имя модуля перед точкой. Исключение — общий модуль с флагом Глобальный: его экспортные функции вызывают без префикса;
  • 🧩 у объекта (документа, справочника) функцию модуля объекта вызывают через сам объект: ДокОбъект = Ссылка.ПолучитьОбъект(); ДокОбъект.ПроверитьЗаполнениеСумм();.

Вычислить: выражение из строки с возвратом значения

Вычислить(Строка) разбирает строку как выражение и возвращает его значение. Внутри строки видны локальные переменные и параметры того места, откуда метод вызван.

ИмяФункции = "МойМодульСервер.СуммаСНДС";
Сумма = 1000;
Ставка = 20;
Итог = Вычислить(ИмяФункции + "(Сумма, Ставка)"); // 1200

Так строят настраиваемые алгоритмы: имя функции хранится в справочнике или константе, код его подставляет.

Выполнить: операторы из строки

Выполнить(Строка) запускает один или несколько операторов. Значения она не возвращает, зато может присвоить его локальной переменной.

Итог = 0;
Выполнить("Итог = МойМодульСервер.СуммаСНДС(Сумма, Ставка);");
СпособЧто возвращаетКогда братьРиск
Прямой вызовЗначение функцииИмя известно заранееМинимальный
Вычислить()Значение выраженияИмя функции в данныхОшибку видно только при запуске
Выполнить()НичегоНужно вызвать процедуру или блок кодаСтрока может содержать любой код

В конфигурациях на БСП есть ОбщегоНазначения.ВыполнитьМетодКонфигурации(ИмяМетода, Параметры). Он проверяет, что строка — именно имя метода, а не произвольный код. Если имя приходит из данных, берите его вместо голой Выполнить().

Как выполнить серверную функцию из клиентского кода

В управляемой форме код делится директивами компиляции. Обработчик кнопки работает на клиенте. Запрос к базе, запись объекта, обращение к регистрам — только на сервере.

&НаКлиенте
Процедура Рассчитать(Команда)
    Объект.СуммаИтого = РассчитатьНаСервере(Объект.Сумма);
КонецПроцедуры

&НаСервереБезКонтекста
Функция РассчитатьНаСервере(Сумма)
    Возврат МойМодульСервер.СуммаСНДС(Сумма, 20);
КонецФункции

Какую директиву выбрать:

ДирективаЧто уходит на серверКогда использовать
&НаСервереВсе данные формыНужно менять реквизиты формы на сервере
&НаСервереБезКонтекстаТолько параметрыРасчёт по переданным значениям
Общий модуль с флагами Сервер + Вызов сервераТолько параметрыФункция нужна многим формам

Пара правил, о которые спотыкаются чаще всего:

  1. Клиентский код не видит общий модуль без флага Вызов сервера. Функции такого модуля вызывают из серверной процедуры формы.
  2. Параметры уходят на сервер копией. Изменение параметра внутри серверной функции на клиенте не отразится — возвращайте результат через Возврат или структуру.
  3. На сервер передаются только сериализуемые значения: примитивы, ссылки, структуры, массивы, таблицы значений через данные формы. Объект формы или элемент формы туда не передать.

Отложенный, фоновый и асинхронный запуск

Иногда функцию нужно выполнить не прямо сейчас. Для этого в платформе три механизма.

Через паузу на клиенте

ПодключитьОбработчикОжидания("ОбновитьСтатус", 2, Истина) вызовет клиентскую процедуру формы ОбновитьСтатус через 2 секунды один раз. Третий параметр Ложь — повторять каждые 2 секунды, пока не отключите методом ОтключитьОбработчикОжидания. Процедура должна быть без параметров.

В фоновом задании на сервере

Долгий расчёт не должен держать интерфейс. Его запускают фоном:

Параметры = Новый Массив;
Параметры.Добавить(НачалоМесяца(ТекущаяДатаСеанса()));
Задание = ФоновыеЗадания.Выполнить("МойМодульСервер.ПересчитатьОстатки",
    Параметры, , "Пересчёт остатков");

Метод должен быть экспортным, лежать в серверном общем модуле и вызываться из серверного кода. Фоновое задание не возвращает значение напрямую: результат кладут во временное хранилище. В БСП это уже обёрнуто в модуль ДлительныеОперации — функции ВыполнитьФункцию и ВыполнитьПроцедуру сами создают задание и хранилище.

Ход заданий видно в режиме «Предприятие»: в конфигурациях на БСП — Администрирование → Обслуживание → Регламентные и фоновые задания.

Асинхронно с ожиданием

С платформы 8.3.18 функции можно объявлять с Асинх и ждать результата оператором Ждать. Так удобно писать диалоги без обработчиков оповещения:

&НаКлиенте
Асинх Процедура Очистить(Команда)
    Ответ = Ждать ВопросАсинх("Очистить табличную часть?", РежимДиалогаВопрос.ДаНет);
    Если Ответ = КодВозвратаДиалога.Да Тогда
        Объект.Товары.Очистить();
    КонецЕсли;
КонецПроцедуры

Выполнение функции из внешней обработки

Внешняя обработка (.epf) — способ выполнить свой код в чужой базе без изменения конфигурации. Функцию в модуле объекта обработки объявляют с Экспорт, потом подключают файл на сервере.

Порядок такой:

  1. На клиенте поместите файл во временное хранилище (например, методом НачатьПомещениеФайлаНаСервер) и получите адрес.
  2. На сервере подключите обработку и создайте объект.
  3. Вызовите экспортную функцию объекта.
&НаСервере
Функция ВыполнитьИзВнешней(АдресВХранилище)
    ИмяОбработки = ВнешниеОбработки.Подключить(АдресВХранилище, , Истина);
    Обработка = ВнешниеОбработки.Создать(ИмяОбработки);
    Возврат Обработка.РассчитатьИтог(1000);
КонецФункции

Третий параметр Истина включает безопасный режим. В нём обработка не получит доступа к файлам сервера, COM-объектам и привилегированному режиму. Выключайте его только для собственных проверенных файлов.

Если обработку нужно запускать регулярно, удобнее зарегистрировать её в базе. В БП 3.0, ЗУП 3.1 и УТ 11 это делается в Администрирование → Печатные формы, отчеты и обработки → Дополнительные отчеты и обработки. Регистрация позволяет назначить обработку разделам, дать права отдельным пользователям и запускать по расписанию.

Внешняя обработка выполняет код с правами того, кто её запустил. Не открывайте .epf из писем и чатов в рабочей базе, сначала проверьте её на копии.

Ошибки при выполнении функции: перехват и отладка

Код из строки компилируется в момент вызова. Опечатка в имени функции не поймается синтаксическим контролем — программа упадёт у пользователя. Поэтому Вычислить() и Выполнить() почти всегда оборачивают в попытку:

Попытка
    Итог = Вычислить(ИмяФункции + "(Сумма, Ставка)");
Исключение
    Инфо = ИнформацияОбОшибке();
    ЗаписьЖурналаРегистрации("Расчёт.Формула", УровеньЖурналаРегистрации.Ошибка, , ,
        ПодробноеПредставлениеОшибки(Инфо));
    ВызватьИсключение КраткоеПредставлениеОшибки(Инфо);
КонецПопытки;

Логика простая: подробности с номером строки и стеком — в журнал регистрации, пользователю — короткий текст. Пустой блок Исключение без записи ошибки прячет проблему, так не делайте.

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

Как посмотреть, что реально выполняется

  • 🐞 Поставьте точку останова клавишей F9 и запустите отладку из конфигуратора (Отладка → Начать отладку).
  • 🔍 На остановке выделите выражение и нажмите Shift+F9 — окно «Вычислить выражение» покажет значение и даже вызовет функцию с вашими параметрами.
  • 🖥 Для серверного кода в клиент-серверной базе отладку нужно разрешить на сервере 1С: агент запускают с ключом -debug. Перезапуск службы обрывает сеансы пользователей — делайте это вне рабочего времени.

Скорость: сколько стоит каждый вызов

Сама функция обычно выполняется быстро. Время съедают обвязка и количество вызовов.

Что замедляетПочемуЧто сделать
Вызов сервера в цикле на клиентеКаждый вызов — отдельный обмен по сетиСобрать данные в массив и вызвать сервер один раз
&НаСервере вместо &НаСервереБезКонтекстаНа сервер и обратно гоняются все данные формыПередавать только нужные значения
Вычислить() в цикле на 10 000 строкСтрока компилируется заново на каждой итерацииВычислить имя один раз, вызывать напрямую или через Соответствие готовых результатов
Повторное чтение неизменных данныхОдин и тот же запрос на каждый вызовОбщий модуль с повторным использованием «На время сеанса» или «На время вызова»

Пример: форма проверяет цены 200 строк, и для каждой вызывает серверную функцию. При 30 мс на обмен это 6 секунд ожидания. Одна функция, получившая массив из 200 товаров, вернёт ответ за один обмен.

Где искать узкие места: запустите отладку и включите Отладка → Замер производительности. Замер покажет, какие строки модуля съели время и сколько раз они выполнялись.

Модуль с повторным использованием возвращаемых значений не должен отдавать изменяемые объекты. Если функция вернула массив, а вызывающий код его изменил, изменённый массив получат и следующие вызовы из кеша.

Безопасный режим и права при выполнении функций

Строка в Выполнить() — это любой код. Если текст пришёл от пользователя или из данных, он может удалить документы, прочитать файлы сервера или запустить внешнюю программу. Защита строится в три слоя.

1. Безопасный режим

УстановитьБезопасныйРежим(Истина);
Результат = Вычислить(ТекстФормулы);

В безопасном режиме код не может включить привилегированный режим, работать с файлами, COM-объектами и внешними компонентами. Режим действует до выхода из текущей процедуры, поэтому формулу удобно вычислять в отдельной маленькой функции. В БСП для этого есть готовые ОбщегоНазначения.ВычислитьВБезопасномРежиме и ОбщегоНазначения.ВыполнитьВБезопасномРежиме.

2. Права пользователя

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

Когда нужно прочитать данные в обход прав (например, служебную настройку), используют УстановитьПривилегированныйРежим(Истина) только на сервере или общий модуль с флагом Привилегированный. Держите такой код коротким и не принимайте в нём строки для Выполнить().

3. Профили безопасности кластера

В клиент-серверном варианте администратор может назначить базе профиль безопасности в консоли кластера. Профиль ограничивает доступ к файловой системе, COM, внешним компонентам и интернет-ресурсам для всей базы, а не для одной функции.

Перед тем как выполнять код из строки
Выполнено: 0 / 5

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

Можно ли вызвать процедуру через Вычислить?
Нет. Вычислить работает только с выражениями, которые возвращают значение. Процедуру вызывают через Выполнить или прямым вызовом.
Как вызвать функцию другой открытой формы?
Получите форму методом ПолучитьФорму или сохраните ссылку при открытии, затем вызовите её экспортную клиентскую процедуру: Форма.ОбновитьДанные(). Функция в модуле формы должна быть объявлена с Экспорт.
Почему функцию общего модуля не видно при внешнем соединении или в HTTP-сервисе?
У общего модуля должен стоять нужный флаг контекста: для внешнего соединения — «Внешнее соединение», для серверного кода HTTP-сервиса достаточно флага «Сервер».
Можно ли передать в серверную функцию параметр по ссылке и получить изменения обратно?
Нет, при вызове с клиента на сервер параметры копируются. Возвращайте изменения результатом функции, например структурой с несколькими полями.
Работает ли Выполнить внутри функции общего модуля с повторным использованием?
Технически да, но результат закешируется по значениям параметров. Если внутри строки читаются меняющиеся данные, кеш вернёт устаревший ответ.