Как определить, были ли изменения в справочнике 1С, используя версионирование БСП?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе работы с информационными системами на платформе 1С:Предприятие 8.3 часто возникает необходимость отслеживать изменения в объектах данных, таких как справочники. Эта задача становится особенно актуальной, когда требуется запустить определенный бизнес-процесс или выполнить дополнительные действия только в том случае, если содержимое объекта действительно изменилось, а не просто произошло его перезапись без модификации данных. Рассмотрим, как эффективно решить эту проблему, используя встроенные механизмы 1С и БСП (Библиотеки стандартных подсистем).

Проблема определения изменений объекта

Прежде всего, давайте выясним, почему эта задача не так проста, как кажется на первый взгляд. Многие разработчики сталкиваются с тем, что стандартные свойства объекта или методы платформы не всегда дают однозначный ответ на вопрос "Изменилось ли содержимое?".

  1. Свойство ВерсияДанных: Мы можем ошибочно полагать, что свойство ВерсияДанных, которое содержит текущую версию данных объекта, поможет нам. Однако, это свойство изменяется при любой записи объекта платформой 1С, независимо от того, были ли внесены реальные изменения в реквизиты или табличные части. То есть, если мы откроем объект и просто сохраним его, не меняя ничего, ВерсияДанных все равно обновится, что делает его непригодным для нашей цели.
  2. Метод Модифицированность(): Этот метод полезен для определения, был ли объект изменен *текущим пользователем* после его считывания из базы данных. Он предотвращает ненужную перезапись, если пользователь не внес изменений. Но он не позволяет отследить изменения, внесенные *другими пользователями* или программно.
  3. Прямое сравнение реквизитов и табличных частей в ПередЗаписью: Один из очевидных, но трудоемких подходов — это в обработчике события ПередЗаписью считывать старые значения всех необходимых реквизитов и табличных частей объекта из базы данных по его ссылке и сравнивать их с текущими значениями.

    Несмотря на кажущуюся логичность, этот метод имеет существенные недостатки, особенно в больших и сложных конфигурациях:

    • Сложность реализации: При большом количестве реквизитов (общих, стандартных, обычных) и, особенно, табличных частей, код сравнения становится объемным и громоздким. Нам придется обходить все строки всех табличных частей, сравнивая каждое поле.
    • Поддержка и развитие: При добавлении новых реквизитов или табличных частей в объект, потребуется постоянно дорабатывать код сравнения, что увеличивает затраты на сопровождение.
    • Производительность: Чтение всего объекта из базы данных по ссылке может быть неэффективным, если изменился лишь один реквизит. Для оптимизации можно использовать запросы для считывания только нужных полей, но это все равно усложняет код.

Использование механизма версионирования объектов БСП

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

Механизм версионирования БСП сохраняет историю изменений в регистре сведений ВерсииОбъектов. Для каждой новой версии объекта система вычисляет КонтрольнаяСумма, которая представляет собой MD5-хэш от XML-представления объекта. Это свойство является нашим ключевым инструментом.

Суть решения заключается в следующем: если содержимое объекта изменилось, то его XML-представление изменится, и, следовательно, изменится его КонтрольнаяСумма. Если же объект был просто перезаписан без фактических изменений, КонтрольнаяСумма останется прежней.

Для определения факта изменения нам достаточно сравнить КонтрольнаяСумма двух последних версий объекта, хранящихся в регистре ВерсииОбъектов:

  1. Мы получаем две самые свежие версии объекта по его ссылке.
  2. Сравниваем их контрольные суммы.
  3. Если суммы отличаются, значит, объект был изменен. Если одинаковы — изменений не было.

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

Реализация сравнения контрольных сумм

Давайте рассмотрим конкретный пример кода, который позволяет нам реализовать функцию проверки изменений, используя КонтрольнаяСумма из регистра сведений ВерсииОбъектов.

Предположим, у нас есть ссылка на объект, который мы хотим проверить. Мы создадим функцию, которая будет возвращать Истина, если объект был изменен, и Ложь в противном случае.

Функция ОбъектБылИзменен():


Функция ОбъектБылИзменен(Ссылка) Экспорт
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 2
    |    ВерсииОбъектов.КонтрольнаяСумма КАК КонтрольнаяСумма
    |ИЗ
    |    РегистрСведений.ВерсииОбъектов КАК ВерсииОбъектов
    |ГДЕ
    |    ВерсииОбъектов.Объект = &Объект
    |
    |УПОРЯДОЧИТЬ ПО
    |    ВерсииОбъектов.НомерВерсии УБЫВ";

    Запрос.УстановитьПараметр("Объект", Ссылка);
    
    РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    
    // Проверяем, что у нас есть как минимум две версии для сравнения
    Если РезультатЗапроса.Количество() >= 2 Тогда
        // Сравниваем контрольные суммы двух последних версий
        Возврат РезультатЗапроса[0].КонтрольнаяСумма <> РезультатЗапроса[1].КонтромальнаяСумма;
    Иначе
        // Если меньше двух версий (например, объект создан только что),
        // считаем, что он не был изменен относительно предыдущей версии.
        // Или, в зависимости от логики, можно считать, что он был изменен,
        // если это его первая запись после создания.
        // Для нашей задачи, предполагая, что нам нужна разница между двумя *существующими* версиями,
        // если версий мало, изменений не было (или это первая запись).
        Возврат Ложь; 
    КонецЕсли;
КонецФункции

Разберем этот код по шагам:

  1. Мы создаем новый объект Запрос.
  2. Формируем текст запроса, который выбирает ПЕРВЫЕ 2 записи из регистра сведений ВерсииОбъектов. Нам нужна КонтрольнаяСумма.
  3. Фильтруем записи по полю Объект, устанавливая в параметр &Объект нашу Ссылка.
  4. Сортируем результаты по полю НомерВерсии в убывающем порядке, чтобы получить самые новые версии первыми.
  5. Выполняем запрос и выгружаем результат в таблицу значений РезультатЗапроса.
  6. Проверяем количество строк в полученной таблице. Если у нас есть две или более версии, мы можем выполнить сравнение.
    • РезультатЗапроса[0].КонтрольнаяСумма будет соответствовать контрольной сумме самой последней версии.
    • РезультатЗапроса[1].КонтрольнаяСумма будет соответствовать контрольной сумме предпоследней версии.
  7. Сравниваем эти две суммы. Если они не равны (<>), это означает, что содержимое объекта изменилось между этими двумя версиями, и функция возвращает Истина.
  8. Если количество версий меньше двух (например, объект был только что создан и это его первая запись, или для него версионирование включили недавно), мы возвращаем Ложь, так как нет предыдущей версии для сравнения.

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

Альтернативные подходы и соображения

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

Сравнение реквизитов и табличных частей в обработчике «ПередЗаписью»

Как мы уже обсуждали, этот метод сопряжен с определенными трудностями, но он может быть единственным вариантом, если версионирование БСП не используется или требуется очень точный контроль над тем, какие именно реквизиты считать "значимыми" для изменения. Если вы решите использовать этот подход:

  1. В событии ПередЗаписью объекта, прежде чем его текущие значения будут записаны в базу данных, прочитайте старые значения нужных реквизитов и табличных частей *непосредственно из базы данных* по ссылке на текущий объект.
  2. Сравните полученные старые значения с новыми значениями, которые содержатся в текущем объекте.
  3. Если обнаружите изменения, вы можете передать флаг в ДополнительныеСвойства объекта (например, Объект.ДополнительныеСвойства.Вставить("Изменен", Истина)).
  4. В событии ПриЗаписи или в подписке на событие после записи, вы можете проверить этот флаг и запустить необходимый бизнес-процесс.

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

Использование внешнего регистра для отложенной проверки

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

  1. Создайте вспомогательный регистр сведений, например, НеобработанныеИзменения. Этот регистр может содержать измерения ОбъектСсылка и ресурс ДатаИзменения или ХэшОбъекта.
  2. В подписке на событие ПриЗаписи для объектов, которые мы хотим отслеживать, или в регламентном задании, мы записываем в этот регистр ссылки на измененные объекты, предварительно проверив их КонтрольнаяСумма (как описано выше).
  3. Другое регламентное задание периодически обрабатывает записи из НеобработанныеИзменения:
    • Получает список объектов, требующих обработки.
    • Выполняет необходимые действия (например, запускает бизнес-процесс).
    • После успешной обработки очищает соответствующие записи из регистра НеобработанныеИзменения.

Этот подход позволяет гибко управлять нагрузкой и выполнять длительные операции по контролю заполнения в фоновом режиме, не блокируя работу пользователей.

Самостоятельный расчет хэша объекта

Если по какой-то причине вы не используете БСП версионирование или вам нужен специфический хэш, который учитывает только определенные реквизиты, можно реализовать собственный расчет хэша:

  1. Сериализация объекта в XML/JSON: Вы можете программно сериализовать объект (или только его значимые реквизиты и табличные части) в формат XML или JSON. Для повышения скорости сериализации используйте методы ЗаписьFastInfoset или ЗаписьJSON.
  2. Вычисление хэша: От полученной строки XML/JSON можно вычислить контрольную сумму (например, MD5 или CRC32) с помощью стандартных средств платформы 1С.
  3. Хранение и сравнение: Этот вычисленный хэш можно хранить в дополнительном реквизите самого объекта или в отдельном регистре сведений. Затем перед записью сравнивать текущий рассчитанный хэш с сохраненным.

Однако, если БСП версионирование уже используется, нет смысла дублировать функциональность, поскольку КонтрольнаяСумма в регистре ВерсииОбъектов уже предоставляет готовый и надежный хэш.

Дополнительные рекомендации

Для эффективной работы с версионированием и отслеживанием изменений, мы рекомендуем учитывать следующие аспекты:

  1. Настройка версионирования БСП: Убедитесь, что механизм версионирования включен и правильно настроен.

    Для этого перейдите в раздел Администрирование – Общие настройки – История изменений. Здесь вы можете выбрать объекты, для которых будет вестись история, и установить срок хранения версий.

  2. Влияние на производительность и размер базы данных: Включение версионирования для большого количества объектов может существенно увеличить размер вашей информационной базы и немного повлиять на производительность при записи объектов.

    Регулярно отслеживайте объем хранимых версий с помощью отчета Количество и объем хранимых версий объекта (доступен в БСП) и настраивайте автоматическое удаление устаревших версий, чтобы оптимизировать базу данных.

  3. Проверка заполнения: Не путайте задачу определения изменений с задачей проверки заполнения реквизитов. Механизм "Проверка заполнения" в 1С (через свойство реквизита "Проверка заполнения") предназначен для интерактивного контроля обязательности заполнения полей перед записью объекта, но не для отслеживания *изменений* данных. Эти механизмы могут дополнять друг друга, но решают разные задачи.

Используя описанный подход с сравнением контрольных сумм из механизма версионирования БСП, мы можем надежно и эффективно детектировать фактические изменения в справочниках и других объектах 1С, значительно упрощая логику запуска связанных бизнес-процессов.

← На главную