При настройке правил обмена в программе 1С:Конвертация данных 2.0 (2.1) разработчики часто сталкиваются со сложными задачами трансформации данных. Одна из классических задач — заполнение табличной части документа-приемника (например, ТЧ Услуги в документе РеализацияТоваровУслуг) на основе данных, которые в базе-источнике хранятся не в аналогичной табличной части, а как отдельные элементы справочника или результат произвольного запроса.
В этой статье мы подробно разберем три профессиональных метода решения этой задачи, проанализируем типичные ошибки, с которыми сталкиваются разработчики, и посмотрим на практические примеры кода.
КоллекцияОбъектов в ПКГСЭтот способ является самым простым, изящным и рекомендуемым. Он применяется, когда сам документ-приемник выгружается стандартным образом, но его табличную часть необходимо сформировать динамически с помощью запроса к базе данных источника.
Разберем этот метод подробно по шагам:
РеализацияТоваровУслуг).Перед обработкой для этого ПКГС. Именно здесь мы напишем алгоритм, который выберет нужные элементы справочника.ТаблицуЗначений и присвоим ее специальной предопределенной переменной КоллекцияОбъектов.Рассмотрим подробнее пример кода в обработчике Перед обработкой ПКГС:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура,
| 1 КАК Количество,
| 100 КАК Цена,
| 100 КАК Сумма
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Родитель.Наименование = &НаименованиеПапки";
Запрос.УстановитьПараметр("НаименованиеПапки", "Консультационные услуги");
// Результат запроса выгружаем в Таблицу Значений и передаем КД
КоллекцияОбъектов = Запрос.Perform().Unload(); // Запрос.Выполнить().Выгрузить();
Важное правило сопоставления полей: Обратите внимание на псевдонимы колонок в запросе (Номенклатура, Количество, Цена, Сумма). Они должны в точности совпадать с именами реквизитов табличной части документа-приемника. Внутри ПКГС для каждого реквизита табличной части создаются обычные правила конвертации свойств (ПКС). Поля Источник в этих ПКС оставляются пустыми, а в поле Приемник указывается соответствующий реквизит. Конвертация данных автоматически сопоставит колонки из таблицы КоллекцияОбъектов с реквизитами приемника по совпадению имен. Никаких галочек "Получить из входящих данных" при этом ставить не требуется!
ВходящиеДанные через ВыгрузитьПоПравилуЭтот подход применяется в тех случаях, когда инициатором выгрузки выступает не стандартная выборка объектов, а произвольный алгоритм в Правилах выгрузки данных (ПВД). Например, когда нам нужно сгенерировать документ «из воздуха», основываясь исключительно на результатах сложного запроса.
Проанализируем типичную ошибку, которую часто допускают разработчики на этом этапе (и с которой столкнулся автор обсуждаемой темы). Попытка вызывать функцию ВыгрузитьПоПравилу в цикле по строкам запроса:
// НЕПРАВИЛЬНЫЙ ПОДХОД, КОТОРЫЙ ПРИВЕДЕТ К КРИТИЧЕСКИМ ОШИБКАМ
Для каждого СтрокаВыборки Из ТаблицаРезультатов Цикл
ВыгрузитьПоПравилу(,, СтрокаВыборки,, "РеализацияТоваровУслуг");
// Платформа пытается создать НОВЫЙ документ на каждую строку табличной части!
КонецЦикла;
Почему этот код вызывает ошибки? Потому что функция ВыгрузитьПоПравилу при таком вызове пытается создать отдельный документ на каждую строчку вашей таблицы результатов! Так как в строке таблицы нет данных для шапки документа (номера, даты, организации), КД выдает серьезные ошибки объектной структуры.
Выясним, как реализовать этот метод корректно с использованием механизма ВходящиеДанные:
ТаблицыЗначений и вкладываем её в структуру входящих данных под ключом, имя которого совпадает с именем табличной части в приемнике.ВыгрузитьПоПравилу ровно один раз для одного документа.Рассмотрим пример правильной реализации в ПВД:
// 1. Создаем таблицу значений для будущей табличной части документа-приемника
ТЗ_Услуги = Новый ТаблицаЗначений;
ТЗ_Услуги.Колонки.Добавить("Номенклатура");
ТЗ_Услуги.Колонки.Добавить("Количество");
ТЗ_Услуги.Колонки.Добавить("Цена");
ТЗ_Услуги.Колонки.Добавить("Сумма");
// Заполняем её данными из нашего запроса к справочнику-источнику
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Ссылка Из Справочник.Номенклатура ГДЕ ...";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НоваяСтрока = ТЗ_Услуги.Добавить();
НоваяСтрока.Номенклатура = Выборка.Ссылка;
НоваяСтрока.Количество = 1;
НоваяСтрока.Цена = 500;
НоваяСтрока.Сумма = 500;
КонецЦикла;
// 2. Упаковываем данные ТЧ и шапки в структуру ВходящиеДанные
ВходящиеДанныеДокумента = Новый Структура;
ВходящиеДанныеДокумента.Вставить("Услуги", ТЗ_Услуги); // Имя ключа строго совпадает с именем ТЧ приемника
ВходящиеДанныеДокумента.Вставить("Дата", ТекущаяДата());
ВходящиеДанныеДокумента.Вставить("Контрагент", СсылкаНаКонтрагента);
// 3. Выгружаем документ ОДИН раз, передавая структуру в качестве третьего параметра
ВыгрузитьПоПравилу(,, ВходящиеДанныеДокумента,, "РеализацияТоваровУслуг");
Настройка ПКО в Конвертации данных: Чтобы этот алгоритм отработал без ошибок, в Правилах конвертации объектов (ПКО) для табличной части Услуги и всех её подчиненных реквизитов (ПКС) необходимо установить флаг «Получить из входящих данных». В этом случае КД поймет, что информацию не нужно искать в объекте-источнике, а следует извлечь из переданной нами структуры.
ПослеЗагрузкиИногда возникают ситуации, когда собирать данные на этапе выгрузки нерационально (например, база-источник не содержит нужных аналитик для сопоставления, которые есть только в базе-приемнике). В этом случае логику заполнения табличной части можно полностью перенести на сторону базы-приемника.
Этот метод реализуется в обработчике события После загрузки в ПКО документа. На данном этапе объект уже создан в оперативной памяти базы-приемника, но еще не записан в базу данных.
Разберем пример кода для обработчика После загрузки:
// Объект — это готовый к записи документ в базе-приемнике
// Выполним поиск нужной услуги непосредственно в приемнике по коду или наименованию
СсылкаНаНоменклатуру = Справочники.Номенклатура.НайтиПоНаименованию("Консультационные услуги");
Если ЗначениеЗаполнено(СсылкаНаНоменклатуру) Тогда
// Добавляем строку в табличную часть документа
НоваяСтрока = Объект.Услуги.Добавить();
НоваяСтрока.Номенклатура = СсылкаНаНоменклатуру;
НоваяСтрока.Количество = 1;
НоваяСтрока.Цена = 1000;
НоваяСтрока.Сумма = 1000;
КонецЕсли;
Плюсы и минусы метода: Этот способ предельно прост в написании, однако у него есть существенный недостаток: теряется гибкость платформы 1С:Конвертация данных. Логика поиска элементов жестко кодируется на языке 1С базы-приемника, игнорируя стандартные правила поиска и сопоставления полей, настроенные в КД.
Проанализируем, почему у автора темы возникали "страшные ошибки при выгрузке". Главная причина — конфликт между источником данных и флагом "Получить из входящих данных". Если у свойства ТЧ в ПКС указан Источник, но при этом включен флаг «Получить из входящих данных» (или наоборот, незаполненный источник без этого флага), КД начинает искать реквизит в переданной структуре, не находит его и возвращает значение Неопределено, что приводит к ошибкам сопоставления типов при загрузке.
Также важно помнить, что при программном создании объектов с помощью ПВД и функции ВыгрузитьПоПравилу, у вас обязательно должны быть настроены правила поиска (например, по полям Номер и Дата), иначе при каждой синхронизации система будет создавать новые дублирующиеся документы.
Рекомендуем использовать первый метод с переменной КоллекцияОбъектов как основной и наиболее надежный стандарт разработки в 1С:Конвертация данных 2.0!