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

· 9 мин

Содержание

Чтобы обратиться к процедуре в 1С, напишите её имя и параметры в скобках: ПересчитатьСуммы(Объект.Товары, Истина);. Если процедура лежит в том же модуле, этого достаточно. Если в другом — она должна быть объявлена с ключевым словом Экспорт, а вызов пишется через владельца: ИмяОбщегоМодуля.Метод(), Документы.ИмяДокумента.Метод() или ОбъектДокумента.Метод().

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

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

Во встроенном языке два вида подпрограмм:

  • 🔹 Процедура — выполняет действия и ничего не возвращает.
  • 🔹 Функция — выполняет действия и возвращает значение через Возврат.
Процедура СообщитьИтог(Сумма)
    Сообщить("Итого: " + Формат(Сумма, "ЧДЦ=2"));
КонецПроцедуры

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

// Вызовы
СообщитьИтог(1500);
Итог = СуммаСНДС(1000);       // 1200
Итог10 = СуммаСНДС(1000, 10); // 1100

Правила, которые проверяет компилятор:

  1. Процедуру вызывают отдельным оператором. Подставить её в выражение нельзя: строка Х = СообщитьИтог(5); даст ошибку Использование процедуры как функции.
  2. Функцию можно вызвать как процедуру: СуммаСНДС(1000);. Результат просто пропадёт.
  3. Скобки обязательны даже без параметров: ОчиститьТаблицу();.
  4. Порядок объявления в модуле не важен. Метод можно вызвать выше того места, где он описан.

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

Параметр со значением по умолчанию (Ставка = 20) можно не передавать. Пропустить параметр в середине списка можно запятой:

Процедура Записать(Объект, Проверять = Истина, Режим = Неопределено, Тихо = Ложь)
    // ...
КонецПроцедуры

Записать(ДокОбъект, , , Истина); // Проверять и Режим — по умолчанию

Когда необязательных параметров больше трёх, удобнее передавать одну Структуру с настройками. Так вызов читается легче, а новые параметры не ломают старые вызовы.

Видимость методов и ключевое слово Экспорт

Каждый модуль сам решает, какие его методы видны снаружи. Метод без Экспорт доступен только внутри своего модуля. Метод с Экспорт можно вызвать из другого модуля через владельца.

Функция ОстатокПоСкладу(Номенклатура, Склад) Экспорт
    // ...
КонецФункции

Кто владелец и как к нему обратиться:

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

Модуль объекта и модуль менеджера путают часто. Разница простая:

  • 📄 Модуль объекта работает с конкретным экземпляром: одним документом, одним элементом справочника. Внутри доступны реквизиты (Дата, Контрагент, Товары) и ЭтотОбъект.
  • 🗂 Модуль менеджера работает с типом в целом. Объекта в нём нет. Туда кладут то, что не требует открытого документа: подбор данных, печать, проверки по списку ссылок.

Экспортные переменные (Перем Кэш Экспорт;) тоже видны снаружи, но в модуле формы и в модуле объекта они живут только пока жив сам объект. Для общих данных сеанса используйте параметры сеанса или общий модуль с повторным использованием.

Вызов методов из модуля формы

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

ДирективаГде выполняетсяКто может вызвать
&НаКлиентеНа клиентеТолько клиентские методы формы
&НаСервереНа сервере, с данными формыКлиентские и серверные методы формы
&НаСервереБезКонтекстаНа сервере, без данных формыКлиент и сервер формы
&НаКлиентеНаСервереБезКонтекстаИ там, и тамКлиент и сервер формы

Главное правило: клиент может вызвать сервер, сервер клиента вызвать не может. Из процедуры &НаСервере нельзя вызвать процедуру &НаКлиенте — компилятор скажет, что метод не определён.

&НаКлиенте
Процедура КомандаПересчитать(Команда)
    ПересчитатьНаСервере();            // серверный вызов с данными формы
    Курс = КурсВалюты(Объект.Валюта);  // серверный вызов без контекста
КонецПроцедуры

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

&НаСервереБезКонтекста
Функция КурсВалюты(Валюта)
    // запрос к регистру курсов
    Возврат 1;
КонецФункции

Когда брать «без контекста»

Вызов &НаСервере отправляет на сервер данные формы и возвращает их обратно. Для формы с таблицей на 3 000 строк это заметная пауза. Если серверному методу нужны только 2-3 значения, передайте их параметрами и объявите метод &НаСервереБезКонтекста. Внутри такого метода нет Объект, Элементы и реквизитов формы — только то, что пришло в параметрах.

Обращение к методам другой формы

Экспортную процедуру открытой формы вызывают через саму форму:

&НаКлиенте
Процедура ОткрытьПодбор(Команда)
    ФормаПодбора = ПолучитьФорму("Обработка.ПодборТоваров.Форма.Форма", , ЭтотОбъект);
    ФормаПодбора.УстановитьОтбор(Объект.Склад); // Экспорт &НаКлиенте в той форме
    ФормаПодбора.Открыть();
КонецПроцедуры

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

Обработчики оповещений и ожидания

Есть вызовы, где процедуру передают по имени строкой:

ПодключитьОбработчикОжидания("ОбновитьСтатус", 5, Истина);

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

Процедура ОбновитьСтатус должна быть клиентской и без параметров. Обработчик ВопросЗавершение принимает два параметра: результат и дополнительные параметры. В типовых конфигурациях такие обработчики объявляют с Экспорт — делайте так же. Опечатка в строке с именем компилятором не ловится, ошибка всплывёт только при выполнении.

Обращение к процедуре модуля объекта и менеджера

Метод модуля объекта нельзя вызвать с клиента напрямую: объект живёт на сервере. Из формы документа порядок такой:

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

Без последней строки изменения останутся в переменной ДокОбъект, а форма их не увидит.

Если ссылка уже есть, а форма не нужна, получите объект по ссылке:

ДокОбъект = Ссылка.ПолучитьОбъект();
ДокОбъект.ЗаполнитьТабличнуюЧасть();
ДокОбъект.Записать();

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

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

Общие модули: как вызвать процедуру из любого места

Общий модуль — главный способ сделать процедуру доступной всей конфигурации. Создаётся в Конфигураторе: ветка Общие → Общие модули. Метод в нём обязательно экспортный, вызов — через имя модуля:

// Общий модуль РаботаСЦенами
Функция ЦенаНоменклатуры(Номенклатура, ВидЦены, Дата) Экспорт
    // запрос к регистру цен
КонецФункции

// Вызов
Цена = РаботаСЦенами.ЦенаНоменклатуры(Товар, ВидЦены, ТекущаяДатаСеанса());

Где модуль доступен, задают флажки в палитре свойств:

ФлажокЧто даёт
Клиент (управляемое приложение)Модуль компилируется на клиенте
СерверМодуль компилируется на сервере
Вызов сервераСерверные экспортные методы можно вызвать с клиента
Внешнее соединениеДоступен при подключении через COM
ГлобальныйМетоды вызываются без имени модуля
ПривилегированныйКод выполняется без проверки прав
Повторное использование возвращаемых значенийКэш результатов функций на время вызова или сеанса

Типичная связка для вызова с клиента: модуль с флажками «Сервер» и «Вызов сервера». Если «Вызов сервера» снят, клиентский код модуль просто не увидит: при вызове будет ошибка Переменная не определена (ИмяМодуля).

Флажок «Глобальный» ставьте редко. Методы глобальных модулей компилируются при старте сеанса, и имена из разных модулей могут конфликтовать. Явное ИмяМодуля.Метод() к тому же сразу показывает, откуда метод.

Вызов по имени из строки

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

Выполнить("РаботаСЦенами.ОбновитьКэш()");

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

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

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

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

А = 5;
Удвоить(А);   // А = 10

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

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

А = 5;
Удвоить(А);   // А = 5

Здесь ловушка, на которой спотыкаются даже опытные. Для объектных типов — Массив, Структура, ТаблицаЗначений, ДокументОбъект — Знач копирует ссылку на объект, а не сам объект. Изменение содержимого видно снаружи:

Процедура Почистить(Знач Таблица)
    Таблица.Очистить();                // таблица у вызывающего тоже пуста
    Таблица = Новый ТаблицаЗначений;   // а это присваивание наружу не уйдёт
КонецПроцедуры

Нужна независимая копия таблицы — сделайте её явно: Копия = Таблица.Скопировать();.

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

Знач в серверных вызовах

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

Ошибки при вызове методов и как их перехватывать

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

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

Перехват ошибки выполнения

Если вызываемый метод может упасть — например, обращается к внешнему файлу или веб-сервису, — оберните вызов в Попытка:

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

Что здесь важно:

  • 🧭 ПодробноеПредставлениеОшибки даёт стек вызовов с модулями и номерами строк — его пишут в журнал регистрации для разработчика.
  • 💬 КраткоеПредставлениеОшибки — понятный текст для пользователя.
  • 🔁 ВызватьИсключение передаёт ошибку выше по стеку. Пустой блок Исключение без записи в журнал «проглатывает» ошибку: данные не загрузились, а пользователь об этом не узнал.

Не оборачивайте в Попытка запись объекта внутри транзакции, чтобы «продолжить при ошибке». После исключения в транзакции её уже нельзя зафиксировать — будет ошибка В данной транзакции уже происходили ошибки.

Свою ошибку из вызываемого метода выбрасывают тем же оператором: ВызватьИсключение "Не задан склад";. Вызывающий код получит её в своём блоке Исключение.

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

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