При организации интеграции между различными базами данных на платформе 1С предприятие часто используется стандартная обработка «Универсальный обмен данными XML» (работающая на технологии «Конвертация данных 2.0»). В интерактивном режиме пользователи могут легко зайти в настройки, выбрать нужное правило выгрузки данных (ПВД), включить его и настроить произвольный отбор (например, указать список конкретных документов или элементов справочника для выгрузки). Для автоматизации этого процесса по расписанию и исключения ручных действий можно использовать программу регулярного автоматического обмена данными между базами 1С.
Однако при автоматизации бизнес-процессов возникает необходимость делать это полностью программно: например, запустить регламентное задание, которое выберет измененные объекты по сложному алгоритму, программно заполнит отборы в обработке обмена и выполнит выгрузку в файл. В этой статье мы подробно разберем, как реализовать этот механизм, разберем частые ошибки при работе с ПостроителемОтчета, а также рассмотрим продвинутые сценарии работы с регистрами и иерархией правил.
Прежде чем перейти к готовому коду, давайте выясним причину популярной ошибки, с которой сталкиваются разработчики при попытке программно настроить отборы. Часто при попытке добавить новое поле в коллекцию отборов построителя система выбрасывает исключение:
Ошибка при вызове метода контекста (Добавить): Недопустимое значение параметра (параметр номер '1')
Давайте проанализируем, почему это происходит. Механизм отборов в обработке обмена построен на использовании стандартного объекта платформы ПостроительОтчета. Этот объект работает по строго определенным правилам:
Текст.{ГДЕ ...}.Построитель.Отбор.Добавить("ИмяПоля"), передаваемое имя должно строго совпадать с псевдонимом (алиасом), который указан в секции {ГДЕ} после ключевого слова КАК.Рассмотрим некорректный пример запроса и настройки отбора:
ПостроительОтчета.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
| АвансовыйОтчет.Ссылка КАК Ссылка
|ИЗ
| Документ.АвансовыйОтчет КАК АвансовыйОтчет
|{ГДЕ
| АвансовыйОтчет.Ссылка.* КАК АвансовыйОтчет_Номер}";
// Попытка добавить отбор по реальному реквизиту:
ПостроительОтчета.Отбор.Добавить("Документ_АвансовыйОтчет.Номер"); // Ошибка!
В данном примере построитель отчета ожидает добавления поля с именем АвансовыйОтчет_Номер, так как именно этот алиас прописан в фигурных скобках в качестве имени доступного поля компоновки. Попытка передать туда строку вида "Документ_АвансовыйОтчет.Номер" или любую другую, не объявленную в секции {ГДЕ}, приведет к аварийному завершению работы программы.
Теперь, когда мы поняли теоретические основы взаимодействия с построителем, давайте разберем рабочий алгоритм настройки отбора для справочников или документов по шагам.
Шаг 1. Инициализация обработки и загрузка правил обмена. Сначала нам необходимо создать объект обработки обмена, указать пути к файлам правил и выгрузки данных, после чего инициализировать структуру правил:
Обработина = Обработки.УниверсальныйОбменДаннымиXML.Создать();
Обработина.РежимОбмена = "Выгрузка";
Обработина.ИмяФайлаПравилОбмена = "C:\Rules\ПравилаОбменаОС.xml";
Обработина.ИмяФайлаОбмена = "C:\Exchange\ВыгрузкаОС.xml";
Обработина.ЗагрузитьПравилаОбмена();
Шаг 2. Подготовка данных для отбора. Сформируем список значений, содержащий ссылки на объекты, которые мы хотим выгрузить. Это могут быть ссылки, полученные из запроса или переданные из внешнего источника:
СписокОС = Новый СписокЗначений;
// Предположим, мы выбираем ОС по определенному условию
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ Справочник.ОсновныеСредства ГДЕ ПометкаУдаления = ЛОЖЬ";
РезультатТаблица = Запрос.Выполнить().Выгрузить();
Для каждого СтрокаИзТаблицы Из РезультатТаблица Цикл
СписокОС.Добавить(СтрокаИзТаблицы.Ссылка);
КонецЦикла;
Шаг 3. Поиск нужного правила в дереве выгрузки и настройка Построителя. Правила выгрузки данных хранятся в свойстве ТаблицаПравилВыгрузки. Нам нужно найти строку, соответствующую нашему объекту, составить для неё текст запроса, инициализировать отбор и передать настройки обратно в строку дерева правил:
Для каждого СтрокаП Из Обработина.ТаблицаПравилВыгрузки.Строки[0].Строки Цикл
Если СтрокаП.Имя = "ОсновныеСредства" Тогда
// Включаем данное правило для выгрузки
СтрокаП.Включить = Истина;
// Инициализируем построитель отчета
Построитель = Новый ПостроительОтчета;
// Динамически формируем текст запроса.
// ИмяОбъектаДляЗапроса содержит системное имя объекта, например, "Справочник.ОсновныеСредства"
ИмяОбъектаМетаданных = СтрокаП.ИмяОбъектаДляЗапроса;
ИмяАлиаса = СтрЗаменить(ИмяОбъектаМетаданных, ".", "_");
Построитель.Текст = "ВЫБРАТЬ Разрешенные _.* ИЗ " + ИмяОбъектаМетаданных + " КАК _
|{ГДЕ _.Ссылка.* КАК " + ИмяАлиаса + "}";
// Добавляем отбор в построитель
Отбор = Построитель.Отбор;
Отбор.Добавить(ИмяАлиаса);
// Настраиваем параметры отбора
Отбор[ИмяАлиаса].Использование = Истина;
Отбор[ИмяАлиаса].ВидСравнения = ВидСравнения.ВСписке;
Отбор[ИмяАлиаса].Значение = СписокОС;
// Сохраняем настройки построителя обратно в структуру правила выгрузки
СтрокаП.ИспользоватьОтбор = Истина;
СтрокаП.НастройкиПостроителя = Построитель.ПолучитьНастройки();
КонецЕсли;
КонецЦикла;
Шаг 4. Запуск выгрузки. После того как дерево правил настроено и отборы установлены, мы можем либо интерактивно открыть форму обработки, чтобы пользователь увидел предзаполненные отборы, либо сразу программно выполнить выгрузку:
// Вариант 1: Открыть форму для визуального контроля
Обработина.ПолучитьФорму("Форма").Открыть();
// Вариант 2: Выполнить автоматическую выгрузку без показа формы
Обработина.ВыполнитьВыгрузку();
На практике при написании универсального кода вы можете столкнуться со специфическими ситуациями, которые не учтены в простейших примерах. Рассмотрим, как их правильно обрабатывать.
Если ваше правило выгрузки настроено не для справочника или документа, а для регистра (сведений или накопления), стандартное свойство ИмяОбъектаДляЗапроса окажется пустым. Для регистров имя таблицы хранится в колонке ИмяОбъектаДляЗапросаРегистра. Чтобы ваш код был универсальным, используйте следующую конструкцию при определении имени таблицы метаданных:
ИмяОбъектаМетаданных = ?(ЗначениеЗаполнено(СтрокаП.ИмяОбъектаДляЗапроса),
СтрокаП.ИмяОбъектаДляЗапроса,
СтрокаП.ИмяОбъектаДляЗапросаРегистра);
В обработке обмена правила выгрузки сгруппированы в дерево (например, ветка «Документы», внутри которой лежат конкретные ПВД). Если вы программно установите флаг СтрокаП.Включить = Истина; для конкретного документа, но его родительский узел (группа «Документы») останется выключенным, то выгрузка этого документа не произойдет.
Чтобы избежать этой проблемы, необходимо рекурсивно обходить дерево правил вверх до самого корня и включать все родительские группы. Рассмотрим готовую процедуру:
Процедура ВключитьРодительскиеУзлы(СтрокаПравила)
Родитель = СтрокаПравила.Родитель;
Пока Родитель <> Неопределено Цикл
Родитель.Включить = Истина;
Родитель = Родитель.Родитель;
КонецЦикла;
КонецПроцедуры
Просто вызывайте эту процедуру каждый раз при активации конкретного правила:
СтрокаП.Включить = Истина;
ВключитьРодительскиеУзлы(СтрокаП);
При выполнении обмена в фоновых заданиях или высоконагруженных многопоточных процессах прямое изменение структуры Обработка.ТаблицаПравилВыгрузки может вызывать сбои или блокировки. Более надежной практикой считается предварительное копирование таблицы во временную переменную, выполнение всех манипуляций там, и последующий возврат структуры обратно:
ВременнаяТаблицаПравил = Обработина.ТаблицаПравилВыгрузки.Скопировать();
// ... Выполняем поиск нужных строк и установку настроек во ВременнаяТаблицаПравил ...
Обработина.ТаблицаПравилВыгрузки = ВременнаяТаблицаПравил.Скопировать();
Что делать, если нам нужно наложить отбор не по ссылке на сам объект, а по его реквизитам? Например, выгрузить только документы «Авансовый отчет», принадлежащие определенной Организации. В этом случае мы должны модифицировать текст запроса построителя, указав нужный путь к реквизиту в секции {ГДЕ}:
Построитель = Новый ПостроительОтчета;
Построитель.Текст = "ВЫБРАТЬ Разрешенные _.* ИЗ Документ.АвансовыйОтчет КАК _
|{ГДЕ _.Организация.* КАК Документ_АвансовыйОтчет_Организация}";
Отбор = Построитель.Отбор;
Отбор.Добавить("Документ_АвансовыйОтчет_Организация");
Отбор["Документ_АвансовыйОтчет_Организация"].Использование = Истина;
Отбор["Документ_АвансовыйОтчет_Организация"].ВидСравнения = ВидСравнения.Равно;
Отбор["Документ_АвансовыйОтчет_Организация"].Значение = ВыбраннаяОрганизация;
СтрокаП.ИспользоватьОтбор = Истина;
СтрокаП.НастройкиПостроителя = Построитель.ПолучитьНастройки();
Хотя манипуляции с Построителем отчета полностью работоспособны, этот способ является довольно громоздким и сильно зависит от правильности написания строк запросов. Если у вас есть возможность вносить изменения в правила обмена через конфигурацию «Конвертация данных 2.0», задачу фильтрации данных можно решить изящнее и безопаснее с помощью параметров правил выгрузки. Когда требуется выполнить точечную миграцию без изменения правил в Конвертации данных, можно использовать обработку для переноса данных между похожими конфигурациями 1С с возможностью сопоставления объектов по произвольным полям.
Шаг 1. Настройка в Конвертации Данных. Откройте ваши правила обмена в КД 2.0 и создайте новый параметр на вкладке «Параметры» (например, СписокОСДляВыгрузки с типом СписокЗначений).
Шаг 2. Написание обработчика в КД. В правиле выгрузки данных (ПВД) для ваших объектов в обработчике «Перед выгрузкой» напишите следующий лаконичный код:
Если Параметры.Свойство("СписокОСДляВыгрузки")
И ЗначениеЗаполнено(Параметры.СписокОСДляВыгрузки) Тогда
// Переопределяем стандартную выборку данных вашим подготовленным списком
ВыборкаДанных = Параметры.СписокОСДляВыгрузки;
КонецЕсли;
Шаг 3. Передача параметров из внешнего кода 1С. Теперь на стороне вашей конфигурации при программном вызове обработки вам вообще не нужно работать с Построителем отчета. Код сокращается до нескольких понятных строк:
Обработина = Обработки.УниверсальныйОбменДаннымиXML.Создать();
Обработина.ИмяФайлаПравилОбмена = "C:\Rules\ПравилаОбменаОС.xml";
Обработина.ИмяФайлаОбмена = "C:\Exchange\ВыгрузкаОС.xml";
Обработина.ЗагрузитьПравилаОбмена();
// Просто передаем подготовленный список объектов напрямую в параметры обработки
Обработина.Параметры.Вставить("СписокОСДляВыгрузки", СписокОС);
// Активируем нужное ПВД
Для каждого СтрокаП Из Обработина.ТаблицаПравилВыгрузки.Строки[0].Строки Цикл
Если СтрокаП.Имя = "ОсновныеСредства" Тогда
СтрокаП.Включить = Истина;
ВключитьРодительскиеУзлы(СтрокаП);
КонецЕсли;
КонецЦикла;
// Выполняем выгрузку
Обработина.ВыполнитьВыгрузку();
Этот подход архитектурно более правильный, поскольку он полностью разделяет бизнес-логику фильтрации (которая теперь хранится внутри правил обмена) и код вызова обмена в самой информационной базе. Это делает систему более гибкой к изменениям метаданных и исключает ошибки, связанные с синтаксисом Построителя отчета. Чтобы убедиться в корректности переданных сведений после завершения обмена, можно запустить автоматическое сравнение проводок в базах 1С с помощью специализированного инструмента.