При настройке обмена данными между конфигурациями 1С:ERP и 1С:УПП с помощью «Конвертации данных 2.0» разработчики часто сталкиваются с необходимостью не просто перенести справочник Номенклатура, но и организовать его структуру в базе-приемнике определенным образом. Типичная задача — поместить всю загружаемую номенклатуру в одну общую папку (например, «Номенклатура ЕРП»), сохранив при этом всю внутреннюю иерархию подпапок и элементов. Однако некорректное использование обработчиков событий может привести к трудночитаемым ошибкам среды выполнения.
В этой статье мы подробно разберем, почему возникает ошибка «Получение элемента по индексу для значения не определено», проанализируем механизмы работы с параметрами объекта и выработаем надежный алгоритм переноса иерархических данных.
Ошибка «Получение элемента по индексу для значения не определено» в контексте универсального обмена данными в формате XML обычно указывает на то, что в программном коде происходит обращение к переменной, которая имеет значение Неопределено, как к коллекции (массиву, структуре или соответствию). Рассмотрим ситуацию, когда мы работаем в обработчике ПослеЗагрузкиОбъекта.
Чаще всего это происходит при попытке получить данные из коллекции ПараметрыОбъекта. Если параметр не был передан из базы-источника или не был инициализирован, обращение к нему по ключу может вызвать исключение, особенно если к результату сразу применяется какой-либо метод. Выясним причину: в модуле обработки УниверсальныйОбменДаннымиXML на строке, указанной в логе, выполняется код, который ожидает структуру, но получает пустое значение. Проанализируем типичный ошибочный код:
// Ошибочный вариант обращения
РодительИзПараметра = ПараметрыОбъекта["НужнаяПапка"];
// Если "НужнаяПапка" не передана, РодительИзПараметра станет Неопределено
Объект.Родитель = РодительИзПараметра;
Чтобы избежать подобных ситуаций, всегда следует проверять наличие ключа в структуре параметров с помощью метода Свойство() или убеждаться, что коллекция ПараметрыОбъекта сама по себе не является Неопределено.
Для того чтобы вся номенклатура попала в папку «Номенклатура ЕРП» и при этом не «схлопнулась» в один уровень, нужно соблюдать важное правило: менять родителя следует только у тех объектов, которые в исходной базе находились в корне справочника. Если вы принудительно назначите нового родителя для каждого элемента, то все вложенные папки потеряют свои связи, и вы получите «плоский» список. Рассмотрим пошаговое решение задачи.
Постоянный поиск или создание папки в обработчике каждого объекта — это ресурсоемкая операция, которая сильно замедлит загрузку. Правильнее будет найти или создать нужную папку один раз в самом начале обмена. Для этого используем обработчик «Перед загрузкой данных» в настройках конвертации.
// В обработчике "Перед загрузкой данных"
ПапкаИмя = "Номенклатура ЕРП";
СсылкаНаПапку = Справочники.Номенклатура.НайтиПоНаименованию(ПапкаИмя, Истина);
Если СсылкаНаПапку.Пустая() Тогда
НоваяПапка = Справочники.Номенклатура.СоздатьГруппу();
НоваяПапка.Наименование = ПапкаИмя;
НоваяПапка.Записать();
СсылкаНаПапку = НоваяПапка.Ссылка;
КонецЕсли;
// Сохраняем ссылку в глобальные параметры, чтобы обращаться к ней позже
Параметры.Вставить("ГлобальнаяПапкаЕРП", СсылкаНаПапку);
Теперь перейдем к обработчику «ПослеЗагрузкиОбъекта» для правила конвертации объектов (ПКО) Номенклатура. Здесь нам нужно проверить, является ли загруженный объект «корневым» с точки зрения иерархии источника. Выясним ситуацию: если у объекта в приемнике Родитель пуст, значит, его нужно поместить в нашу созданную папку.
// В обработчике "ПослеЗагрузкиОбъекта" для ПКО Номенклатура
// Проверяем, не является ли сам загружаемый объект нашей целевой папкой
// чтобы избежать зацикливания иерархии
Если Объект.Ссылка = Параметры.ГлобальнаяПапкаЕРП Тогда
Возврат;
КонецЕсли;
// Устанавливаем родителя только для тех, кто находится в корне
Если Объект.Родитель.Пустая() Тогда
Объект.Родитель = Параметры.ГлобальнаяПапкаЕРП;
// Не забываем записать объект, так как ПослеЗагрузкиОбъекта
// подразумевает, что объект уже может быть подготовлен к записи или записан
// В зависимости от настроек обмена может потребоваться явный вызов Записать()
Объект.Записать();
КонецЕсли;
Если вы все же решили передавать информацию о родителе через ПараметрыОбъекта (например, из ПВД или перед выгрузкой), проанализируем безопасный способ извлечения данных. Чтобы не возникло ошибки «Получение элемента по индексу», используем следующий подход:
Если ПараметрыОбъекта <> Неопределено Тогда
СсылкаНаРодителя = Неопределено;
Если ПараметрыОбъекта.Свойство("ИмяНужногоПараметра", СсылкаНаРодителя) Тогда
Если ЗначениеЗаполнено(СсылкаНаРодителя) И Объект.Родитель.Пустая() Тогда
Объект.Родитель = СсылкаНаРодителя;
КонецЕсли;
КонецЕсли;
КонецЕсли;
ПослеЗагрузки крайне важно учитывать, что объект уже может быть записан в базу данных. Если вы меняете реквизиты объекта (в данном случае Родитель), необходимо вызвать метод Объект.Записать(). Однако будьте осторожны: если в ПКО включен режим «Не замещать существующие объекты», повторная запись может нарушить логику обмена.Номенклатура родителем может быть только группа (если иерархия групп и элементов) или другой элемент (если иерархия элементов). В УПП и ERP используется иерархия групп и элементов.Конфигураторе. Остановка по ошибке точно укажет на проблемную строку кода в сгенерированном модуле.Проанализировав ситуацию, мы видим, что ключ к решению задачи — это сочетание глобальной инициализации целевой папки и аккуратной проверки текущего состояния иерархии объекта. Это гарантирует отсутствие ошибок обращения к коллекциям и сохраняет целостность данных при переносе из ERP в УПП.