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