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