К статье
Практический материал

Разработка и Конфигурация · 12 мин

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

Где выполняется модуль объекта в 1С: сервер, контекст, обработчики событий, директивы компиляции. Примеры кода, типичные ошибки и ответы на вопросы разработчиков.

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

Именно место исполнения определяет, какие возможности доступны внутри модуля: прямые запросы к базе данных, обращение к файловой системе сервера, выполнение запросов 1С, работа с COM-объектами на сервере. При этом недоступны интерактивные действия с пользователем — вызов Вопрос() или Предупреждение() в модуле объекта приведёт к ошибке, потому что сервер не имеет интерфейса.

Ниже разберём, где физически и логически выполняется код модуля объекта, какие события там обрабатываются, чем он отличается от модуля формы и модуля менеджера, и какие ошибки чаще всего допускают разработчики при работе с серверным контекстом.

Где физически выполняется код модуля объекта

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

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

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

Какие события обрабатываются в модуле объекта

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

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

Кроме событий, в модуле объекта можно объявлять собственные экспортные процедуры и функции — они становятся методами объекта и доступны извне через переменную типа «объект» (например, ДокументОбъект.ПересчитатьИтоги()). Это удобный способ инкапсулировать бизнес-логику внутри самого объекта.

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

Модуль объекта, модуль формы и модуль менеджера: в чём разница

Путаница между этими тремя модулями — самая частая причина ошибок начинающих разработчиков. Модуль формы работает с представлением объекта на экране и имеет клиентскую и серверную части (директивы &НаКлиенте, &НаСервере). Модуль объекта работает с данными самого объекта и только на сервере. Модуль менеджера содержит общие процедуры для всего типа объектов (например, печать, подбор) и тоже выполняется на сервере, но без контекста конкретного экземпляра.

Ключевое отличие в доступе к данным: в модуле объекта реквизиты доступны напрямую, в модуле менеджера нужно получать объект по ссылке, а в модуле формы на сервере работа идёт через реквизит формы Объект с преобразованием РеквизитФормыВЗначение("Объект"). Выбор модуля определяется задачей: логика данных — в модуль объекта, логика интерфейса — в модуль формы, операции над множеством объектов — в модуль менеджера.

Сравнение модулей

Модуль объекта

Сервер, контекст конкретного экземпляра. Подходит для событий записи, проведения, заполнения. Не доступен из клиентской формы напрямую.

Модуль формы

Клиент и сервер через директивы. Подходит для реакции на действия пользователя и управления элементами. Работает с данными формы, а не с объектом напрямую.

Модуль менеджера

Сервер, без контекста экземпляра. Подходит для печати, обработок получения данных, операций над множеством объектов типа.

Как вызвать код модуля объекта из формы

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

Типовой шаблон выглядит так: в модуле формы объявляется процедура &НаСервере, внутри неё вызывается ОбъектДанных = РеквизитФормыВЗначение("Объект"), далее вызывается метод объекта ОбъектДанных.ЗаполнитьПоУмолчанию(), и изменения возвращаются через ЗначениеВРеквизитФормы(ОбъектДанных, "Объект"). Это полноценный серверный вызов с передачей данных между клиентом и сервером.

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

Вызов метода объекта из формы
  1. 1
    В модуле объекта объявите экспортную процедуру, например Процедура ЗаполнитьЦены() Экспорт
  2. 2
    В модуле формы создайте серверную процедуру с директивой &НаСервере
  3. 3
    Получите объект: ОбъектДанных = РеквизитФормыВЗначение("Объект")
  4. 4
    Вызовите метод: ОбъектДанных.ЗаполнитьЦены()
  5. 5
    Верните изменения: ЗначениеВРеквизитФормы(ОбъектДанных, "Объект")
  6. 6
    Вызовите серверную процедуру из клиентского обработчика команды или кнопки

Директивы компиляции и ограничения серверного контекста

В модуле объекта директивы компиляции не используются — платформа сама компилирует его только для сервера. Это отличает его от модуля формы, где каждая процедура требует явной директивы. Если вы попытаетесь написать &НаКлиенте в модуле объекта, конфигуратор выдаст ошибку проверки.

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

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

Типичные ошибки при работе с модулем объекта

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

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

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

Типичные ошибки
01
Вызов метода через ссылку вместо ПолучитьОбъект() — ошибка выполнения, метод не найден
02
Использование Предупреждение() или Вопрос() в модуле объекта — ошибка на сервере, интерактивные вызовы недоступны
03
Изменение реквизитов в ОбработкаПроверкиЗаполнения — изменения теряются или нарушают логику записи
04
Отсутствие проверки ОбменДанными.Загрузка — срабатывание бизнес-логики при загрузке данных через обмен
05
Длительные операции в ОбработкаПроведения — блокировки и тайм-ауты при параллельной работе

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

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

Для проверки без отладчика используйте СообщениеПользователю или запись в журнал регистрации через ЗаписьЖурналаРегистрации. Второй вариант предпочтительнее в рабочей базе: сообщения не мешают пользователям, а события потом можно отфильтровать в журнале. Убедитесь, что после окончания тестирования отладочные записи удалены или отключены.

Контрольные точки

Как проверить результат

Проведение документа
Норма

Движения по регистрам сформированы согласно логике обработчика

Тревожный признак

Движений нет, они дублируются или содержат неверные суммы

Сообщения об ошибках
Норма

Сообщения выводятся через СообщениеПользователю и привязаны к полям

Тревожный признак

Ошибка выполнения из-за интерактивных вызовов на сервере

Загрузка через обмен данными
Норма

При ОбменДанными.Загрузка = Истина бизнес-логика пропускается

Тревожный признак

При обмене создаются лишние движения и сообщения

Часто задаваемые вопросы

Можно ли вызвать процедуру модуля объекта из другого объекта?

Да, если получить объект через ПолучитьОбъект() по ссылке и вызвать его экспортную процедуру. Учитывайте, что это загружает объект целиком в память сервера, поэтому для чтения отдельных реквизитов эффективнее использовать запрос.

Почему в модуле объекта не работает Предупреждение()?

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

Чем ОбработкаПроверкиЗаполнения отличается от ПередЗаписью?

ОбработкаПроверкиЗаполнения вызывается при проверке заполнения (в том числе при интерактивной проверке до записи) и предназначен только для контроля данных. ПередЗаписью вызывается непосредственно в транзакции записи и подходит для подготовки и изменения данных перед сохранением.

Выполняется ли модуль объекта при программной записи из фонового задания?

Да. Любая запись объекта — интерактивная, программная, из фонового задания или через обмен — вызывает обработчики модуля объекта на сервере. Единственный способ записать объект без логики — установить ОбменДанными.Загрузка = Истина, но и тогда сами обработчики вызываются, просто корректный код должен проверять этот признак.

Где разместить функцию, которая нужна и форме, и объекту?

Если функция работает с данными объекта, разместите её в модуле объекта как экспортную и вызывайте из формы через серверную процедуру с РеквизитФормыВЗначение. Если функция универсальна и не привязана к экземпляру, лучше вынести её в общий модуль с признаком «Сервер».

Нужны ли директивы &НаСервере в модуле объекта?

Нет. Модуль объекта компилируется только для серверного контекста, и директивы компиляции в нём не применяются. Их использование приведёт к ошибке при проверке конфигурации.

Итог

Что важно запомнить

Модуль объекта выполняется только на сервере в контексте конкретного экземпляра объекта. Он предназначен для событий записи, проведения и заполнения; интерактивные вызовы в нём запрещены. Из формы его методы вызываются через серверные процедуры с преобразованием РеквизитФормыВЗначение, а при обмене данными обязательна проверка ОбменДанными.Загрузка.