Вызов функций и процедур во встроенном языке 1С 8.3

· 10 мин

Содержание

Чтобы обратиться к функции в 1С, пишут её имя и аргументы в скобках, а результат присваивают переменной: Сумма = ПосчитатьСумму(Документ, Истина);. Функцию из своего модуля вызывают просто по имени. Функцию из общего модуля — через имя модуля: РаботаСФайлами.ИмяФункции(), и только если она объявлена с ключевым словом Экспорт.

Почти все проблемы с вызовом сводятся к трём вещам: метод не экспортирован, вызван не в том контексте (клиент вместо сервера) или передано не то число параметров. Ниже — правила для платформы 8.3 с управляемыми формами, общие для любых конфигураций: Бухгалтерии, ЗУП, УТ и собственных разработок.

Синтаксис вызова функции и процедуры

Во встроенном языке два вида методов. Функция возвращает значение через Возврат, процедура — нет.

Функция СуммаСНДС(Сумма, Ставка = 20)
	Возврат Сумма + Сумма * Ставка / 100;
КонецФункции

Процедура ВывестиИтог(Сумма)
	Сообщить("Итого: " + Формат(Сумма, "ЧДЦ=2"));
КонецПроцедуры

// Вызовы
Итог = СуммаСНДС(1000);     // 1200
ВывестиИтог(Итог);           // Итого: 1 200,00
СуммаСНДС(500);              // допустимо: результат просто отбрасывается

Правила, которые стоит помнить:

  • 📌 Скобки ставят всегда, даже без параметров: ОбновитьДанные();.
  • 📌 Функцию можно вызвать как процедуру — значение потеряется, ошибки не будет.
  • 📌 Процедуру нельзя использовать в выражении. А = ВывестиИтог(10); не пройдёт проверку модуля.
  • 📌 Если функция дошла до КонецФункции без Возврат, она вернёт Неопределено.
  • 📌 Порядок методов в модуле не важен: процедуру, описанную ниже по тексту, можно вызывать из той, что выше.

Имена регистронезависимы: суммасндс(1000) сработает так же. Но в типовых конфигурациях принят стиль с заглавной буквы каждого слова — держитесь его, чтобы код читался.

Контекст выполнения: почему функция «не видна»

В управляемом приложении код формы работает и на клиенте, и на сервере. Где именно выполнится метод, задаёт директива компиляции над ним. От неё зависит, кто и откуда может этот метод вызвать.

ДирективаГде выполняетсяДоступ к данным формыОткуда вызывают
&НаКлиентеклиентдаиз клиентских методов
&НаСервересерверда, форма передаётся на серверс клиента и с сервера
&НаСервереБезКонтекстасервернетс клиента и с сервера
&НаКлиентеНаСервереБезКонтекстатам, откуда вызвалинетотовсюду

Отсюда типичные ситуации:

  • ❌ Из &НаСервере вызывают метод с директивой &НаКлиенте. С сервера на клиент вызывать нельзя — только наоборот.
  • ❌ В &НаСервереБезКонтекста обращаются к Объект или к реквизитам формы. Их там нет: форма на сервер не передавалась.
  • ❌ На клиенте вызывают общий модуль с флажком только «Сервер». Клиент его не видит.

С общими модулями контекст задают флажки в свойствах модуля в Конфигураторе: «Клиент (управляемое приложение)», «Сервер», «Вызов сервера», «Внешнее соединение», «Глобальный», «Привилегированный».

Чтобы клиент мог вызвать серверный общий модуль, у модуля должны стоять одновременно «Сервер» и «Вызов сервера». Без второго флажка с клиента модуль недоступен, хотя с сервера вызывается нормально.

Если метод нужен и на клиенте, и на сервере (например, форматирование строки), поставьте у общего модуля флажки «Клиент» и «Сервер» без «Вызова сервера». Код скомпилируется в обоих местах, и обращение к серверу не понадобится.

Передача параметров: по ссылке и через Знач

По умолчанию параметр передаётся по ссылке. Если метод присвоит параметру новое значение, оно изменится и у вызывающего кода.

Процедура Увеличить(Число)
	Число = Число + 1;
КонецПроцедуры

А = 5;
Увеличить(А);
// А = 6

Ключевое слово Знач передаёт значение копией:

Процедура Увеличить(Знач Число)
	Число = Число + 1;
КонецПроцедуры

А = 5;
Увеличить(А);
// А = 5

С объектами есть тонкость. Для структуры, массива или таблицы значений Знач копирует только ссылку, а не содержимое. Поэтому Параметр.Вставить("Ключ", 1) внутри метода изменит исходную структуру и при Знач. А вот Параметр = Новый Структура с Знач исходную переменную не тронет.

Что делает метод с параметромБез ЗначС Знач
Присваивает новое значениеменяется у вызывающегоне меняется
Меняет содержимое коллекциименяетсяменяется
Вызов с клиента на серверзначение передаётся обратно на клиентобратно не передаётся

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

Необязательные параметры

Значение по умолчанию делает параметр необязательным: Функция Остаток(Склад, Дата = Неопределено). Пропустить параметр в середине можно пустым местом через запятую:

Результат = Отчет(Организация, , КонецМесяца(ТекущаяДата()));

Необязательные параметры ставят в конец списка. Тогда вызов без хвостовых аргументов выглядит чисто: Остаток(Склад).

Обращение к функциям других модулей: Экспорт и квалификаторы

Метод, который должен быть доступен снаружи модуля, объявляют со словом Экспорт после списка параметров:

Функция ЦенаНоменклатуры(Номенклатура, Дата) Экспорт
	// ...
КонецФункции

Дальше путь обращения зависит от вида модуля.

Где описан методКак вызвать
Общий модульИмяМодуля.ИмяМетода()
Глобальный общий модульИмяМетода() — без имени модуля
Модуль менеджера справочникаСправочники.Номенклатура.ИмяМетода()
Модуль менеджера документаДокументы.РеализацияТоваровУслуг.ИмяМетода()
Модуль объектаСправочникОбъект.ИмяМетода() — у полученного объекта
Модуль формыФорма.ИмяМетода() — у открытой формы, на клиенте

Методы модуля объекта доступны только у объекта, а не у ссылки. На сервере в форме объект получают так:

&НаСервере
Процедура ПересчитатьНаСервере()
	ДокументОбъект = РеквизитФормыВЗначение("Объект");
	ДокументОбъект.ЗаполнитьТабличнуюЧасть();
	ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры

Из другого места объект берут по ссылке: Объект = Ссылка.ПолучитьОбъект();. Это чтение из базы, поэтому делайте его на сервере и не в цикле.

Модуль менеджера и модуль объекта — разные модули. Метод из модуля менеджера через объект не вызвать, и наоборот. При ошибке Метод объекта не обнаружен первым делом проверьте, в каком из двух модулей лежит код.

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

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

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

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

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

Обратный вызов через ОписаниеОповещения

В клиентском коде с немодальными окнами (вопрос, выбор, ввод числа) результат приходит позже, в другую процедуру. Её передают через ОписаниеОповещения:

&НаКлиенте
Процедура Удалить(Команда)
	Доп = Новый Структура("Строка", Элементы.Товары.ТекущаяСтрока);
	Оповещение = Новый ОписаниеОповещения("ПослеВопроса", ЭтотОбъект, Доп);
	ПоказатьВопрос(Оповещение, "Удалить строку?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры

&НаКлиенте
Процедура ПослеВопроса(Ответ, ДополнительныеПараметры) Экспорт
	Если Ответ = КодВозвратаДиалога.Да Тогда
		// удаляем строку из ДополнительныеПараметры.Строка
	КонецЕсли;
КонецПроцедуры

Процедура-обработчик обязательно экспортная, иначе платформа её не найдёт. С платформы 8.3.18 есть и другой вариант — асинхронные методы с ключевыми словами Асинх и Ждать: код пишется сверху вниз без отдельного обработчика.

Рекурсия и что в 1С вместо замыканий

Функция может вызывать сама себя. Классический случай — обход дерева значений или иерархического справочника:

Процедура СобратьЛистья(СтрокиДерева, Результат)
	Для Каждого Строка Из СтрокиДерева Цикл
		Если Строка.Строки.Количество() = 0 Тогда
			Результат.Добавить(Строка.Номенклатура);
		Иначе
			СобратьЛистья(Строка.Строки, Результат);
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры

Листья = Новый Массив;
СобратьЛистья(Дерево.Строки, Листья);

Массив Результат передаётся по ссылке и заполняется на всех уровнях. Возвращать его из функции на каждом шаге не нужно.

Для рекурсии держите в голове два правила:

  1. Условие выхода должно срабатывать гарантированно. Если справочник по ошибке ссылается сам на себя через родителя, обход уйдёт в бесконечный вызов и упадёт с переполнением стека.
  2. Для иерархии справочника проще запрос с ИЕРАРХИЯ или выборка ВыбратьИерархически, чем рекурсивные обращения к базе на каждом уровне.

Замыканий и анонимных функций в языке 1С нет. Передать «функцию со своими данными» можно двумя способами: ОписаниеОповещения с параметром ДополнительныеПараметры (на клиенте) или пара «имя метода строкой + структура параметров» для ВыполнитьМетодКонфигурации. Состояние между вызовами храните в реквизитах формы или в структуре, а не в переменных модуля: на сервере переменные модуля формы между вызовами не сохраняются.

Ошибки при обращении к функциям и что с ними делать

Тексты ниже выдаёт платформа при проверке модуля или во время выполнения. По ним обычно сразу видно, куда смотреть.

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

Отдельно про доработки типовых конфигураций. Если вы вызываете из своего кода типовую функцию, после обновления её параметры могут поменяться, и проверка модуля упадёт. Перед обновлением ищите такие вызовы через Правка → Найти в Конфигураторе по имени метода.

Как ускорить вызовы: серверные обращения и кеш

Медленный код в 1С чаще всего тормозит не из-за самих функций, а из-за лишних переходов между клиентом и сервером. Каждый такой вызов — сетевой запрос.

  • ⚡ Не вызывайте серверный метод в цикле на клиенте. Соберите данные в массив и отправьте одним вызовом.
  • ⚡ Если серверному методу не нужны данные формы, ставьте &НаСервереБезКонтекста. С &НаСервере на сервер уходит вся форма со всеми реквизитами и таблицами.
  • ⚡ У параметров серверных методов, которые вызывают с клиента, ставьте Знач — значения не поедут обратно.
  • ⚡ Не обращайтесь к реквизитам через точку от ссылки в цикле (Ссылка.Контрагент.ИНН). Каждое такое обращение может читать объект из базы. Получите данные одним запросом.

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

Значение свойстваКогда сбрасывается кешЧто кешировать
На время вызовапосле завершения серверного вызоваданные, нужные несколько раз за одну операцию
На время сеансапри завершении сеансаредко меняющиеся настройки, права, константы

Функция в таком модуле не должна менять возвращённые коллекции у вызывающего кода: все вызовы получат один и тот же экземпляр. Возвращайте фиксированные структуры и массивы (ФиксированнаяСтруктура, ФиксированныйМассив).

Проверить, где теряется время, помогает замер производительности в режиме отладки: он показывает, сколько раз вызвана каждая строка и сколько времени она заняла.

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

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