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