Как вызвать методы модуля объекта в 1С: разбор всех способов
Содержание
Чтобы обратиться к модулю объекта в 1С, нужно получить сам объект, а не ссылку: для существующего элемента — Ссылка.ПолучитьОбъект(), для нового — СоздатьЭлемент() или СоздатьДокумент() у менеджера. После этого экспортные процедуры и функции модуля объекта доступны через точку: Объект.ИмяМетода(). Ключевое слово Экспорт в объявлении метода обязательно — без него вызов снаружи выдаст ошибку.
Дальше — чем прямой вызов отличается от вызова через менеджер, когда без Выполнить не обойтись, как перебирать реквизиты по метаданным и почему половина проблем с модулем объекта связана не с синтаксисом, а с транзакцией записи. Все примеры — на встроенном языке 8.3, интерфейс «Такси».
Что живёт в модуле объекта и к чему можно обратиться
Модуль объекта есть у ссылочных данных — справочников, документов, планов видов характеристик, бизнес-процессов. Это серверный модуль: он компилируется и выполняется на стороне сервера, с клиента тонкого приложения напрямую создать объект и дёрнуть его метод нельзя — код должен идти в серверном контексте (&НаСервере, серверный общий модуль, модуль менеджера).
Внутри модуля объекта два рода содержимого:
- 🔹 Обработчики событий —
ПередЗаписью,ПриЗаписи,ОбработкаПроведения,ПередУдалением,ОбработкаЗаполненияи другие. Их вызывает платформа сама, напрямую вы к ним не обращаетесь. - 🔹 Собственные процедуры и функции — то, что вы написали для бизнес-логики элемента: пересчёт табличной части, заполнение по умолчанию, проверка реквизитов. Вот к ним и обращаются извне — но только если стоит
Экспорт.
Правило простое: видимо снаружи то, что помечено Экспорт. Всё остальное — внутренняя кухня модуля, доступная только его собственному коду.
Важно не путать модуль объекта с модулем менеджера. Модуль объекта работает с конкретным экземпляром (этим документом, этим элементом справочника). Модуль менеджера — с типом в целом (со всеми контрагентами сразу), ему не нужен экземпляр.
Прямой вызов экспортных методов через точку
Самый частый и самый наглядный способ. Получаете объект — вызываете его метод.
Для нового элемента:
Объект = Справочники.Контрагенты.СоздатьЭлемент();
Объект.Наименование = "ООО Ромашка";
Объект.ЗаполнитьРеквизитыПоУмолчанию(); // экспортный метод модуля объекта
Объект.Записать();
Для существующего — сначала превращаем ссылку в объект:
Объект = СсылкаНаКонтрагента.ПолучитьОбъект();
Объект.ПересчитатьЛимитДолга(); // экспортный метод модуля объекта
Объект.Записать();
Для документа всё симметрично, только менеджер и метод создания другие:
Док = Документы.РеализацияТоваров.СоздатьДокумент();
Док.ЗаполнитьПоЗаказу(СсылкаНаЗаказ); // ваш экспортный метод
Док.Записать(РежимЗаписиДокумента.Проведение);
Что здесь легко упустить:
ПолучитьОбъект()каждый раз возвращает новый экземпляр объекта из базы. Если вызвать его дважды для одной ссылки и записать оба — получите конфликт блокировки или перезапись чужих изменений.- Метод выполняется в контексте объекта: внутри него доступны все реквизиты и табличные части через
ЭтотОбъект(или просто по имени реквизита). - Функция может вернуть значение — используйте его:
Остаток = Объект.ВычислитьОстатокПоСкладу(Склад);.
Прямой вызов подходит, когда вы точно знаете тип объекта и имя метода на этапе написания кода. В 90% задач этого достаточно.
Когда обращаются не к объекту, а к менеджеру
Бывает, что экземпляр не нужен или его ещё нет. Тогда логику выносят в модуль менеджера и вызывают через точку от самого менеджера — без СоздатьЭлемент и ПолучитьОбъект.
// метод объявлен с Экспорт в модуле менеджера справочника Контрагенты
Результат = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ИНН);
Когда это правильнее, чем модуль объекта:
| Задача | Где размещать метод |
|---|---|
| Работа с одним элементом (пересчёт, проверка полей) | Модуль объекта |
| Поиск, выборка, групповая обработка по типу | Модуль менеджера |
| Печать, формирование по данным нескольких объектов | Модуль менеджера |
| Заполнение реквизитов нового объекта | Модуль объекта (метод ОбработкаЗаполнения или свой) |
Логика та же: метод должен быть Экспорт, обращение — через точку от менеджера. Выбор между объектом и менеджером — это выбор «работаю с экземпляром» против «работаю с типом». Если для операции не нужно читать и писать конкретную запись, ей место в менеджере: не придётся лишний раз поднимать объект из базы.
Динамический вызов: Выполнить и Вычислить
Когда имя метода или реквизита известно только во время работы программы (пришло строкой из настройки, из обработки, из таблицы соответствий), прямой точкой не обойтись. Тогда — операторы Выполнить и функция Вычислить.
Выполнить исполняет строку как оператор, ничего не возвращает:
ИмяМетода = "ПересчитатьЛимитДолга";
Выполнить("Объект." + ИмяМетода + "()");
Вычислить вычисляет выражение и возвращает результат:
ИмяРеквизита = "СуммаДокумента";
Значение = Вычислить("Объект." + ИмяРеквизита);
Это гибко, но у динамического вызова три реальные платы:
- ⚠️ Нет проверки на этапе компиляции. Опечатка в имени метода всплывёт только в момент выполнения строки, и то не всегда понятной ошибкой.
- ⚠️ Безопасность. Строка, собранная из внешних данных, — это потенциальный ввод произвольного кода. Для таких мест включайте безопасный режим:
УстановитьБезопасныйРежим(Истина)— он запрещает опасные операции внутриВыполнить. - ⚠️ Производительность. Платформа каждый раз разбирает и компилирует строку заново. В цикле на тысячи итераций это заметно.
В большинстве случаев Выполнить не нужен. Если имя приходит из ограниченного набора вариантов — замените его на Если … Тогда или на соответствие «строка → процедура». Код станет быстрее и его проверит компилятор.
Доступ к реквизитам по имени через метаданные
Отдельная и частая задача — обратиться к реквизиту, имя которого хранится в переменной, или перебрать все реквизиты объекта. Для этого Выполнить не нужен: у объекта работает обращение по имени в квадратных скобках.
ИмяРеквизита = "ИНН";
Значение = Объект[ИмяРеквизита]; // прочитать
Объект[ИмяРеквизита] = "7701234567"; // записать
Чтобы пройтись по всем реквизитам, берём их список из метаданных:
Метаданные = Объект.Метаданные();
Для Каждого Реквизит Из Метаданные.Реквизиты Цикл
ИмяРекв = Реквизит.Имя;
Значение = Объект[ИмяРекв];
// ... обработка значения по типу Реквизит.Тип
КонецЦикла;
Так же перебираются табличные части — через Метаданные.ТабличныеЧасти, а колонки каждой — через .Реквизиты этой табличной части. Этот приём незаменим в универсальных обработках: выгрузке, сравнении версий, копировании данных между однотипными объектами. Обращение через Объект[Имя] и метаданные всегда предпочтительнее Выполнить: и быстрее, и безопаснее, и читается яснее.
Если нужно узнать тип объекта на лету — ТипЗнч(Объект) или сравнение Объект.Метаданные() = Метаданные.Справочники.Контрагенты.
Модуль объекта внутри транзакции: события записи и проведения
Главная засада при работе с модулем объекта — не синтаксис вызова, а то, что часть кода выполняется внутри транзакции, которую открывает сама платформа. Когда вы делаете Объект.Записать(), события ПередЗаписью и ПриЗаписи, а при проведении — ОбработкаПроведения идут в одной транзакции с записью.
Что из этого следует:
- Если в обработчике установить
Отказ = Истина, запись отменяется и транзакция откатывается целиком. Движения, которые успели записаться, не сохранятся. ВызватьИсключениевнутри события тоже прерывает запись и откатывает транзакцию — используйте его для контроля заполнения.- Нельзя внутри события записи вызывать интерактивные действия (
Предупреждение,ВопросПользователю): код идёт на сервере и в транзакции.
Пример контроля в ОбработкаПроведения:
Процедура ОбработкаПроведения(Отказ, Режим)
Если СуммаДокумента = 0 Тогда
Отказ = Истина;
Сообщить("Нельзя провести документ с нулевой суммой");
Возврат;
КонецЕсли;
// формирование движений по регистрам
КонецПроцедуры
Блокировки при проведении
Когда модуль объекта формирует движения и контролирует остатки, нужна управляемая блокировка — чтобы два пользователя не списали один и тот же товар одновременно. Её ставят на нужные данные до чтения остатков:
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
Элемент.УстановитьЗначение("Склад", Склад);
Блокировка.Заблокировать();
Блокировка действует до конца транзакции и снимается автоматически при её завершении. Руками её снимать не нужно и не получится. Если контроль остатков в конфигурации включён, платформа поможет с блокировками сама, но в своей логике проведения их ставят явно.
Частые ошибки при обращении к модулю объекта
Разберём то, что ломает вызов чаще всего — симптом и причину.
Экспорт. Снаружи модуля виден только экспортный метод. Добавьте Экспорт в объявление процедуры или функции.СправочникСсылка, ДокументСсылка) методов модуля объекта не имеет. Сначала Ссылка.ПолучитьОбъект().Справочники.Контрагенты.Метод()). От менеджера видны только методы модуля менеджера. Либо получите объект, либо перенесите метод в модуль менеджера.&НаСервере или вынесите в серверный общий модуль.Объект.Записать(). Либо запись отменена через Отказ = Истина в событии записи.Ещё одна ошибка, которую не видно сразу: повторное получение объекта. Если в одном методе дважды сделать ПолучитьОбъект() для одной ссылки, вы получите два независимых экземпляра. Изменили один, записали другой — изменения потерялись. Держите один объект в одной переменной.
Что ускоряет вызовы, а что тормозит
Сами по себе обращения к модулю объекта недорогие. Тормозит обычно то, что происходит вокруг.
- 🚀 Читайте и пишите объекты пачкой осознанно.
ПолучитьОбъект()— это запрос к базе. В цикле на тысячи элементов каждый вызов — отдельное чтение. Там, где можно обойтись запросом или выборкой без изменения данных, не поднимайте объект. - 🚀 Прямой вызов вместо
Выполнить. Динамический вызов компилируется в рантайме каждый раз. На горячем участке разница ощутима. - 🚀 Не делайте тяжёлых операций в событиях записи. Всё, что внутри
ПриЗаписи, идёт в транзакции и держит блокировки. Долгий код здесь увеличивает взаимоблокировки у других пользователей. - 🚀 Выборка вместо запроса объектов по одному. Если нужно только прочитать реквизиты многих элементов — запрос к базе, а не
ПолучитьОбъект()в цикле.
Чем дольше открыта транзакция записи, тем выше риск, что на блокировках встанут соседние сеансы. Короткая и быстрая ОбработкаПроведения — это не только про скорость, но и про стабильность работы всех пользователей.
Если нужно решить, как именно обращаться к объекту в конкретной задаче:
Объект = Ссылка.ПолучитьОбъект(), затем Объект.Метод(). Метод — с Экспорт в модуле объекта.Справочники.Имя.Метод().Объект[ИмяРеквизита] и перебор по метаданным. Для метода — Выполнить, но только если без него никак, и с безопасным режимом.ПередЗаписью/ОбработкаПроведения, прерывайте через Отказ = Истина.Частые вопросы
Чем ссылка отличается от объекта при обращении к модулю?
Ссылка.ПолучитьОбъект().Почему метод модуля объекта не виден снаружи?
Экспорт. Без него процедура или функция доступна лишь внутри самого модуля объекта. Добавьте Экспорт — и вызов через точку заработает.Можно ли вызвать метод модуля объекта с клиента?
&НаСервере или в серверном общем модуле.Как обратиться к реквизиту, имя которого в переменной?
Значение = Объект[ИмяРеквизита]. Это работает и на чтение, и на запись, и не требует Выполнить. Перебрать все реквизиты можно через Объект.Метаданные().Реквизиты.В чём опасность оператора Выполнить?
Выполнить на прямой вызов или на обращение по имени через метаданные.