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