Обращение к экспортным процедурам модуля объекта из внешнего кода
Содержание
Чтобы вызвать процедуру из модуля объекта, у неё должно стоять ключевое слово Экспорт, а сам объект нужно сначала получить в переменную: Объект = Ссылка.ПолучитьОбъект(), и только потом писать Объект.ИмяПроцедуры(). Без Экспорт платформа такую процедуру снаружи не видит — отсюда большинство ошибок «процедура с указанным именем не определена».
Второй частый промах — попытка дёрнуть метод прямо у ссылки (Ссылка.Метод()) или прямо из клиентского кода управляемой формы. Модуль объекта исполняется на сервере, и работать можно только с объектом, а не со ссылкой. Ниже — все рабочие способы вызова, с кодом и границами применимости.
Ключевое слово Экспорт — условие видимости
Процедуры и функции модуля объекта делятся на два вида: локальные (видны только внутри модуля) и экспортные (видны снаружи). Разницу задаёт одно слово в конце строки объявления.
Процедура ОбновитьЦены() Экспорт
Для Каждого Строка Из Товары Цикл
Строка.Цена = ПолучитьБазовуюЦену(Строка.Номенклатура);
КонецЦикла;
КонецПроцедуры
Теперь из формы, общего модуля или обработки эту процедуру можно вызвать по имени объекта:
Объект = Ссылка.ПолучитьОбъект();
Объект.ОбновитьЦены();
Объект.Записать();
Если убрать Экспорт, при вызове снаружи вы получите ошибку Процедура или функция с указанным именем не определена (ОбновитьЦены). Внутри самого модуля объекта та же процедура будет вызываться без проблем — Экспорт нужен только для вызова извне.
💡 Имена методов в 1С регистронезависимы, но платформа различает процедуры и функции. Если метод возвращает значение, объявляйте его как Функция … КонецФункции и вызывайте через присваивание: Итог = Объект.РассчитатьИтог().
Параметры в метод передаются как обычно. По умолчанию параметр передаётся по ссылке — метод может его изменить, и изменение увидит вызывающий код. Если нужно защитить переданную переменную от изменения, в объявлении параметра ставят Знач:
Процедура ПрименитьСкидку(Знач Процент) Экспорт
Для Каждого Строка Из Товары Цикл
Строка.Сумма = Строка.Сумма * (1 - Процент / 100);
КонецЦикла;
КонецПроцедуры
Вызов с параметром:
Объект = Ссылка.ПолучитьОбъект();
Объект.ПрименитьСкидку(10);
Объект.Записать();
Как получить сам объект перед вызовом метода
Вызвать метод можно только у объекта (ДокументОбъект, СправочникОбъект и т. п.), а не у ссылки. Получить объект можно двумя путями — в зависимости от того, работаете вы с существующим элементом или создаёте новый.
Существующий объект берут из ссылки:
Объект = Ссылка.ПолучитьОбъект();
Новый объект создают через менеджер:
НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
У обоих вариантов доступны экспортные методы их модуля объекта. Важно помнить про жизненный цикл:
- 📌
ПолучитьОбъект()при каждом вызове возвращает новый экземпляр из базы. Если вызвать его дважды для одной ссылки, получите два разных объекта в памяти. - 📌 Изменения, сделанные методом, живут только в этом экземпляре. Чтобы они попали в базу, объект нужно записать:
Объект.Записать()(илиЗаписать(РежимЗаписиДокумента.Проведение)для проведения). - 📌 Не вызывайте
ПолучитьОбъект()в цикле по одной и той же ссылке — это лишние обращения к базе. Получите объект один раз до цикла.
Метод, который меняет данные и сам вызывает Записать(), в рабочей базе выполняйте осознанно: запись документа запускает проведение и движения по регистрам. Перед массовым вызовом таких методов по множеству объектов проверяйте логику на копии базы.
Типичный сценарий — создать документ , заполнить его экспортным методом и записать одной транзакцией:
Документ = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
Документ.Дата = ТекущаяДата();
Документ.Организация = Организация;
Документ.Контрагент = Контрагент;
Документ.ЗаполнитьПоЗаказу(Заказ); // экспортный метод модуля объекта
Документ.Записать(РежимЗаписиДокумента.Проведение);
Обратите внимание: реквизиты «шапки» можно задать прямо снаружи, а сложное заполнение табличных частей логично спрятать в метод модуля объекта — тогда одна и та же логика будет работать и при интерактивном вводе, и при программном создании.
Предопределённые обработчики событий вручную не вызывают
В модуле объекта, помимо ваших экспортных процедур, есть процедуры-обработчики событий: ПередЗаписью, ПриЗаписи, ОбработкаПроведения, ОбработкаУдаленияПроведения, ОбработкаЗаполнения и другие. Их вызывает сама платформа в нужный момент жизненного цикла объекта.
Прямой вызов такого обработчика по имени — частая ошибка новичка:
// Так делать не нужно:
Объект.ОбработкаПроведения(Отказ, РежимПроведения);
При обычной записи Объект.Записать() платформа сама вызовет ОбработкаПроведения с корректными параметрами и внутри собственной транзакции. Ручной вызов передаст неверный контекст и, скорее всего, приведёт к незаполненным движениям или ошибке.
💡 Если логику из обработчика события нужно переиспользовать, вынесите её в отдельную экспортную процедуру модуля объекта и вызывайте уже её — и из обработчика события, и из внешнего кода. Это чистый способ без «ручного» запуска системных событий.
Откуда можно вызвать: клиент, сервер и контекст модуля
Модуль объекта компилируется и исполняется на сервере. Директив &НаКлиенте в нём нет. Поэтому напрямую из клиентского кода управляемой формы метод объекта вызвать нельзя — сначала нужно перейти в серверный контекст.
Правильная связка в модуле формы выглядит так:
&НаКлиенте
Процедура ОбновитьЦеныКоманда(Команда)
ОбновитьЦеныНаСервере(Объект.Ссылка);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ОбновитьЦеныНаСервере(Ссылка)
Объект = Ссылка.ПолучитьОбъект();
Объект.ОбновитьЦены();
Объект.Записать();
КонецПроцедуры
Клиентская процедура только инициирует вызов, а всю работу с объектом ведёт серверная. Если метод должен поменять реквизиты открытой в форме записи, используйте &НаСервере без БезКонтекста и работайте с реквизитом Объект формы через РеквизитФормыВЗначение / ЗначениеВРеквизитФормы.
Отдельно стоит модуль менеджера. Его экспортные методы вызывают не через объект, а через сам менеджер:
Результат = Документы.РеализацияТоваровУслуг.ПроверитьДоступность(Номенклатура);
Правило простое: логика, привязанная к конкретному экземпляру (пересчёт строк, заполнение по данным), живёт в модуле объекта; общая логика вида «найти», «проверить», «построить» — в модуле менеджера.
Из общего модуля вызов выглядит так же — сначала объект, потом метод:
// Общий модуль, серверный контекст
Процедура ОбновитьПачкуДокументов(МассивСсылок) Экспорт
Для Каждого Ссылка Из МассивСсылок Цикл
Объект = Ссылка.ПолучитьОбъект();
Объект.ОбновитьЦены();
Объект.Записать();
КонецЦикла;
КонецПроцедуры
Общий модуль удобен тем, что один раз написанную обёртку можно звать из любой формы, обработки и регламентного задания.
Вызов процедуры по имени, заданному строкой
Когда имя метода заранее неизвестно и лежит в переменной (например, приходит из настроек или таблицы правил), применяют операторы Выполнить() и Вычислить().
Выполнить() исполняет строку как оператор — подходит для процедур:
ИмяМетода = "ОбновитьЦены";
Выполнить("Объект." + ИмяМетода + "()");
Вычислить() вычисляет выражение и возвращает значение — подходит для функций:
ИмяФункции = "РассчитатьИтог";
Итог = Вычислить("Объект." + ИмяФункции + "()");
Способ мощный, но у него есть цена:
- ❗ Код в строке не проверяется на этапе компиляции — опечатка всплывёт только при выполнении.
- ❗ Работает медленнее прямого вызова, так как разбор строки идёт в рантайме.
- ❗ На клиенте и в безопасном режиме действуют ограничения; строку, собранную из внешних данных, подставлять опасно.
Такой приём оправдан, например, в универсальной обработке, которая по настройке пользователя вызывает один из нескольких методов заполнения. Имя метода хранится в реквизите настройки, а код обработки остаётся единым — не нужно плодить ветки Если под каждый вид документа.
Для Каждого Строка Из ТаблицаПравил Цикл
Объект = Строка.Ссылка.ПолучитьОбъект();
Выполнить("Объект." + Строка.ИмяМетода + "()");
Объект.Записать();
КонецЦикла;
Используйте Выполнить() и Вычислить() только тогда, когда имя метода действительно неизвестно до запуска. Если имён конечный набор, надёжнее обычный Если … Тогда с прямыми вызовами — компилятор проверит каждый.
Асинхронный вызов после интерактивного действия
Модуль объекта выполняется синхронно на сервере. «Асинхронность» в этом сценарии появляется на клиенте: когда перед вызовом метода нужно задать пользователю вопрос, дождаться выбора файла или закрытия формы. Для этого платформа использует объект ОписаниеОповещения.
Схема: клиент показывает диалог и передаёт в него описание процедуры-обработчика, которая отработает после ответа пользователя — и уже она вызывает серверный метод объекта.
&НаКлиенте
Процедура ПересчитатьКоманда(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеОтветаНаВопрос", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Пересчитать суммы по документу?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура ПослеОтветаНаВопрос(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
ПересчитатьНаСервере(Объект.Ссылка);
КонецЕсли;
КонецПроцедуры
Здесь важны два требования к процедуре-обработчику:
- Она должна быть объявлена с
Экспорт— иначеОписаниеОповещенияеё не найдёт. - Она живёт в модуле формы или в неглобальном общем модуле с клиентским контекстом, а не в модуле объекта.
Если в обработчик нужно передать данные (ссылку, флаг, значение поля), их кладут в третий параметр конструктора — ДополнительныеПараметры. Внутри обработчика они придут вторым аргументом:
ПараметрыОбработчика = Новый Структура("Ссылка, ТолькоПроверка", Объект.Ссылка, Ложь);
Оповещение = Новый ОписаниеОповещения("ПослеОтветаНаВопрос", ЭтотОбъект, ПараметрыОбработчика);
То есть сам метод объекта по-прежнему вызывается синхронно на сервере; асинхронным становится лишь клиентский сценарий вокруг него.
Чем способы отличаются друг от друга
Выбор зависит от того, где находится код вызова и известно ли имя метода заранее.
| Способ | Когда применять | Ограничения |
|---|---|---|
Объект.Метод() | Имя известно, метод экспортный | Только на сервере, нужен полученный объект |
Менеджер.Метод() | Общая логика без конкретного экземпляра | Метод должен быть в модуле менеджера |
Выполнить() / Вычислить() | Имя метода в переменной | Нет проверки при компиляции, медленнее, безопасный режим |
Через ОписаниеОповещения | После вопроса, выбора файла, закрытия формы | Только клиентский обработчик, обязателен Экспорт |
На практике большинство вызовов — это первая строка таблицы: получили объект, вызвали экспортный метод, записали. Остальные способы подключают, когда появляется дополнительное условие: динамическое имя или интерактив перед действием.
Если метод записывает объект и вызывается в цикле по многим ссылкам, держите каждую итерацию самодостаточной: получайте свежий объект, вызывайте метод, записывайте и переходите к следующему. Не накапливайте изменения в одном объекте между итерациями — после Записать() работать с тем же экземпляром дальше можно, но для другой ссылки нужен свой ПолучитьОбъект().
Обработка ошибок при вызове метода
Метод объекта может завершиться исключением: не хватило данных, сработала проверка заполнения, запись упала на блокировке. Чтобы один сбойный объект не прерывал обработку всей пачки, оборачивайте вызов в Попытка.
Попытка
Объект = Ссылка.ПолучитьОбъект();
Объект.ОбновитьЦены();
Объект.Записать();
Исключение
ЗаписьЖурналаРегистрации(
"ОбновлениеЦен",
УровеньЖурналаРегистрации.Ошибка,
, Ссылка,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
Текст ошибки берите из ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()) — это актуальный способ для платформы 8.3 (устаревший ОписаниеОшибки() даёт меньше деталей).
Частые ошибки при вызове и их причины:
Частые вопросы
Нужен ли Экспорт, если я вызываю процедуру внутри того же модуля объекта?
Экспорт. Ключевое слово нужно только для вызова снаружи — из формы, общего модуля или обработки.Чем модуль объекта отличается от модуля менеджера при вызове?
Объект.Метод()), они работают с данными конкретного экземпляра. Методы модуля менеджера вызывают через менеджер (Документы.Вид.Метод()) — это общая логика, не привязанная к одной записи.Почему после вызова метода изменения не сохранились в базе?
Объект.Записать() — для документа при необходимости с режимом проведения.Можно ли вызвать метод объекта напрямую из кнопки на клиенте?
&НаСервере или &НаСервереБезКонтекста), получите в ней объект и вызовите метод, а с клиента зовите уже эту процедуру.Как вызвать метод, если его имя хранится в переменной?
Выполнить() для процедуры или Вычислить() для функции: Выполнить("Объект." + ИмяМетода + "()"). Учтите, что такой код не проверяется компилятором и медленнее прямого вызова.