Вызов процедур и функций во встроенном языке 1С: от соседней строки до общего модуля
Содержание
Чтобы обратиться к процедуре в 1С, напишите её имя и параметры в скобках: ПересчитатьСуммы(Объект.Товары, Истина);. Если процедура лежит в том же модуле, этого достаточно. Если в другом — она должна быть объявлена с ключевым словом Экспорт, а вызов пишется через владельца: ИмяОбщегоМодуля.Метод(), Документы.ИмяДокумента.Метод() или ОбъектДокумента.Метод().
Способ вызова зависит от того, где лежит метод и где выполняется код. Внутри модуля — просто по имени. Из формы на сервер — через директиву &НаСервере или &НаСервереБезКонтекста. Из любого места конфигурации — через общий модуль. Всё это работает в платформе 8.3 одинаково для любой конфигурации: БП, ЗУП, УТ или своей разработки.
Синтаксис вызова процедуры и функции
Во встроенном языке два вида подпрограмм:
- 🔹 Процедура — выполняет действия и ничего не возвращает.
- 🔹 Функция — выполняет действия и возвращает значение через
Возврат.
Процедура СообщитьИтог(Сумма)
Сообщить("Итого: " + Формат(Сумма, "ЧДЦ=2"));
КонецПроцедуры
Функция СуммаСНДС(Сумма, Ставка = 20)
Возврат Сумма + Сумма * Ставка / 100;
КонецФункции
// Вызовы
СообщитьИтог(1500);
Итог = СуммаСНДС(1000); // 1200
Итог10 = СуммаСНДС(1000, 10); // 1100
Правила, которые проверяет компилятор:
- Процедуру вызывают отдельным оператором. Подставить её в выражение нельзя: строка
Х = СообщитьИтог(5);даст ошибкуИспользование процедуры как функции. - Функцию можно вызвать как процедуру:
СуммаСНДС(1000);. Результат просто пропадёт. - Скобки обязательны даже без параметров:
ОчиститьТаблицу();. - Порядок объявления в модуле не важен. Метод можно вызвать выше того места, где он описан.
Необязательные параметры
Параметр со значением по умолчанию (Ставка = 20) можно не передавать. Пропустить параметр в середине списка можно запятой:
Процедура Записать(Объект, Проверять = Истина, Режим = Неопределено, Тихо = Ложь)
// ...
КонецПроцедуры
Записать(ДокОбъект, , , Истина); // Проверять и Режим — по умолчанию
Когда необязательных параметров больше трёх, удобнее передавать одну Структуру с настройками. Так вызов читается легче, а новые параметры не ломают старые вызовы.
Видимость методов и ключевое слово Экспорт
Каждый модуль сам решает, какие его методы видны снаружи. Метод без Экспорт доступен только внутри своего модуля. Метод с Экспорт можно вызвать из другого модуля через владельца.
Функция ОстатокПоСкладу(Номенклатура, Склад) Экспорт
// ...
КонецФункции
Кто владелец и как к нему обратиться:
| Где описан метод | Как вызвать снаружи | Нужен ли Экспорт |
|---|---|---|
| Тот же модуль | ИмяМетода() | Нет |
| Общий модуль | ИмяМодуля.ИмяМетода() | Да |
| Глобальный общий модуль | ИмяМетода() без префикса | Да |
| Модуль менеджера | Документы.ИмяДокумента.ИмяМетода() | Да |
| Модуль объекта | ДокументОбъект.ИмяМетода() | Да |
| Модуль формы | Форма.ИмяМетода() | Да |
Модуль объекта и модуль менеджера путают часто. Разница простая:
- 📄 Модуль объекта работает с конкретным экземпляром: одним документом, одним элементом справочника. Внутри доступны реквизиты (
Дата,Контрагент,Товары) иЭтотОбъект. - 🗂 Модуль менеджера работает с типом в целом. Объекта в нём нет. Туда кладут то, что не требует открытого документа: подбор данных, печать, проверки по списку ссылок.
Экспортные переменные (Перем Кэш Экспорт;) тоже видны снаружи, но в модуле формы и в модуле объекта они живут только пока жив сам объект. Для общих данных сеанса используйте параметры сеанса или общий модуль с повторным использованием.
Вызов методов из модуля формы
Модуль управляемой формы разделён на клиентскую и серверную части. Каждый метод помечается директивой компиляции, и от неё зависит, откуда его можно вызвать.
| Директива | Где выполняется | Кто может вызвать |
|---|---|---|
&НаКлиенте | На клиенте | Только клиентские методы формы |
&НаСервере | На сервере, с данными формы | Клиентские и серверные методы формы |
&НаСервереБезКонтекста | На сервере, без данных формы | Клиент и сервер формы |
&НаКлиентеНаСервереБезКонтекста | И там, и там | Клиент и сервер формы |
Главное правило: клиент может вызвать сервер, сервер клиента вызвать не может. Из процедуры &НаСервере нельзя вызвать процедуру &НаКлиенте — компилятор скажет, что метод не определён.
&НаКлиенте
Процедура КомандаПересчитать(Команда)
ПересчитатьНаСервере(); // серверный вызов с данными формы
Курс = КурсВалюты(Объект.Валюта); // серверный вызов без контекста
КонецПроцедуры
&НаСервере
Процедура ПересчитатьНаСервере()
Для Каждого Строка Из Объект.Товары Цикл
Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;
КонецПроцедуры
&НаСервереБезКонтекста
Функция КурсВалюты(Валюта)
// запрос к регистру курсов
Возврат 1;
КонецФункции
Когда брать «без контекста»
Вызов &НаСервере отправляет на сервер данные формы и возвращает их обратно. Для формы с таблицей на 3 000 строк это заметная пауза. Если серверному методу нужны только 2-3 значения, передайте их параметрами и объявите метод &НаСервереБезКонтекста. Внутри такого метода нет Объект, Элементы и реквизитов формы — только то, что пришло в параметрах.
Обращение к методам другой формы
Экспортную процедуру открытой формы вызывают через саму форму:
&НаКлиенте
Процедура ОткрытьПодбор(Команда)
ФормаПодбора = ПолучитьФорму("Обработка.ПодборТоваров.Форма.Форма", , ЭтотОбъект);
ФормаПодбора.УстановитьОтбор(Объект.Склад); // Экспорт &НаКлиенте в той форме
ФормаПодбора.Открыть();
КонецПроцедуры
Из подчинённой формы к владельцу обращаются через ВладелецФормы.ИмяМетода(). Метод владельца тоже должен быть экспортным и клиентским.
Обработчики оповещений и ожидания
Есть вызовы, где процедуру передают по имени строкой:
ПодключитьОбработчикОжидания("ОбновитьСтатус", 5, Истина);
Оповещение = Новый ОписаниеОповещения("ВопросЗавершение", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Перезаполнить таблицу?", РежимДиалогаВопрос.ДаНет);
Процедура ОбновитьСтатус должна быть клиентской и без параметров. Обработчик ВопросЗавершение принимает два параметра: результат и дополнительные параметры. В типовых конфигурациях такие обработчики объявляют с Экспорт — делайте так же. Опечатка в строке с именем компилятором не ловится, ошибка всплывёт только при выполнении.
Обращение к процедуре модуля объекта и менеджера
Метод модуля объекта нельзя вызвать с клиента напрямую: объект живёт на сервере. Из формы документа порядок такой:
&НаСервере
Процедура ЗаполнитьПоОснованиюНаСервере()
ДокОбъект = РеквизитФормыВЗначение("Объект");
ДокОбъект.ЗаполнитьТабличнуюЧасть(); // Экспорт в модуле объекта
ЗначениеВРеквизитФормы(ДокОбъект, "Объект");
КонецПроцедуры
Без последней строки изменения останутся в переменной ДокОбъект, а форма их не увидит.
Если ссылка уже есть, а форма не нужна, получите объект по ссылке:
ДокОбъект = Ссылка.ПолучитьОбъект();
ДокОбъект.ЗаполнитьТабличнуюЧасть();
ДокОбъект.Записать();
Модуль менеджера вызывается через коллекцию менеджеров: Справочники.Номенклатура.ИмяМетода(), Документы.РеализацияТоваровУслуг.ИмяМетода(), РегистрыСведений.КурсыВалют.ИмяМетода(). Модуль менеджера тоже серверный, поэтому с клиента — только через серверный метод формы или общий модуль с флагом «Вызов сервера».
В типовых конфигурациях модули объекта и менеджера часто разбиты на области ПрограммныйИнтерфейс и СлужебныйПрограммныйИнтерфейс. Методы из первой области предназначены для вызова снаружи, их меньше меняют при обновлении.
Общие модули: как вызвать процедуру из любого места
Общий модуль — главный способ сделать процедуру доступной всей конфигурации. Создаётся в Конфигураторе: ветка Общие → Общие модули. Метод в нём обязательно экспортный, вызов — через имя модуля:
// Общий модуль РаботаСЦенами
Функция ЦенаНоменклатуры(Номенклатура, ВидЦены, Дата) Экспорт
// запрос к регистру цен
КонецФункции
// Вызов
Цена = РаботаСЦенами.ЦенаНоменклатуры(Товар, ВидЦены, ТекущаяДатаСеанса());
Где модуль доступен, задают флажки в палитре свойств:
| Флажок | Что даёт |
|---|---|
| Клиент (управляемое приложение) | Модуль компилируется на клиенте |
| Сервер | Модуль компилируется на сервере |
| Вызов сервера | Серверные экспортные методы можно вызвать с клиента |
| Внешнее соединение | Доступен при подключении через COM |
| Глобальный | Методы вызываются без имени модуля |
| Привилегированный | Код выполняется без проверки прав |
| Повторное использование возвращаемых значений | Кэш результатов функций на время вызова или сеанса |
Типичная связка для вызова с клиента: модуль с флажками «Сервер» и «Вызов сервера». Если «Вызов сервера» снят, клиентский код модуль просто не увидит: при вызове будет ошибка Переменная не определена (ИмяМодуля).
Флажок «Глобальный» ставьте редко. Методы глобальных модулей компилируются при старте сеанса, и имена из разных модулей могут конфликтовать. Явное ИмяМодуля.Метод() к тому же сразу показывает, откуда метод.
Вызов по имени из строки
Иногда имя метода известно только при выполнении — например, хранится в настройках. Для этого есть оператор Выполнить:
Выполнить("РаботаСЦенами.ОбновитьКэш()");
Не собирайте строку для Выполнить из данных, которые вводит пользователь — так в базу можно протащить произвольный код. В конфигурациях на БСП есть безопасная обёртка ОбщегоНазначения.ВыполнитьМетодКонфигурации(ИмяМетода, Параметры): она проверяет, что строка — действительно имя экспортного метода.
Параметры по значению и по ссылке: Знач
По умолчанию параметры передаются по ссылке. Если процедура присвоит параметру новое значение, оно изменится и у вызывающего кода:
Процедура Удвоить(Число)
Число = Число * 2;
КонецПроцедуры
А = 5;
Удвоить(А); // А = 10
Ключевое слово Знач перед параметром передаёт копию значения:
Процедура Удвоить(Знач Число)
Число = Число * 2;
КонецПроцедуры
А = 5;
Удвоить(А); // А = 5
Здесь ловушка, на которой спотыкаются даже опытные. Для объектных типов — Массив, Структура, ТаблицаЗначений, ДокументОбъект — Знач копирует ссылку на объект, а не сам объект. Изменение содержимого видно снаружи:
Процедура Почистить(Знач Таблица)
Таблица.Очистить(); // таблица у вызывающего тоже пуста
Таблица = Новый ТаблицаЗначений; // а это присваивание наружу не уйдёт
КонецПроцедуры
Нужна независимая копия таблицы — сделайте её явно: Копия = Таблица.Скопировать();.
| Что делаем с параметром | Без Знач | Со Знач |
|---|---|---|
| Присвоили новое значение | Видно снаружи | Не видно |
| Изменили содержимое массива или таблицы | Видно снаружи | Видно снаружи |
| Серверный вызов с клиента | Значение возвращается на клиент | Обратно не передаётся |
Знач в серверных вызовах
При вызове сервера с клиента параметры сериализуются и уходят на сервер. Параметры без Знач после вызова передаются обратно, параметры со Знач — нет. Для больших структур это лишний трафик. Если серверный метод не меняет параметр для клиента, пишите Знач: вызов станет легче, а намерение — понятнее.
Ошибки при вызове методов и как их перехватывать
Ошибки вызова бывают двух видов. Одни ловит компилятор при сохранении модуля или при синтаксической проверке. Другие всплывают только при выполнении — чаще всего при вызове через переменную, строку или другую форму.
Экспорт, или он лежит не в том модуле (менеджер вместо объекта). Добавьте Экспорт или поправьте владельца.Возврат или вызовите отдельной строкой.Перехват ошибки выполнения
Если вызываемый метод может упасть — например, обращается к внешнему файлу или веб-сервису, — оберните вызов в Попытка:
Попытка
РезультатЗагрузки = ЗагрузкаИзФайла.Загрузить(ПутьКФайлу);
Исключение
Инфо = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации("Загрузка из файла",
УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(Инфо));
ВызватьИсключение КраткоеПредставлениеОшибки(Инфо);
КонецПопытки;
Что здесь важно:
- 🧭
ПодробноеПредставлениеОшибкидаёт стек вызовов с модулями и номерами строк — его пишут в журнал регистрации для разработчика. - 💬
КраткоеПредставлениеОшибки— понятный текст для пользователя. - 🔁
ВызватьИсключениепередаёт ошибку выше по стеку. Пустой блокИсключениебез записи в журнал «проглатывает» ошибку: данные не загрузились, а пользователь об этом не узнал.
Не оборачивайте в Попытка запись объекта внутри транзакции, чтобы «продолжить при ошибке». После исключения в транзакции её уже нельзя зафиксировать — будет ошибка В данной транзакции уже происходили ошибки.
Свою ошибку из вызываемого метода выбрасывают тем же оператором: ВызватьИсключение "Не задан склад";. Вызывающий код получит её в своём блоке Исключение.
Частые вопросы
Можно ли вызвать процедуру модуля формы из общего модуля?
Форма.ОбновитьИтоги().Как вызвать процедуру с сервера на клиенте?
Чем Асинх-функция отличается при вызове?
Асинх (платформа 8.3.18 и выше) возвращает Обещание. Чтобы получить результат, вызывайте её с Ждать внутри другой Асинх-процедуры.Как вызвать метод внешней обработки?
ВнешниеОбработки.Создать(ИмяФайла). Её экспортные методы модуля объекта вызываются через полученный объект. Это серверный код.Можно ли передать процедуру как параметр?
ОписаниеОповещения и затем вызывают ВыполнитьОбработкуОповещения.