Как исправить ошибку преобразования XDTO при передаче данных между клиентом и сервером и открыть форму выбора с отбором в 1С?

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

В процессе разработки в среде 1С:Предприятие 8.3 на управляемых формах начинающие (и не только) специалисты часто сталкиваются со следующей системной ошибкой: «Ошибка передачи данных между клиентом и сервером. Ошибка преобразования данных XDTO: Отсутствует отображение для типа...». Особенно часто эта проблема возникает при попытке вернуть результат запроса или выборку строк из серверной процедуры на клиент.

В рамках этой статьи мы подробно разберем природу данной ошибки, выясним, почему платформа отказывается передавать определенные типы данных между контекстами, и пошагово реализуем правильный, оптимизированный механизм открытия формы выбора справочника с установленным динамическим отбором.

Данное руководство основано на реальном практическом кейсе: необходимо открыть форму выбора справочника Номенклатура с отбором по списку позиций, соответствующих определенной алкогольной продукции (АП) ЕГАИС, данные о которых хранятся в регистре сведений.

---

Причина ошибки: почему возникает сбой преобразования XDTO?

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

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

  1. Сериализуемые (простые и универсальные): Числа, Строки, Даты, Булево, Ссылки на объекты базы данных (например, СправочникСсылка.Номенклатура), а также коллекции: Массив, Структура, Соответствие, СписокЗначений (при условии, что их элементы тоже сериализуемы).
  2. Несериализуемые (серверные): Прикладные объекты баз данных (СправочникОбъект, ДокументОбъект), объекты запросов (Запрос, РезультатЗапроса, ВыборкаИзРезультатаЗапроса), а также таблицы значений (ТаблицаЗначений) и их строки (СтрокаТаблицыЗначений).
  3. Клиентские объекты: Элементы управления формой, описание оповещений и другие интерфейсные сущности.

В коде автора из примера функция на сервере выполняла следующий код:


РезультатЗапроса = Запрос.Выполнить().Выгрузить(); // Создается ТаблицаЗначений
ПараметрыОтбора = Новый Структура();
ПараметрыОтбора.Вставить("АлкогольнаяПродукция", СтрокаСАлкПрод);
НайденныеСтроки = РезультатЗапроса.НайтиСтроки(ПараметрыОтбора); // Возвращается Массив, содержащий СтрокиТаблицыЗначений
Возврат(НайденныеСтроки); // Пытаемся передать этот массив на клиент

Хотя сам по себе Массив отлично передается на клиент, его элементы внутри — это объекты типа СтрокаТаблицыЗначений. Данный тип не поддерживается на клиенте и не может быть упакован в XML-пакет. Платформа выдает исключение преобразования XDTO, сообщая разработчику: Отсутствует отображение для типа 'СтрокаТаблицыЗначений'.

---

Шаг 1. Оптимизация серверного запроса и подготовка данных

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

Рассмотрим первоначальный вариант запроса автора темы:


"ВЫБРАТЬ
|   алкСоответствияАлкогольнойПродукцииЕГАИСИНоменклатуры.Номенклатура КАК Номенклатура,
|   алкСоответствияАлкогольнойПродукцииЕГАИСИНоменклатуры.АлкогольнаяПродукция КАК АлкогольнаяПродукция
|ИЗ
|   РегистрСведений.алкСоответствияАлкогольнойПродукцииЕГАИСИНоменклатуры КАК алкСоответствияАлкогольнойПродукцииЕГАИСИНоменклатуры
|
|СГРУППИРОВАТЬ ПО
|   алкСоответствияАлкогольнойПродукцииЕГАИСИНоменклатуры.Номенклатура,
|   алкСоответствияАлкогольнойПродукцииЕГАИСИНоменклатуры.АлкогольнаяПродукция"

Этот запрос содержит несколько архитектурных неточностей:

  1. Использование секции СГРУППИРОВАТЬ ПО без применения агрегатных функций (например, СУММА или КОЛИЧЕСТВО) неэффективно. Для получения уникальных строк гораздо правильнее использовать ключевое слово РАЗЛИЧНЫЕ. Это облегчает понимание кода и оптимизирует план выполнения на СУБД.
  2. В запросе полностью отсутствует фильтрация в секции ГДЕ, хотя параметр Наименование устанавливается ниже в коде. Это привело бы к чтению всей таблицы регистра сведений в память сервера с последующей избыточной нагрузкой.
  3. Также обратите внимание на разыменование ссылочных полей. При обращении через точку вида АлкогольнаяПродукция.Ссылка платформа выполняет неявное левое соединение таблиц (LEFT JOIN) на уровне СУБД, что ухудшает производительность. Ссылочные поля нужно сравнивать напрямую без использования реквизита .Ссылка.

Исправим запрос и напишем эффективную серверную функцию, которая сразу будет возвращать на клиент сериализуемый СписокЗначений, наполненный готовыми ссылками на справочник Номенклатура.


&НаСервере
Функция ПолучитьСписокСоответствующейНоменклатуры(СсылкаНаАлкПрод)
    
    // Инициализируем запрос
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗЛИЧНЫЕ
        |   алкСоответствия.Номенклатура КАК Номенклатура
        |ИЗ
        |   РегистрСведений.алкСоответствияАлкогольнойПродукцииЕГАИСИНоменклатуры КАК алкСоответствия
        |ГДЕ
        |   алкСоответствия.АлкогольнаяПродукция = &АлкогольнаяПродукция";
    
    // Передаем ссылку на алкогольную продукцию напрямую, без разыменования через точку
    Запрос.УстановитьПараметр("АлкогольнаяПродукция", СсылкаНаАлкПрод);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    // Создаем СписокЗначений — это сериализуемый тип, который легко передается на клиент
    СписокНоменклатуры = Новый СписокЗначений();
    
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Пока Выборка.Следующий() Цикл
            // Добавляем Ссылку на Номенклатуру в наш список значений
            СписокНоменклатуры.Добавить(Выборка.Номенклатура);
        КонецЦикла;
    КонецЕсли;
    
    Возврат СписокНоменклатуры;
    
КонецФункции

Важное примечание: Если список значений покажется вам избыточным, вы можете использовать обычный Массив. Массивы ссылок также прекрасно сериализуются платформой и могут служить источником для динамического отбора в управляемой форме.

---

Шаг 2. Передача отбора и программное открытие формы выбора

Теперь спроектируем клиентскую процедуру, которая будет вызываться при начале выбора в поле ввода номенклатуры. Наша задача — получить список разрешенных к выбору элементов с сервера, сформировать структуру параметров отбора и корректно открыть форму выбора.

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

Если мы передаем в качестве значения отбора по полю "Ссылка" одиночную ссылку, платформа отфильтрует список по равенству. Если же мы передаем СписокЗначений или Массив, платформа автоматически применит условие сравнения «В списке».

Проанализируем готовую клиентскую процедуру:


&НаКлиенте
Процедура Бит_ТоварыНоменклатураНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    
    // Получаем текущую строку табличной части
    ТекДанные = ЭтаФорма.ТекущийЭлемент.ТекущиеДанные;
    Если ТекДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Извлекаем значение алкогольной продукции из строки
    СтрокаСАлкПрод = ТекДанные.АлкогольнаяПродукция;
    
    // Отменяем стандартную обработку события начала выбора, 
    // чтобы платформа не открыла стандартную форму без наших отборов
    СтандартнаяОбработка = Ложь;
    
    // Вызываем серверную функцию для получения СпискаЗначений с номенклатурой
    СписокНоменклатуры = ПолучитьСписокСоответствующейНоменклатуры(СтрокаСАлкПрод);
    
    // Формируем структуру отбора для динамического списка формы выбора
    // Поле "Ссылка" должно быть в списке "СписокНоменклатуры"
    СтруктураОтбора = Новый Структура("Ссылка", СписокНоменклатуры);
    
    // Помещаем отбор в параметры открытия формы
    ПараметрыФормы = Новый Структура("Отбор", СтруктураОтбора);
    
    // Открываем форму выбора справочника Номенклатура
    // Третьим параметром передаем Элемент (поле ввода формы), который инициировал выбор.
    // Это критически важно для автоматического возврата выбранного значения назад в поле!
    ОткрытьФорму("Справочник.Номенклатура.Форма.ФормаВыбора", ПараметрыФормы, Элемент);
    
КонецПроцедуры

---

Разбор важных нюансов реализации

При тестировании этой схемы разработчики часто сталкиваются с неочевидным поведением платформы. Разберем ключевые тонкости, которые были выявлены в процессе обсуждения на форуме.

1. Почему форма выбора открывается пустой?

Если после выполнения кода форма выбора открывается абсолютно пустой, без элементов, обязательно проверьте, что именно вы добавляете в СписокЗначений на сервере. Самая частая ошибка — добавление строковых представлений (наименований) вместо реальных ссылок. В динамическом списке поле Ссылка имеет тип СправочникСсылка.Номенклатура. Если передать туда список строковых значений (например, наименований), платформа попытается сравнить ссылки со строками, отбор не найдет соответствий, и форма откроется пустой.

2. Как правильно указать путь к форме выбора?

В методе ОткрытьФорму первым параметром передается полное имя формы. Обратите внимание на разницу в написании путей:

Если в вашей конфигурации переопределен глобальный обработчик получения форм ОбработкаПолученияФормы в модуле менеджера справочника, использование первого варианта может приводить к тому, что платформа перехватит вызов и подменит вашу форму на другую (например, форму выбора групп и элементов). Использование явного пути через .Форма.ИмяФормы позволяет гарантированно открыть именно ту форму, которую вы спроектировали.

3. Зачем передавать владельца формы выбора?

Если вызвать метод ОткрытьФорму следующим образом:

ОткрытьФорму("Справочник.Номенклатура.Форма.ФормаВыбора", ПараметрыФормы);

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


ОткрытьФорму("Справочник.Номенклатура.Форма.ФормаВыбора", ПараметрыФормы, Элемент);

---

Альтернативное решение: Быстрый выбор без открытия тяжелых окон (UX-оптимизация)

С точки зрения интерфейса и удобства пользователя (UX/UI), открытие целого модального или блокирующего окна справочника ради выбора из 2–3 позиций номенклатуры — не самое изящное решение. Пользователю приходится совершать лишние клики, ждать прорисовки окна и переключать внимание.

Платформа 1С:Предприятие предлагает замечательную альтернативу — компактное выпадающее меню выбора прямо под полем ввода. Для этого мы можем использовать асинхронный метод ПоказатьВыборИзСписка(). Реализуем этот вариант:


&НаКлиенте
Процедура Бит_ТоварыНоменклатураНачалоВыбораАльтернатива(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    
    ТекДанные = ЭтаФорма.ТекущийЭлемент.ТекущиеДанные;
    Если ТекДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    СтрокаСАлкПрод = ТекДанные.АлкогольнаяПродукция;
    СтандартнаяОбработка = Ложь;
    
    // Получаем список значений с сервера
    СписокНоменклатуры = ПолучитьСписокСоответствующейНоменклатуры(СтрокаСАлкПрод);
    
    // Если список пустой — сообщаем пользователю
    Если СписокНоменклатуры.Количество() = 0 Тогда
        ПоказатьПредупреждение(, "Для данной алкогольной продукции нет сопоставленной номенклатуры в регистре!");
        Возврат;
    КонецЕсли;
    
    // Описываем обработчик оповещения, который сработает после выбора пункта меню
    ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВыбораИзСпискаНоменклатуры", ЭтотОбъект);
    
    // Показываем лаконичный выпадающий список у элемента формы
    ПоказатьВыборИзСписка(ОписаниеОповещения, СписокНоменклатуры, Элемент);
    
КонецПроцедуры

&НаКлиенте
Процедура ПослеВыбораИзСпискаНоменклатуры(ВыбранныйЭлемент, ДополнительныеПараметры) Экспорт
    
    // Если пользователь отменил выбор или кликнул мимо списка
    Если ВыбранныйЭлемент = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Записываем выбранную ссылку напрямую в данные текущей строки
    ТекДанные = ЭтаФорма.ТекущийЭлемент.ТекущиеДанные;
    Если ТекДанные <> Неопределено Тогда
        ТекДанные.Номенклатура = ВыбранныйЭлемент.Значение;
    КонецЕсли;
    
КонецПроцедуры

Такой подход делает работу пользователя в разы быстрее, избавляя от мерцания экрана и открытия лишних окон. Рекомендуем использовать его во всех сценариях, когда заведомо известно, что количество вариантов выбора не превышает 10–15 позиций.

---

Резюме по результатам разбора

Мы детально изучили причины возникновения ошибки преобразования данных XDTO и выработали надежные механизмы работы с отборами в управляемых формах 1С 8.3. Основные выводы, которые необходимо запомнить программисту 1С:

← На главную