Обращение к экспортным методам модуля объекта в 1С 8.3: от ссылки до внешней обработки
Содержание
Чтобы вызвать процедуру модуля объекта в 1С, объявите её с ключевым словом Экспорт, получите сам объект (не ссылку) и обратитесь к методу через точку: Ссылка.ПолучитьОбъект().ПересчитатьСуммы(). Вызов работает только на сервере или в толстом клиенте, потому что модуль объекта в управляемом приложении на тонкий клиент не попадает.
Почти все ошибки при обращении к процедуре модуля объекта сводятся к трём вещам: метод не экспортный, вызывают его у ссылки или у данных формы, код выполняется на клиенте. Разберём каждый способ вызова на примерах для платформы 8.3 и то, как обходить эти ловушки.
Что лежит в модуле объекта и чем он отличается от модуля менеджера
Модуль объекта есть у справочников, документов, планов видов характеристик, планов счетов, бизнес-процессов, задач, обработок и отчётов. Его код исполняется в контексте одного экземпляра: внутри доступны реквизиты (Номенклатура, Товары), сам экземпляр называется ЭтотОбъект.
У того же объекта метаданных есть второй модуль — модуль менеджера. Он не привязан к экземпляру и вызывается через коллекцию: Документы.РеализацияТоваровУслуг.ИмяМетода().
| Признак | Модуль объекта | Модуль менеджера |
|---|---|---|
| Контекст | один экземпляр, есть ЭтотОбъект | весь вид объектов, экземпляра нет |
| Как вызвать | Объект.Метод() | Справочники.Имя.Метод() |
| Нужно ли читать объект из базы | да, ПолучитьОбъект() | нет |
| Типичное содержимое | обработчики ПередЗаписью, ОбработкаПроведения, расчёт по табличной части | печать, выборки, обёртки над модулем объекта |
Открыть модуль
объекта в конфигураторе: правый щелчок по объекту в дереве → Открыть модуль объекта. Модуль менеджера открывается там же соседней командой.
Процедура видна снаружи только с ключевым словом Экспорт:
// Модуль объекта документа "ЗаказКлиента"
Процедура ПересчитатьСуммы() Экспорт
Для Каждого Строка Из Товары Цикл
Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;
СуммаДокумента = Товары.Итог("Сумма");
КонецПроцедуры
Функция КоличествоСтрок() Экспорт
Возврат Товары.Количество();
КонецФункции
Без Экспорт метод доступен только изнутри того же модуля. Снаружи платформа его не видит.
В типовых конфигурациях на БСП код модуля объекта обычно обёрнут в #Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда … #КонецЕсли. Так модуль не компилируется там, где ему не место. Пишите свои экспортные методы внутри этой обёртки.
Вызов процедуры объекта через точку
Базовый способ: есть объект — есть доступ к его экспортным процедурам и функциям.
Когда объект уже в руках
Внутри обработчиков подписок на события, в модуле набора записей, в коде, который сам создал объект, переменная уже содержит экземпляр:
ДокументОбъект.ПересчитатьСуммы();
Строк = ДокументОбъект.КоличествоСтрок();
Внутри самого модуля объекта метод зовут без префикса: ПересчитатьСуммы(); или ЭтотОбъект.ПересчитатьСуммы();.
Когда есть только ссылка
Ссылка (ДокументСсылка.ЗаказКлиента) — это указатель на запись в базе. Методов модуля объекта у неё нет. Сначала читаем объект:
ДокументОбъект = ЗаказСсылка.ПолучитьОбъект();
Если ДокументОбъект = Неопределено Тогда
ВызватьИсключение "Документ не найден в базе (битая ссылка)";
КонецЕсли;
ДокументОбъект.ПересчитатьСуммы();
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
ПолучитьОбъект() возвращает Неопределено, если записи в базе уже нет. Без проверки получите ошибку на следующей строке.
Если процедура меняет данные, объект нужно записать — иначе изменения останутся в памяти и пропадут. При конкурентной работе заблокируйте объект перед изменением: ДокументОбъект.Заблокировать();.
Из формы объекта
Самая частая задача: кнопка на форме документа должна вызвать процедуру модуля объекта. Реквизит формы Объект — это не документ, а ДанныеФормыСтруктура. Методов модуля объекта у него нет, и на клиенте их не будет в принципе.
Рабочая схема — серверная процедура формы с преобразованием данных:
&НаКлиенте
Процедура Пересчитать(Команда)
ПересчитатьНаСервере();
КонецПроцедуры
&НаСервере
Процедура ПересчитатьНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ПересчитатьСуммы();
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
Записывать документ здесь не нужно: изменения вернутся в форму, пользователь сохранит их сам. Объект создаётся из данных формы, а не перечитывается из базы, поэтому несохранённые правки пользователя не теряются.
Работа через менеджер, когда ссылки нет
Иногда ссылки нет вовсе: объект нужно создать, найти по коду или вызвать логику без конкретного экземпляра. Тогда помогает менеджер.
Создание нового экземпляра
НовыйЗаказ = Документы.ЗаказКлиента.СоздатьДокумент();
НовыйЗаказ.Дата = ТекущаяДатаСеанса();
// ... заполнение табличной части ...
НовыйЗаказ.ПересчитатьСуммы();
НовыйЗаказ.Записать(РежимЗаписиДокумента.Запись);
Для справочника — Справочники.Номенклатура.СоздатьЭлемент(), для обработки — Обработки.ЗагрузкаПрайса.Создать(). У обработки после Создать() доступны все экспортные методы её модуля объекта.
Поиск и чтение
Элемент = Справочники.Контрагенты.НайтиПоКоду("000000125");
Если НЕ Элемент.Пустая() Тогда
КонтрагентОбъект = Элемент.ПолучитьОбъект();
КонтрагентОбъект.ОбновитьРеквизиты();
КонецЕсли;
НайтиПоКоду и НайтиПоНаименованию возвращают пустую ссылку, если ничего не нашли. Проверяйте Пустая() до ПолучитьОбъект().
Обёртка в модуле менеджера
Если процедуру зовут из многих мест по ссылке, вынесите чтение объекта в экспортный метод менеджера:
// Модуль менеджера документа "ЗаказКлиента"
Процедура ПересчитатьИЗаписать(ЗаказСсылка) Экспорт
ДокументОбъект = ЗаказСсылка.ПолучитьОбъект();
ДокументОбъект.Заблокировать();
ДокументОбъект.ПересчитатьСуммы();
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецПроцедуры
Вызов из любого серверного кода — одна строка: Документы.ЗаказКлиента.ПересчитатьИЗаписать(Ссылка);. С клиента такую обёртку зовут через общий модуль с флагом «Вызов сервера».
Вызов метода объекта по имени из строки
Бывает, что имя процедуры известно только во время работы: оно хранится в настройках, приходит из регистра или выбирается пользователем.
Оператор Выполнить
ИмяМетода = "ПересчитатьСуммы";
Выполнить("ДокументОбъект." + ИмяМетода + "()");
Выполнить видит локальные переменные, поэтому параметры передают по имени переменной:
Коэффициент = 1.2;
Выполнить("ДокументОбъект." + ИмяМетода + "(Коэффициент)");
Никогда не склеивайте в строку для Выполнить значения, которые ввёл пользователь или прислала внешняя система. Это прямой путь к выполнению чужого кода на сервере. Имя метода сверяйте со списком разрешённых.
Через БСП
В конфигурациях на БСП (БП 3.0, ЗУП 3.1, УТ 11 и другие) есть готовая безопасная обёртка в общем модуле ОбщегоНазначения:
Параметры = Новый Массив;
Параметры.Добавить(1.2);
ОбщегоНазначения.ВыполнитьМетодОбъекта(ДокументОбъект, "ПересчитатьСуммы", Параметры);
Процедура проверяет, что имя метода — корректный идентификатор, и выполняет вызов в безопасном режиме. Для методов менеджеров и общих модулей там же есть ОбщегоНазначения.ВыполнитьМетодКонфигурации("Документы.ЗаказКлиента.ПересчитатьИЗаписать", Параметры).
| Способ | Скорость | Безопасность | Когда брать |
|---|---|---|---|
| Через точку | максимальная | полная проверка при компиляции | имя метода известно заранее |
Выполнить | ниже, строка компилируется при каждом вызове | на совести разработчика | быстрые прототипы, свой код без БСП |
ВыполнитьМетодОбъекта | как у Выполнить | проверка имени, безопасный режим | имя из настроек в типовой конфигурации |
Можно ли узнать методы объекта программно
Полноценной рефлексии, как в Java или C#, во встроенном языке нет. Получить список процедур модуля объекта во время работы нельзя: текст модулей не входит в метаданные, доступные из кода.
Что можно сделать:
- 🔍 Узнать тип и вид объекта через
Метаданные:ДокументОбъект.Метаданные().ИмявернётЗаказКлиента,Метаданные.Документы.Найти("ЗаказКлиента")— описание илиНеопределено. - 🧩 Проверить реквизит по коллекции
Метаданные().Реквизиты.Найти("Склад"). Это про реквизиты, а не про методы. - 🛡 Проверить наличие метода опытным путём — обернуть вызов в
Попытка:
Попытка
Выполнить("ДокументОбъект." + ИмяМетода + "()");
Исключение
ЗаписьЖурналаРегистрации("Вызов метода",
УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
Минус подхода: исключение ловит и отсутствие метода, и ошибку внутри него. Различайте их по тексту ошибки или, лучше, держите явный список разрешённых имён для каждого вида объекта.
Если вам нужна «рефлексия», чтобы вызывать одинаковую процедуру у разных видов документов, поставьте одноимённый экспортный метод в модуль каждого из них. Тогда вызов через точку сработает у любого из них без строк и Выполнить.
Процедуры внешних обработок и вызов из них
Внешняя обработка (.epf) — тот же объект с модулем объекта, только загруженный из файла. Её экспортные процедуры вызывают после создания экземпляра.
Подключение в клиент-серверной базе
Файл должен попасть на сервер через временное хранилище. Схема для формы:
&НаКлиенте
Процедура ЗапуститьОбработку(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
Если НЕ Результат Тогда
Возврат;
КонецЕсли;
ВыполнитьНаСервере(Адрес);
КонецПроцедуры
&НаСервере
Процедура ВыполнитьНаСервере(АдресФайла)
ИмяОбработки = ВнешниеОбработки.Подключить(АдресФайла, , Ложь);
Обработка = ВнешниеОбработки.Создать(ИмяОбработки);
Обработка.ЗаполнитьИЗаписать();
КонецПроцедуры
Третий параметр Подключить — безопасный режим. Ложь выключает его, и платформа может запросить подтверждение в рамках защиты от опасных действий. В типовых базах внешние обработки обычно подключают через справочник «Дополнительные отчёты и обработки», где этим управляет БСП.
Отключайте безопасный режим только для обработок из проверенного источника. Обработка без безопасного режима может работать с файлами сервера и внешними ресурсами.
Из внешней обработки — к объектам базы
Внутри обработки объекты конфигурации доступны так же, как в коде самой конфигурации:
ДокументОбъект = Ссылка.ПолучитьОбъект();
ДокументОбъект.ПересчитатьСуммы();
Важное отличие от формы документа: реквизит формы обработки Объект хранит данные обработки, а не документа. Чтобы вызвать метод модуля объекта самой обработки из её формы, используйте тот же приём РеквизитФормыВЗначение("Объект") в серверной процедуре.
Ошибки при обращении к процедуре модуля объекта
Экспорт, вызов у ссылки вместо объекта или у реквизита формы Объект. Добавьте Экспорт, получите объект через ПолучитьОбъект() или РеквизитФормыВЗначение.ДокументОбъект.ПересчитатьСуммы().Функция … Возврат … КонецФункции или уберите присваивание.Процедура Пересчитать(Коэффициент = 1) Экспорт.ПолучитьОбъект() вернул Неопределено для битой ссылки, или переменная пустая. Проверяйте результат перед вызовом.&НаСервере или в общий модуль с флагом «Вызов сервера».Отдельный случай — синтаксическая проверка в конфигураторе молчит, а в работе вылетает «Метод объекта не обнаружен». Так бывает при динамическом вызове: строка в Выполнить проверяется только в момент исполнения. Ищите опечатку в имени метода в настройках или регистре, откуда оно пришло.
Как вызывать методы объекта быстро и без побочных эффектов
ПолучитьОбъект() читает из базы всё: шапку и все табличные части. Документ с 5 000 строк товаров прочитается целиком, даже если процедуре нужно одно поле.
Правила, которые держат код быстрым:
- 📖 Только чтение — без объекта. Если процедура лишь вычисляет значение по данным, перенесите её в модуль менеджера и возьмите данные запросом или
ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Склад"). - 🔁 Не открывайте объект в цикле без нужды. 1 000 вызовов
ПолучитьОбъект()— 1 000 чтений всех табличных частей. Сначала отберите запросом только те ссылки, которые действительно надо менять. - 🌐 Один серверный вызов вместо многих. С клиента собирайте параметры и делайте один вызов
&НаСервере, внутри которого уже работайте с объектом. - ✍️ Записывайте один раз. Вызовите все нужные методы, потом один
Записать(). Каждая запись запускаетПередЗаписью,ОбработкаПроведенияи подписки на события. - 🧱 Не дублируйте логику. Код расчёта держите в модуле объекта, а форма, менеджер и внешние обработки только зовут его. Тогда правка в одном месте меняет поведение везде.
- 🏷 Передавайте флаги через
ДополнительныеСвойства. Если процедуре нужно знать, что запись идёт из вашего кода, положите признак вДокументОбъект.ДополнительныеСвойства.Вставить("БезПересчета", Истина)и проверьте его вПередЗаписью.
Массовый вызов процедуры с записью в рабочей базе меняет сотни документов разом, а у проведённых — ещё и движения. Сначала прогоните код на копии базы и сделайте резервную копию перед запуском в рабочей.
Если процедура модуля объекта нужна в расширении, учтите: в модуль объекта расширения добавляют собственные методы и перехватывают типовые через аннотации &Перед, &После, &Вместо. Вызов снаружи при этом выглядит так же — через точку у объекта.
Частые вопросы
Можно ли вызвать процедуру модуля объекта без записи объекта?
Как вызвать процедуру модуля объекта из общего модуля?
ПолучитьОбъект() или создан менеджером.Работает ли вызов модуля объекта во внешнем соединении (COM)?
Чем экспортная функция модуля объекта отличается от процедуры при вызове?
Возврат, его можно присвоить переменной. Процедуру присваивать нельзя — это ошибка «Использование процедуры как функции».Как передать в процедуру параметр по значению?
Знач: Процедура Пересчитать(Знач Коэффициент) Экспорт. Изменения внутри процедуры тогда не вернутся вызывающему коду.