Почему не удаляется регистрация изменений набора записей регистра сведений в 1С и как это исправить?

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

При разработке механизмов интеграции и выгрузки данных (например, при обмене с сайтами или внешними CRM-системами) программисты часто сталкиваются с необходимостью ручного управления регистрацией изменений в планах обмена. Одной из классических проблем является ситуация, когда код для снятия с регистрации набора записей независимого регистра сведений выполняется без ошибок, но сами записи упорно остаются в таблице регистрации изменений.

В этой статье мы подробно разберем, почему стандартный метод ПланыОбмена.УдалитьРегистрациюИзменений() может не отрабатывать для независимых регистров сведений (на примере регистра ЗначенияСвойствОбъектов), проанализируем внутреннее устройство платформы «1С:Предприятие 8» и рассмотрим лучшие практики реализации подобных механизмов обмена.

1. Почему не работает стандартный код удаления регистрации изменений?

Рассмотрим исходный код, который часто используют разработчики:


НаборЗаписей = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Объект.Установить(Выборка.Объект);    
НаборЗаписей.Отбор.Свойство.Установить(Выборка.Свойство);    
НаборЗаписей.Прочитать();
ПланыОбмена.УдалитьРегистрациюИзменений(УзелСсылка, НаборЗаписей);

На первый взгляд, код выглядит абсолютно логично: создается набор записей, устанавливается отбор по ключевым полям (измерениям), данные считываются из СУБД, и набор передается в планы обмена для очистки. Однако на практике записи остаются зарегистрированными на узле. Выясним скрытые причины такого поведения платформы.

Влияние свойства «Основной отбор» у измерений регистра

Главная и наиболее частая причина кроется в настройках метаданных самого регистра сведений в конфигураторе. Платформа 1С проектирует таблицы регистрации изменений в СУБД на основе структуры измерений регистра, но с одним важнейшим нюансом: в таблицу регистрации попадают только те измерения, у которых в свойствах включен флаг «Основной отбор».

Проанализируем структуру регистра ЗначенияСвойствОбъектов:

Если флаг «Основной отбор» для измерения Свойство отключен, то физическая таблица регистрации изменений хранит информацию только в разрезе измерения Объект. Платформа регистрирует изменение объекта «целиком», не детализируя его до конкретного свойства в служебных таблицах планов обмена.

Когда мы передаем в метод УдалитьРегистрациюИзменений() набор записей, в котором установлены отборы и по свойству, и по объекту, платформа сопоставляет структуру нашего отбора со структурой таблицы регистрации изменений. Обнаружив «лишнее» поле отбора (Свойство), которого нет в ключевых полях таблицы регистрации, платформа либо тихо игнорирует вызов, либо (в зависимости от релиза платформы) вызывает ошибку недопустимых параметров. Сопоставление ключей не происходит, и регистрация изменений остается нетронутой.

Избыточность и вред метода Прочитать()

Обратим внимание на вызов метода НаборЗаписей.Прочитать() в исходном коде. Для целей управления планами обмена этот вызов не просто избыточен, он вреден по нескольким причинам:

  1. Избыточная нагрузка на СУБД: Метод Прочитать() выполняет полноценный SQL-запрос к базе данных для получения актуальных записей. При удалении регистрации большого объема данных в цикле это приведет к серьезным задержкам и падению производительности. Метод УдалитьРегистрациюИзменений() работает исключительно со свойствами отбора переданного объекта и не анализирует сами строки внутри набора.
  2. Проблема удаленных данных: Если запись регистра сведений была физически удалена пользователем или бизнес-логикой системы, то метод Прочитать() вернет пустой набор записей. На некоторых версиях платформы работа с пустым прочитанным набором записей при снятии с регистрации приводила к неявным ошибкам работы механизма регистрации.

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

2. Как правильно очистить регистрацию изменений набора записей в коде?

Если перед нами стоит задача точечного снятия с регистрации конкретных записей независимого регистра сведений, необходимо предварительно проверить свойства измерений в конфигураторе. Набор записей должен содержать отборы строго по тем измерениям, для которых включено свойство «Основной отбор».

Если «Основной отбор» включен только у измерения Объект, правильный код будет выглядеть следующим образом:


// Создаем пустой набор записей БЕЗ вызова метода Прочитать()
НаборЗаписей = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьНаборЗаписей();

// Устанавливаем отбор только по тем измерениям, у которых стоит флаг "Основной отбор"
НаборЗаписей.Отбор.Объект.Установить(Выборка.Объект);    

// Снимаем регистрацию изменений на узле
ПланыОбмена.УдалитьРегистрациюИзменений(УзелСсылка, НаборЗаписей);

В этом случае платформа успешно сопоставит ключ отбора с физической таблицей регистрации изменений и очистит регистрацию для данного объекта.

3. Безопасная выгрузка данных: Механизм квитирования (номера сообщений)

Часто разработчики пытаются удалять регистрацию точечно вручную из-за страха потерять изменения, которые пользователи вносят в систему непосредственно во время процесса выгрузки данных. Например, выгрузка на сайт может идти несколько минут. Если в конце выгрузки просто очистить всю регистрацию по регистру, то затрутся и те изменения, которые произошли за эти несколько минут, и они никогда не попадут на сайт.

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

  1. Формирование пакета обмена: Перед началом выборки данных для отправки на сайт мы получаем уникальный номер сообщения обмена и фиксируем изменения:
    
    НовыйНомерОтправленного = ПланыОбмена.ВыбратьИзменения(УзелСсылка, УзелСсылка.НомерОтправленного + 1);
    
    Платформа автоматически помечает все зарегистрированные на данный момент изменения этим номером сообщения.
  2. Обработка и отправка данных: Мы спокойно считываем выборку изменений, сериализуем ее в XML/JSON и отправляем на сайт. Этот процесс может занимать длительное время. Все новые изменения, которые пользователи вносят в базу данных прямо сейчас, регистрируются платформой с номером сообщения NULL (они не входят в текущий отправленный пакет).
  3. Подтверждение получения: Как только сайт успешно принял данные и вернул ответ (например, HTTP 200 OK), мы фиксируем успешную доставку и очищаем только те изменения, которые входили в отправленный пакет: ПланыОбмена.УдалитьРегистрациюИзменений(УзелСсылка, НовыйНомерОтправленного); Этот метод удалит из таблицы регистрации изменений только записи с номером сообщения, меньшим или равным переданному. Все новые изменения, зарегистрированные пользователями во время процесса выгрузки (с номером NULL), останутся нетронутыми и будут выгружены при следующем сеансе обмена.

Использование механизма квитирования полностью избавляет разработчика от необходимости вручную обходить записи в цикле и точечно снимать их с регистрации.

4. Архитектурный Best Practice: Отказ от авторегистрации на регистрах сведений

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

Рекомендованное архитектурное решение:

  1. В свойствах плана обмена для регистра сведений ЗначенияСвойствОбъектов установить режим авторегистрации в значение «Запретить».
  2. Вместо этого зарегистрировать изменения для основного родительского объекта (в данном случае — справочника Номенклатура) в момент изменения его свойств.

Реализуем этот подход с помощью подписки на события. Разберем по шагам создание такого механизма:

Шаг 1. Создание подписки на событие

В конфигураторе создаем новую подписку на событие со следующими свойствами:

Шаг 2. Написание кода обработчика подписки

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


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

Преимущества данного подхода:

Проанализировав все рассмотренные варианты, мы видим, что правильное проектирование планов обмена в 1С требует понимания как низкоуровневых настроек метаданных (таких как «Основной отбор»), так и архитектурных паттернов оптимизации производительности СУБД.

← На главную