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