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