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