При организации регулярного обмена данными между конфигурациями 1С часто возникает необходимость передавать специфические пакеты информации. Типичный сценарий: выгрузка данных из регистров накопления или сведений конфигурации-источника (например, 1С:Управление торговлей 11) и их сворачивание в один виртуальный документ-регистратор в базе-приемнике. Самого этого документа в источнике физически не существует — он генерируется «на лету» с помощью произвольного алгоритма в правилах выгрузки данных (ПВД).
Такой механизм прекрасно работает при интерактивной выгрузке через обработку Универсальный обмен данными XML. Однако при попытке настроить автоматический регулярный обмен с указанием конкретного Узла плана обмена система выдает критическую ошибку:
Ошибка при вызове метода контекста (НайтиПоТипу): Несоответствие типов (параметр номер '1')
В этой статье мы подробно разберем, почему возникает данная проблема, проанализируем внутренние механизмы работы Конвертации данных 2.0 (2.1) и рассмотрим три надежных способа её решения.
Давайте разберем поведение платформы и обработок обмена по шагам. Когда в настройках выгрузки не указан узел обмена, система последовательно выполняет все ПВД. Но как только мы позиционируемся на конкретном узле плана обмена, логика работы кардинально меняется:
Метаданные.НайтиПоТипу().СтрокаПравилаВыгрузки.ОбъектВыборки.Неопределено).Попытка передать пустое значение в метод НайтиПоТипу() мгновенно вызывает исключение. Важно понимать, что эта ошибка происходит на этапе предварительного анализа метаданных, поэтому стандартные обработчики ПВД (такие как Перед обработкой или Перед выгрузкой) даже не успевают начать выполняться. Установка флага Отказ = Истина в этих обработчиках не принесет никакого результата.
Ниже детально рассмотрим три практических способа обхода этого системного ограничения.
Самый чистый с архитектурной точки зрения метод — полностью отказаться от использования проблемного ПВД. Так как у нас нет реального объекта-источника, мы можем инициировать программную выгрузку данных напрямую из глобальных событий конвертации, которые выполняются один раз за сеанс обмена.
Выполним следующие шаги для реализации этого метода:
Пример программного кода для обработчика «Перед выгрузкой данных»:
// Запрос для выборки данных из регистров-источников
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Продажи.Номенклатура КАК Номенклатура,
| Продажи.Количество КАК Количество,
| Продажи.Сумма КАК Сумма
|ИЗ
| РегистрНакопления.ВыручкаИСебестоимостьПродаж КАК Продажи
|ГДЕ
| Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода";
Запрос.УстановитьПараметр("НачалоПериода", НачалоПериода);
Запрос.УстановитьПараметр("КонецПериода", КонецПериода);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
// Формируем структуру данных, которую ожидает наше ПКО приемника
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("ТабличнаяЧастьДокумента", РезультатЗапроса.Выгрузить());
СтруктураДанных.Вставить("Дата", ТекущаяДата());
СтруктураДанных.Вставить("Комментарий", "Сгенерировано автоматически по данным регистров");
// Вызываем выгрузку по конкретному правилу конвертации объектов (ПКО)
// Имя ПКО должно точно совпадать с именем правила для документа-приемника
ВыгрузитьПоПравилу(СтруктураДанных, , , , "ИмяВашегоПКО_ДокументРегистратор");
КонецЕсли;
Преимущество этого метода заключается в том, что система не будет спотыкаться на пустом объекте выборки в правилах выгрузки, так как самого ПВД больше не существует в структуре обмена.
Если вам критически важно сохранить логику именно внутри ПВД (например, для наглядности структуры правил конвертации), можно применить обходной маневр и «обмануть» валидатор метаданных платформы.
Разберем этот процесс по шагам:
Организации или константу ВалютаРегламентированногоУчета).Метаданные.НайтиПоТипу() отработает корректно и вернет тип выбранного фиктивного объекта. Ошибка несоответствия типов исчезнет.Пример кода в обработчике ПВД «Перед обработкой»:
// Выполняем наш произвольный алгоритм формирования данных
ТаблицаДанных = ПолучитьДанныеДляВиртуальногоДокумента();
Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
// Выгружаем данные через ПКО
ВыгрузитьПоПравилу(СтрокаДанных, , , , "ИмяВашегоПКО_ДокументРегистратор");
КонецЦикла;
// Говорим системе, что стандартный выбор объектов (например, Организаций) делать не нужно
Отказ = Истина;
Важное предупреждение: При обмене по узлу плана обмена правила выгрузки запускаются только тогда, когда для указанного объекта выборки зарегистрированы изменения на узле. Если вы указали в качестве объекта выборки справочник Организации, вам придется программно регистрировать фиктивное изменение для этого справочника при каждом обмене, иначе система решит, что изменений нет, и просто пропустит это ПВД.
Если вы используете внешнюю обработку выгрузки (например, запускаете обмен по расписанию через командную строку или регламентное задание с внешней обработкой УниверсальныйОбменДаннымиXML.epf), вы можете устранить ошибку непосредственно в коде самой обработки.
Давайте проанализируем, где именно происходит сбой, и внесем исправления:
УниверсальныйОбменДаннымиXML в конфигураторе.ВыполнитьВыгрузкуИзмененныхДанныхДляУзлаОбмена.НайтиПоТипу.Отредактируем код следующим образом:
Для Каждого СтрокаПравилаВыгрузки Из ВременныйМассивПравилКонвертации Цикл
// БЫЛО:
// МетаданныеПВД = Метаданные.НайтиПоТипу(СтрокаПравилаВыгрузки.ОбъектВыборки);
// МассивВыгружаемыхМетаданных.Добавить(МетаданныеПВД);
// СТАЛО:
// Добавляем проверку, чтобы избежать падения при пустом объекте выборки (произвольный алгоритм)
Если ЗначениеЗаполнено(СтрокаПравилаВыгрузки.ОбъектВыборки) Тогда
МетаданныеПВД = Метаданные.НайтиПоТипу(СтрокаПравилаВыгрузки.ОбъектВыборки);
Если МетаданныеПВД <> Неопределено Тогда
МассивВыгружаемыхМетаданных.Добавить(МетаданныеПВД);
КонецЕсли;
КонецЕсли;
КонецЦикла;
После этого изменения обработка будет полностью игнорировать пустые объекты выборки в ПВД при анализе изменений на узле обмена, и выгрузка произвольным алгоритмом отработает в штатном режиме.
Поскольку физического документа в источнике не существует, стандартная авторегистрация изменений на узле для него не работает. Чтобы выгружать только новые или изменившиеся данные из регистров, нам нужно правильно построить логику получения этих изменений.
Для этого в произвольном запросе (в первом или втором способе) следует обращаться не к физическим таблицам регистров, а к таблицам зарегистрированных изменений этих регистров на узле обмена.
Проанализируем пример запроса для получения изменений регистра накопления:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ИзмененияРегистра.Регистратор КАК Регистратор,
| ИзмененияРегистра.НомерСтроки КАК НомерСтроки
|ИЗ
| РегистрНакопления.ВыручкаИСебестоимостьПродаж.Изменения КАК ИзмененияРегистра
|ГДЕ
| ИзмененияРегистра.Узел = &УзелОбмена";
// Ссылка на узел обмена в обработчиках КД обычно доступна через переменную "УзелДляОбмена"
Запрос.УстановитьПараметр("УзелОбмена", УзелДляОбмена);
РезультатЗапроса = Запрос.Выполнить();
После того как данные успешно выбраны, скомпонованы и отправлены в приемник с помощью метода ВыгрузитьПоПравилу, необходимо обязательно очистить регистрацию изменений для обработанного узла. Это предотвратит повторную выгрузку одних и тех же данных при следующем сеансе обмена.
Для очистки регистрации используем следующий метод в конце вашего алгоритма выгрузки:
// Очищаем регистрацию изменений по регистру для нашего узла
ПланыОбмена.УдалитьРегистрациюИзменений(УзелДляОбмена, Метаданные.РегистрыНакопления.ВыручкаИСебестоимостьПродаж);
Используя предложенные методики, вы сможете гибко настраивать сложные обмены данными, не ограничивая себя стандартными механизмами регистрации документов в планах обмена.