При настройке правил обмена в конфигурации «1С:Конвертация данных 2» нередко возникает необходимость отобрать данные по сложным условиям, которые не поддерживаются стандартным интерфейсом обработки выгрузки (например, выгрузка документов только по определенной иерархической папке справочника контрагентов из «1С:Предприятия 7.7»). В таких случаях разработчики используют произвольный алгоритм выборки данных в Правиле выгрузки данных (ПВД), заполняя предопределенную коллекцию ВыборкаДанных вручную с помощью объекта ТаблицаЗначений. Однако на практике часто возникает ситуация: выборка формируется полностью, но в результирующий XML-файл попадает только первый документ из списка.
Разберем причины такого поведения платформы и обработки выгрузки, а также рассмотрим несколько способов исправления ситуации.
Проанализируем работу механизма выгрузки. По умолчанию в правилах обмена включено кэширование выгружаемых объектов для исключения дублирования ссылок. Когда выгрузка выполняется в стандартном режиме, обработка получает ссылку на реальный объект информационной базы и вычисляет уникальный идентификатор. Если же мы передаем в ПВД искусственно созданную коллекцию ТаблицаЗначений, движок правил не может автоматически получить ссылочный идентификатор источника. В результате для всех строк создается пустой или одинаковый ключ выгрузки. Выгрузив первую строку, обработка фиксирует, что объект уже выгружен, и пропускает все последующие строки коллекции.
Самый простой и быстрый способ обойти поведение кэширования — отключить запоминание объектов для конкретного правила выгрузки. Посмотрим, как это сделать:
Важное ограничение: если между правилами конвертации объектов (ПКО) присутствуют циклические ссылки, отключение кэширования может привести к бесконечной рекурсии при выгрузке. Применяйте эту опцию осознанно.
Рассмотрим еще одну распространенную ошибку, связанную с особенностями платформы «1С:Предприятие 7.7». Объекты типа ТаблицаЗначений в 7.7 передаются по ссылке. Если инициализировать подчиненную таблицу (например, табличную часть документа) один раз до цикла, а внутри цикла только очищать ее через УдалитьСтроки(), то в каждой строке главной таблицы ВыборкаДанных окажется ссылка на один и тот же экземпляр объекта. В итоге все документы получат состав табличной части самого последнего обработанного документа.
Чтобы избежать этой проблемы, необходимо создавать новый экземпляр таблицы значений на каждой итерации цикла. Посмотрим на правильный пример формирования структуры выборки:
ВыборкаДанных = СоздатьОбъект("ТаблицаЗначений");
ВыборкаДанных.НоваяКолонка("Валюта");
ВыборкаДанных.НоваяКолонка("КодОперации");
ВыборкаДанных.НоваяКолонка("ДатаДок");
ВыборкаДанных.НоваяКолонка("НомерДок");
ВыборкаДанных.НоваяКолонка("Комментарий");
ВыборкаДанных.НоваяКолонка("Контрагент");
ВыборкаДанных.НоваяКолонка("Фирма");
ВыборкаДанных.НоваяКолонка("ПометкаУдаления");
ВыборкаДанных.НоваяКолонка("Проведен");
ВыборкаДанных.НоваяКолонка("ТабличнаяЧасть");
СпрКонтр = СоздатьОбъект("Справочник.Контрагенты");
СпрКонтр.ВыборГруппы(1);
Если СпрКонтр.Выбрать("Введите группу поставщиков", "ДляВыбора") = 1 Тогда
ВыбГруппа = СпрКонтр.ТекущийЭлемент();
Иначе
Возврат 0;
КонецЕсли;
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(Сформировать)
|Период с ДатаНачала по ДатаОкончания;
|Обрабатывать НеПомеченныеНаУдаление;
|ТекДок = Документ.ПоступлениеТМЦ.ТекущийДокумент;
|Контрагент = Документ.ПоступлениеТМЦ.Контрагент;
|Фирма = Документ.ПоступлениеТМЦ.Фирма;
|Группировка ТекДок;
|Условие(Контрагент в ВыбГруппа);
|Условие(Фирма = ОрганизацияПоУмолчанию);
|"//}}ЗАПРОС
;
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат 0;
КонецЕсли;
Пока Запрос.Группировка(1) = 1 Цикл
ТекДок = Запрос.ТекДок;
ВыборкаДанных.НоваяСтрока();
ВыборкаДанных.Валюта = ТекДок.Валюта;
ВыборкаДанных.КодОперации = ТекДок.КодОперации;
ВыборкаДанных.ДатаДок = ТекДок.ДатаДок;
ВыборкаДанных.НомерДок = ТекДок.НомерДок;
ВыборкаДанных.Комментарий = ТекДок.Комментарий;
ВыборкаДанных.Контрагент = ТекДок.Контрагент;
ВыборкаДанных.Фирма = ТекДок.Фирма;
ВыборкаДанных.ПометкаУдаления = 0;
ВыборкаДанных.Проведен = 1;
// Создаем НОВЫЙ объект ТаблицаЗначений для каждой строки
ТабличнаяЧасть = СоздатьОбъект("ТаблицаЗначений");
ТабличнаяЧасть.НоваяКолонка("Номенклатура");
ТабличнаяЧасть.НоваяКолонка("Количество");
ТабличнаяЧасть.НоваяКолонка("Цена");
ТабличнаяЧасть.НоваяКолонка("Сумма");
ТабличнаяЧасть.НоваяКолонка("СтавкаНДС");
ТабличнаяЧасть.НоваяКолонка("СуммаНДС");
ТекДок.ВыбратьСтроки();
Пока ТекДок.ПолучитьСтроку() = 1 Цикл
ТабличнаяЧасть.НоваяСтрока();
ТабличнаяЧасть.Номенклатура = ТекДок.Номенклатура;
ТабличнаяЧасть.Количество = ТекДок.Количество;
ТабличнаяЧасть.Цена = ТекДок.Цена;
ТабличнаяЧасть.Сумма = ТекДок.Сумма;
ТабличнаяЧасть.СтавкаНДС = ТекДок.СтавкаНДС;
ТабличнаяЧасть.СуммаНДС = ТекДок.СуммаНДС;
КонецЦикла;
ВыборкаДанных.ТабличнаяЧасть = ТабличнаяЧасть;
КонецЦикла;
Ручной сбор всех полей документа и его табличной части в структуру ТаблицаЗначений является трудоемким процессом и часто приводит к ошибкам сопоставления типов. Более гибкий и лаконичный путь решения задачи — обход выборки документов в обработчике ПВД с вызовом функции ВыгрузитьПоПравилу() для каждого найденного объекта.
Разберем преимущества этого подхода:
Посмотрим на пример кода в обработчике ПВД:
Запрос = СоздатьОбъект("Запрос");
// ... текст запроса с отбором документов ...
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
Пока Запрос.Группировка(1) = 1 Цикл
ТекДок = Запрос.ТекДок;
// Выгружаем документ по имени соответствующего ПКО
ВыгрузитьПоПравилу(ТекДок, , , , "ПоступлениеТМЦ");
КонецЦикла;
КонецЕсли;
Если по условиям обмена коллекция ВыборкаДанных должна оставаться таблицей значений, но флаг отключения кэширования использовать нельзя, мы можем явно задать уникальный ключ для каждой строки. Для этого в ПКО в обработчике «Перед выгрузкой» переопределим параметр КлючВыгружаемыхДанных, связав его с уникальными характеристиками строки:
// Формируем уникальный строковый идентификатор на основе номера и даты
КлючВыгружаемыхДанных = "" + Источник.НомерДок + "_" + Источник.ДатаДок;
Таким образом, обработка обмена сможет однозначно отличать строки выборки друг от друга, сохраняя механизм защиты от повторной выгрузки.