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