При разработке в среде «1С:Предприятие 8» программисты часто сталкиваются с необходимостью вынести логику проверки или обработки данных в Общий модуль. Одной из наиболее частых задач является использование Подписок на события. Однако при переносе кода из модуля документа в общий модуль привычные методы обращения к данным, такие как ЭтотОбъект или прямое указание имени реквизита, перестают работать. В этой статье мы подробно разберем, как правильно получать значения реквизитов документа, находясь в контексте общего модуля, и какие подводные камни могут встретиться на этом пути.
Рассмотрим ситуацию, с которой столкнулся автор темы. В конфигурации «Управление торговлей 10.3» была создана подписка на событие ОбработкаПроведения для документа РеализацияТоваровУслуг. Код обработчика размещен в общем модуле. Попытка обратиться к реквизиту НаличиеПлатёжногоПоручения напрямую привела к ошибке компиляции.
Причина ошибки заключается в области видимости переменных. Когда код выполняется в модуле самого документа, платформа «понимает», что под именами реквизитов подразумеваются поля текущего объекта. Общий же модуль — это обособленная сущность. Он не «знает», какой именно объект сейчас обрабатывается, пока мы явно не передадим его в качестве параметра. Ключевое слово ЭтотОбъект в общих модулях также не определено, так как модуль не принадлежит конкретному экземпляру данных.
Любая подписка на событие в 1С имеет строго определенную структуру параметров. Первым параметром в процедуре-обработчике всегда выступает Источник. Именно в эту переменную платформа передает ссылку на объект (или сам объект), который вызвал событие.
Для того чтобы прочитать значение реквизита из общего модуля, нам необходимо обращаться к нему через точку от этого параметра. Разберем правильную структуру процедуры:
// Процедура в общем модуле, назначенная обработчиком события
Процедура ПодпискаНаСобытиеОбработкаПроведения(Источник, Отказ, РежимПроведения) Экспорт
// Получаем значение реквизита напрямую из объекта
Если Источник.НаличиеПлатёжногоПоручения Тогда
// Наша логика
Сообщить("Документ содержит платежное поручение № " + Источник.НомерПлатёжногоПоручения);
Иначе
// Пример отмены проведения, если флаг не установлен
// Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Важный нюанс: Обращение Источник.ИмяРеквизита — это самый быстрый и эффективный способ. Поскольку событие (например, проведение) происходит с объектом, который уже загружен в оперативную память, нам не нужно делать дополнительные запросы к базе данных. Объект Источник уже содержит все актуальные данные, которые пользователь ввел на форме.
В исходном вопросе автор пытался использовать Запрос для получения данных из документа. Рассмотрим, почему это плохая практика в контексте событий «ПередЗаписью» или «ОбработкаПроведения»:
ПередЗаписью в базе данных все еще лежит старое значение. Новые данные находятся только в оперативной памяти объекта Источник.Используйте запросы в подписках только в том случае, если вам нужно получить данные из других объектов или сверить текущее состояние с тем, что было в базе до начала транзакции.
Важно четко разделять данные. Реквизиты документа сохраняются в базе данных. Реквизиты формы — это временные элементы управления, которые существуют только пока открыто окно документа. Из общего модуля (который чаще всего выполняется на сервере) невозможно напрямую обратиться к реквизитам формы или элементам интерфейса (флажкам, полям ввода, кнопкам).
Если вам жизненно необходимо передать информацию из интерфейса в подписку (например, какую-то временную «галочку», которая не является реквизитом документа), используйте механизм ДополнительныеСвойства. Проанализируем, как это сделать:
1. В модуле формы документа (перед записью):
ЭтотОбъект.ДополнительныеСвойства.Вставить("ПропуститьПроверку", Истина);
2. В общем модуле (в подписке):
Если Источник.ДополнительныеСвойства.Свойство("ПропуститьПроверку") И Источник.ДополнительныеСвойства.ПропуститьПроверку Тогда
Возврат; // Выходим из проверки
КонецЕсли;
Подписки часто создаются для целого набора типов документов (например, для всех документов реализации и поступления). В этом случае в процедуру Источник может прийти объект, у которого нет реквизита НаличиеПлатёжногоПоручения. Это приведет к ошибке «Поле объекта не обнаружено».
Рассмотрим, как обезопасить наш код с помощью проверки метаданных или типа:
// Способ 1: Проверка типа объекта
Если ТипЗнч(Источник) = Тип("ДокументОбъект.РеализацияТоваровУслуг") Тогда
// Здесь мы уверены, что реквизит существует
Значение = Источник.НаличиеПлатёжногоПоручения;
КонецЕсли;
// Способ 2: Проверка наличия реквизита через метаданные
Если Источник.Метаданные().Реквизиты.Найти("НаличиеПлатёжногоПоручения") <> Неопределено Тогда
Значение = Источник.НаличиеПлатёжногоПоручения;
КонецЕсли;
Еще один критический момент при работе в общих модулях — это учет автоматических обменов (РИБ, синхронизация). Если документ загружается из другой базы, подписки на события все равно срабатывают. Если ваша проверка жесткая (например, выдает Отказ = Истина), это может полностью заблокировать обмен данными.
Рекомендуется всегда добавлять проверку на загрузку в начало любой подписки:
Процедура ОбработкаСобытия(Источник, Отказ) Экспорт
// Если идет загрузка данных из другой базы, проверку выполнять не нужно
Если Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Далее основной код
КонецПроцедуры
Подводя итог, можно составить краткий алгоритм действий для успешной работы с реквизитами в общем модуле:
ЭтотОбъект и прямое обращение к именам — в общем модуле их нет.Источник.ДополнительныеСвойства.ОбменДанными.Загрузка, чтобы не сломать синхронизацию баз.Метаданные().Соблюдение этих правил позволит вам писать надежный и производительный код, который будет легко поддерживать и развивать в рамках любых конфигураций на платформе 1С.