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