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