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