Как добавить элемент в список XDTO, избегая ошибки "Поле объекта не обнаружено (Добавить)"

Программист 1С v8.3 (Управляемые формы)
← На главную

При работе с объектами XDTO в 1С:Предприятии мы часто сталкиваемся с необходимостью формировать сложные структуры данных, в том числе списки. Одной из распространенных задач является добавление элементов в свойство, которое по схеме XDTO определено как список. Однако, на этом этапе многие разработчики сталкиваются с ошибкой "Поле объекта не обнаружено (Добавить)". Давайте подробно разберем, почему возникает эта ошибка и как ее эффективно решить, используя различные подходы.

В чем причина ошибки "Поле объекта не обнаружено (Добавить)"?

Когда мы создаем родительский объект XDTO при помощи ФабрикаXDTO.Создать(), его свойства, которые должны представлять собой коллекции (списки), изначально не инициализированы и имеют значение Неопределено. Попытка вызвать метод Добавить() у свойства, которое еще не является объектом типа СписокXDTO, приводит к ошибке "Поле объекта не обнаружено". Метод Добавить() существует у объекта СписокXDTO, но не у Неопределено.

Подход 1: Неявная инициализация списка при добавлении первого элемента

Это решение оказалось рабочим в рассматриваемой ситуации и демонстрирует интересную особенность платформы 1С. Разберем его по шагам.

Суть подхода заключается в том, чтобы корректно создать сам элемент, который мы собираемся добавить в список, а затем вызвать метод Добавить() у списочного свойства. Платформа в этом случае автоматически инициализирует свойство как СписокXDTO и добавит туда элемент.

  1. Создание родительского объекта и заполнение простых свойств:

    
    тСвСчФакт = ФабрикаXDTO.Создать(тДокумент.Свойства.Получить("СвСчФакт").Тип);
    тСвСчФакт.НомерСчФ = Объект.РасходнаяНакладная.Номер;
    тСвСчФакт.ДатаСчФ = Формат(Объект.РасходнаяНакладная.Дата, "ДЛФ=D");
    тСвСчФакт.КодОКВ = "638";
    
  2. Создание элемента, который будет помещен в список:

    Ключевой момент здесь — правильно определить тип элемента списка. Для этого мы можем использовать ВладеющееСвойство.Тип. Это позволяет получить тип объекта, который "владеет" текущим свойством списка, и таким образом определить, какой тип элементов должен быть в этом списке.

    тСвПрод = ФабрикаXDTO.Создать(тСвСчФакт.СвПрод.ВладеющееСвойство.Тип);

    Далее мы создаем и заполняем вложенные объекты этого элемента:

    
    тИдСв = ФабрикаXDTO.Создать(тСвПрод.Свойства.Получить("ИдСв").Тип);
    тСвЮЛУч = ФабрикаXDTO.Создать(тИдСв.Свойства.Получить("СвЮЛУч").Тип);
    тСвЮЛУч.НаимОрг = Объект.РасходнаяНакладная.Организация.НаименованиеДляПечати;
    тСвЮлУч.ИННЮЛ = Объект.РасходнаяНакладная.Организация.ИНН;
    тСвЮлУч.КПП = Объект.РасходнаяНакладная.Организация.КПП;
    тИдСв.СвЮЛУч = тСвЮЛУч;
    
    тАдрес = ФабрикаXDTO.Создать(тСвПрод.Свойства.Получить("Адрес").Тип);
    тАдрРФ = ФабрикаXDTO.Создать(тАдрес.Свойства.Получить("АдрРФ").Тип);
    тАдрРФ.Индекс = "630058";
    тАдрРФ.КодРегион = "54";
    тАдрРФ.Город = "Новосибирск";
    тАдрРФ.Улица = "Тихая";
    тАдрРФ.Дом = "22";
    тАдрРФ.Кварт = "11";
    тАдрес.АдрРФ = тАдрРФ;
    тСвПрод.Адрес = тАдрес;
    тСвПрод.ИдСВ = тИдСв;
    
  3. Добавление элемента в список:

    Теперь, когда тСвПрод (элемент списка) полностью сформирован, мы можем добавить его в свойство СвПрод родительского объекта тСвСчФакт. В этот момент 1С автоматически инициализирует тСвСчФакт.СвПрод как СписокXDTO.

    тСвСчФакт.СвПрод.Добавить(тСвПрод);

    Строка тСвСчФакт.СвПрод[0] = тСвПрод;, которая также присутствовала в рабочем коде автора, в большинстве случаев является избыточной, так как метод Добавить() уже помещает элемент в список.

Подход 2: Явная инициализация списка перед добавлением элементов

Этот способ является более явным и структурированным, давая полный контроль над процессом. Мы сначала создаем сам объект списка, наполняем его элементами, а затем присваиваем готовый список свойству родительского объекта.

  1. Создание родительского объекта:

    
    тСвСчФакт = ФабрикаXDTO.Создать(тДокумент.Свойства.Получить("СвСчФакт").Тип);
    // ... заполняем его простые свойства ...
    
  2. Получение типа элемента списка:

    Мы используем свойство ТипЭлементаСписка у типа свойства-списка, чтобы получить тип элементов, которые будут в нем содержаться.

    ТипЭлементаСписка = тСвСчФакт.Свойства.Получить("СвПрод").Тип.ТипЭлементаСписка;
  3. Создание и заполнение элементов списка:

    Теперь создаем каждый элемент списка по его типу и заполняем его свойства. Это может происходить, например, в цикле, если данные для списка берутся из таблицы или другой коллекции.

    
    // Предположим, у нас есть источник данных, например, табличная часть документа
    Для Каждого СтрокаТаблицы Из Объект.РасходнаяНакладная.СписокПоставщиков Цикл
        тСвПрод_Элемент = ФабрикаXDTO.Создать(ТипЭлементаСписка);
    
        тИдСв = ФабрикаXDTO.Создать(тСвПрод_Элемент.Свойства.Получить("ИдСв").Тип);
        тСвЮЛУч = ФабрикаXDTO.Создать(тИдСв.Свойства.Получить("СвЮЛУч").Тип);
        тСвЮЛУч.НаимОрг = СтрокаТаблицы.Организация.НаименованиеДляПечати;
        тСвЮЛУч.ИННЮЛ = СтрокаТаблицы.Организация.ИНН;
        тСвЮЛУч.КПП = СтрокаТаблицы.Организация.КПП;
        тИдСв.СвЮЛУч = тСвЮЛУч;
        тСвПрод_Элемент.ИдСВ = тИдСв;
    
        тАдрес = ФабрикаXDTO.Создать(тСвПрод_Элемент.Свойства.Получить("Адрес").Тип);
        тАдрРФ = ФабрикаXDTO.Создать(тАдрес.Свойства.Получить("АдрРФ").Тип);
        тАдрРФ.Индекс = СтрокаТаблицы.Индекс; // Пример, данные из источника
        тАдрРФ.КодРегион = СтрокаТаблицы.КодРегион;
        тАдрРФ.Город = СтрокаТаблицы.Город;
        тАдрРФ.Улица = СтрокаТаблицы.Улица;
        тАдрРФ.Дом = СтрокаТаблицы.Дом;
        тАдрРФ.Кварт = СтрокаТаблицы.Кварт;
        тАдрес.АдрРФ = тАдрРФ;
        тСвПрод_Элемент.Адрес = тАдрес;
    
        // Добавляем готовый элемент в список
        тСвСчФакт.СвПрод.Добавить(тСвПрод_Элемент);
    КонецЦикла;
    

    В этом случае, при первом вызове Добавить(), платформа также неявно инициализирует свойство СвПрод как СписокXDTO. Если же мы хотим явно создать сам объект списка перед тем, как наполнять его, можно сделать так:

    
    // 1. Получаем тип самого свойства-списка
    ТипСвойстваСписка = тСвСчФакт.Свойства.Получить("СвПрод").Тип;
    // 2. Создаем пустой объект списка
    МойСписокXDTO = ФабрикаXDTO.Создать(ТипСвойстваСписка);
    
    // 3. Создаем и добавляем в него элементы
    ТипЭлементаСписка = ТипСвойстваСписка.ТипЭлементаСписка;
    Для Каждого СтрокаТаблицы Из Объект.РасходнаяНакладная.СписокПоставщиков Цикл
        ЭлементСписка = ФабрикаXDTO.Создать(ТипЭлементаСписка);
        // ... заполняем ЭлементСписка ... (как показано выше для тСвПрод_Элемент)
        МойСписокXDTO.Добавить(ЭлементСписка);
    КонецЦикла;
    
    // 4. Присваиваем готовый список свойству родительского объекта
    тСвСчФакт.СвПрод = МойСписокXDTO;
    

    Этот вариант, хотя и более многословный, дает самый полный контроль над созданием объекта списка и его содержимого, что может быть предпочтительнее для очень сложных или динамически формируемых XDTO-структур. Для упрощения программного формирования подобных XML-структур можно использовать фреймворк для генерации XML в 1С.

Заключение

Мы выяснили, что ошибка "Поле объекта не обнаружено (Добавить)" при работе со списками XDTO в 1С связана с тем, что свойство-список не было инициализировано. Существует два основных подхода к решению этой проблемы: неявная инициализация при первом вызове Добавить() с корректно созданным элементом, или явное создание самого объекта СписокXDTO перед его заполнением и присвоением. Оба метода эффективны, выбор конкретного подхода зависит от предпочтений разработчика и сложности формируемой структуры.

← На главную