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