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

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

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

Определение типа операции в событии ПередЗаписью

В отличие от справочников или документов, независимый регистр сведений не имеет объекта «Запись» в привычном понимании для событий модуля. Мы всегда работаем с объектом РегистрСведенийНаборЗаписей. У этого объекта нет метода ЭтоНовый(), поэтому определение того, создается ли запись, изменяется или удаляется, требует анализа свойств набора и состояния базы данных.

Проанализируем основные сценарии в обработчике ПередЗаписью:

  1. Удаление записи: Если в наборе записей Источник.Количество() равно 0, а параметр Замещение установлен в значение Истина, это означает, что система готовится стереть все данные, соответствующие текущему отбору набора.
  2. Создание или изменение: Чтобы отличить новую запись от редактирования старой, нам придется обратиться к текущему состоянию базы. Мы можем создать вспомогательный набор записей, прочитать его по тем же отборам и сравнить результат.

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


Процедура ПередЗаписью(Отказ, Замещение)
    
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

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

КонецПроцедуры

Проблема регистрации «фантомных» изменений при удалении

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

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

Способ 1: Очистка регистрации через запрос (постфактум)

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

Рассмотрим пример реализации такой очистки:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТвойРегистрИзменения.Измерение1,
    |	ТвойРегистрИзменения.Измерение2,
    |	ТвойРегистрИзменения.Узел
    |ИЗ
    |	РегистрСведений.ТвойРегистр.Изменения КАК ТвойРегистрИзменения
    |		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ТвойРегистр КАК ТвойРегистр
    |		ПО ТвойРегистрИзменения.Измерение1 = ТвойРегистр.Измерение1
    |			И ТвойРегистрИзменения.Измерение2 = ТвойРегистр.Измерение2
    |ГДЕ
    |	ТвойРегистр.Измерение1 ЕСТЬ NULL";

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    УзелОбмена = Выборка.Узел;
    
    // Создаем пустой набор с отбором по измерениям удаленной записи
    НаборДляОчистки = РегистрыСведений.ТвойРегистр.СоздатьНаборЗаписей();
    НаборДляОчистки.Отбор.Измерение1.Установить(Выборка.Измерение1);
    НаборДляОчистки.Отбор.Измерение2.Установить(Выборка.Измерение2);
    
    // Удаляем регистрацию конкретного изменения для узла
    ПланыОбмена.УдалитьРегистрациюИзменений(УзелОбмена, НаборДляОчистки);
КонецЦикла;

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

Способ 2: Управление регистрацией в событии ПриЗаписи

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

Проанализируем алгоритм: если мы понимаем, что происходит удаление и оно не должно мигрировать в другие базы, мы вызываем ПланыОбмена.УдалитьРегистрациюИзменений() сразу после того, как БСП или встроенные механизмы выполнили регистрацию.


Процедура ПриЗаписи(Отказ)
    
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    ТипОперации = "";
    ЭтотОбъект.ДополнительныеСвойства.Свойство("ТипОперации", ТипОперации);

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

КонецПроцедуры

Рекомендации при работе с БСП

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

Разберем, почему возникают ошибки «Объект не найден» при обмене:

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

Подведем итог: Для управления состоянием записей в независимых регистрах используйте анализ Количество() и Замещение в связке с ДополнительныеСвойства. Для очистки очереди обмена используйте метод ПланыОбмена.УдалитьРегистрациюИзменений(), передавая в него либо конкретный узел, либо массив узлов, и сам набор записей с установленными отборами.

← На главную