Как вызвать методы модуля объекта в 1С: разбор всех способов

· 9 мин

Содержание

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

Дальше — чем прямой вызов отличается от вызова через менеджер, когда без Выполнить не обойтись, как перебирать реквизиты по метаданным и почему половина проблем с модулем объекта связана не с синтаксисом, а с транзакцией записи. Все примеры — на встроенном языке 8.3, интерфейс «Такси».

Что живёт в модуле объекта и к чему можно обратиться

Модуль объекта есть у ссылочных данных — справочников, документов, планов видов характеристик, бизнес-процессов. Это серверный модуль: он компилируется и выполняется на стороне сервера, с клиента тонкого приложения напрямую создать объект и дёрнуть его метод нельзя — код должен идти в серверном контексте (&НаСервере, серверный общий модуль, модуль менеджера).

Внутри модуля объекта два рода содержимого:

  • 🔹 Обработчики событий — ПередЗаписью, ПриЗаписи, ОбработкаПроведения, ПередУдалением, ОбработкаЗаполнения и другие. Их вызывает платформа сама, напрямую вы к ним не обращаетесь.
  • 🔹 Собственные процедуры и функции — то, что вы написали для бизнес-логики элемента: пересчёт табличной части, заполнение по умолчанию, проверка реквизитов. Вот к ним и обращаются извне — но только если стоит Экспорт.

Правило простое: видимо снаружи то, что помечено Экспорт. Всё остальное — внутренняя кухня модуля, доступная только его собственному коду.

Важно не путать модуль объекта с модулем менеджера. Модуль объекта работает с конкретным экземпляром (этим документом, этим элементом справочника). Модуль менеджера — с типом в целом (со всеми контрагентами сразу), ему не нужен экземпляр.

Прямой вызов экспортных методов через точку

Самый частый и самый наглядный способ. Получаете объект — вызываете его метод.

Для нового элемента:

Объект = Справочники.Контрагенты.СоздатьЭлемент();
Объект.Наименование = "ООО Ромашка";
Объект.ЗаполнитьРеквизитыПоУмолчанию(); // экспортный метод модуля объекта
Объект.Записать();

Для существующего — сначала превращаем ссылку в объект:

Объект = СсылкаНаКонтрагента.ПолучитьОбъект();
Объект.ПересчитатьЛимитДолга();        // экспортный метод модуля объекта
Объект.Записать();

Для документа всё симметрично, только менеджер и метод создания другие:

Док = Документы.РеализацияТоваров.СоздатьДокумент();
Док.ЗаполнитьПоЗаказу(СсылкаНаЗаказ);  // ваш экспортный метод
Док.Записать(РежимЗаписиДокумента.Проведение);

Что здесь легко упустить:

  • ПолучитьОбъект() каждый раз возвращает новый экземпляр объекта из базы. Если вызвать его дважды для одной ссылки и записать оба — получите конфликт блокировки или перезапись чужих изменений.
  • Метод выполняется в контексте объекта: внутри него доступны все реквизиты и табличные части через ЭтотОбъект (или просто по имени реквизита).
  • Функция может вернуть значение — используйте его: Остаток = Объект.ВычислитьОстатокПоСкладу(Склад);.

Прямой вызов подходит, когда вы точно знаете тип объекта и имя метода на этапе написания кода. В 90% задач этого достаточно.

Когда обращаются не к объекту, а к менеджеру

Бывает, что экземпляр не нужен или его ещё нет. Тогда логику выносят в модуль менеджера и вызывают через точку от самого менеджера — без СоздатьЭлемент и ПолучитьОбъект.

// метод объявлен с Экспорт в модуле менеджера справочника Контрагенты
Результат = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ИНН);

Когда это правильнее, чем модуль объекта:

ЗадачаГде размещать метод
Работа с одним элементом (пересчёт, проверка полей)Модуль объекта
Поиск, выборка, групповая обработка по типуМодуль менеджера
Печать, формирование по данным нескольких объектовМодуль менеджера
Заполнение реквизитов нового объектаМодуль объекта (метод ОбработкаЗаполнения или свой)

Логика та же: метод должен быть Экспорт, обращение — через точку от менеджера. Выбор между объектом и менеджером — это выбор «работаю с экземпляром» против «работаю с типом». Если для операции не нужно читать и писать конкретную запись, ей место в менеджере: не придётся лишний раз поднимать объект из базы.

Динамический вызов: Выполнить и Вычислить

Когда имя метода или реквизита известно только во время работы программы (пришло строкой из настройки, из обработки, из таблицы соответствий), прямой точкой не обойтись. Тогда — операторы Выполнить и функция Вычислить.

Выполнить исполняет строку как оператор, ничего не возвращает:

ИмяМетода = "ПересчитатьЛимитДолга";
Выполнить("Объект." + ИмяМетода + "()");

Вычислить вычисляет выражение и возвращает результат:

ИмяРеквизита = "СуммаДокумента";
Значение = Вычислить("Объект." + ИмяРеквизита);

Это гибко, но у динамического вызова три реальные платы:

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

В большинстве случаев Выполнить не нужен. Если имя приходит из ограниченного набора вариантов — замените его на Если … Тогда или на соответствие «строка → процедура». Код станет быстрее и его проверит компилятор.

Доступ к реквизитам по имени через метаданные

Отдельная и частая задача — обратиться к реквизиту, имя которого хранится в переменной, или перебрать все реквизиты объекта. Для этого Выполнить не нужен: у объекта работает обращение по имени в квадратных скобках.

ИмяРеквизита = "ИНН";
Значение = Объект[ИмяРеквизита];        // прочитать
Объект[ИмяРеквизита] = "7701234567";    // записать

Чтобы пройтись по всем реквизитам, берём их список из метаданных:

Метаданные = Объект.Метаданные();
Для Каждого Реквизит Из Метаданные.Реквизиты Цикл
    ИмяРекв = Реквизит.Имя;
    Значение = Объект[ИмяРекв];
    // ... обработка значения по типу Реквизит.Тип
КонецЦикла;

Так же перебираются табличные части — через Метаданные.ТабличныеЧасти, а колонки каждой — через .Реквизиты этой табличной части. Этот приём незаменим в универсальных обработках: выгрузке, сравнении версий, копировании данных между однотипными объектами. Обращение через Объект[Имя] и метаданные всегда предпочтительнее Выполнить: и быстрее, и безопаснее, и читается яснее.

Если нужно узнать тип объекта на лету — ТипЗнч(Объект) или сравнение Объект.Метаданные() = Метаданные.Справочники.Контрагенты.

Модуль объекта внутри транзакции: события записи и проведения

Главная засада при работе с модулем объекта — не синтаксис вызова, а то, что часть кода выполняется внутри транзакции, которую открывает сама платформа. Когда вы делаете Объект.Записать(), события ПередЗаписью и ПриЗаписи, а при проведении — ОбработкаПроведения идут в одной транзакции с записью.

Что из этого следует:

  • Если в обработчике установить Отказ = Истина, запись отменяется и транзакция откатывается целиком. Движения, которые успели записаться, не сохранятся.
  • ВызватьИсключение внутри события тоже прерывает запись и откатывает транзакцию — используйте его для контроля заполнения.
  • Нельзя внутри события записи вызывать интерактивные действия (Предупреждение, ВопросПользователю): код идёт на сервере и в транзакции.

Пример контроля в ОбработкаПроведения:

Процедура ОбработкаПроведения(Отказ, Режим)
    Если СуммаДокумента = 0 Тогда
        Отказ = Истина;
        Сообщить("Нельзя провести документ с нулевой суммой");
        Возврат;
    КонецЕсли;
    // формирование движений по регистрам
КонецПроцедуры

Блокировки при проведении

Когда модуль объекта формирует движения и контролирует остатки, нужна управляемая блокировка — чтобы два пользователя не списали один и тот же товар одновременно. Её ставят на нужные данные до чтения остатков:

Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
Элемент.УстановитьЗначение("Склад", Склад);
Блокировка.Заблокировать();

Блокировка действует до конца транзакции и снимается автоматически при её завершении. Руками её снимать не нужно и не получится. Если контроль остатков в конфигурации включён, платформа поможет с блокировками сама, но в своей логике проведения их ставят явно.

Частые ошибки при обращении к модулю объекта

Разберём то, что ломает вызов чаще всего — симптом и причину.

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

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

Что ускоряет вызовы, а что тормозит

Сами по себе обращения к модулю объекта недорогие. Тормозит обычно то, что происходит вокруг.

  • 🚀 Читайте и пишите объекты пачкой осознанно. ПолучитьОбъект() — это запрос к базе. В цикле на тысячи элементов каждый вызов — отдельное чтение. Там, где можно обойтись запросом или выборкой без изменения данных, не поднимайте объект.
  • 🚀 Прямой вызов вместо Выполнить. Динамический вызов компилируется в рантайме каждый раз. На горячем участке разница ощутима.
  • 🚀 Не делайте тяжёлых операций в событиях записи. Всё, что внутри ПриЗаписи, идёт в транзакции и держит блокировки. Долгий код здесь увеличивает взаимоблокировки у других пользователей.
  • 🚀 Выборка вместо запроса объектов по одному. Если нужно только прочитать реквизиты многих элементов — запрос к базе, а не ПолучитьОбъект() в цикле.

Чем дольше открыта транзакция записи, тем выше риск, что на блокировках встанут соседние сеансы. Короткая и быстрая ОбработкаПроведения — это не только про скорость, но и про стабильность работы всех пользователей.

Если нужно решить, как именно обращаться к объекту в конкретной задаче:

Как обратиться к логике объекта в вашем случае?

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

Чем ссылка отличается от объекта при обращении к модулю?
Ссылка — это указатель на запись, у неё нет методов модуля объекта и её нельзя менять. Объект — это сам экземпляр с данными, его методы (экспортные) доступны, реквизиты можно изменять и записывать. Превратить ссылку в объект: Ссылка.ПолучитьОбъект().
Почему метод модуля объекта не виден снаружи?
Снаружи модуля виден только метод с ключевым словом Экспорт. Без него процедура или функция доступна лишь внутри самого модуля объекта. Добавьте Экспорт — и вызов через точку заработает.
Можно ли вызвать метод модуля объекта с клиента?
Нет. Модуль объекта выполняется на сервере. Создавать объект и обращаться к его методам нужно в серверном контексте — в процедуре &НаСервере или в серверном общем модуле.
Как обратиться к реквизиту, имя которого в переменной?
Через квадратные скобки: Значение = Объект[ИмяРеквизита]. Это работает и на чтение, и на запись, и не требует Выполнить. Перебрать все реквизиты можно через Объект.Метаданные().Реквизиты.
В чём опасность оператора Выполнить?
Код в строке не проверяется компилятором, может содержать опечатку или внешний небезопасный ввод, и каждый раз компилируется заново. Если имя заранее известно, замените Выполнить на прямой вызов или на обращение по имени через метаданные.