Как передать структуру с сервера на клиент для заполнения нового документа в 1С

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

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

Однако при попытке передать с сервера на клиент структуру, содержащую серверные объекты (такие как ТаблицаЗначений или РезультатЗапроса), платформа выдает ошибку: «Ошибка передачи данных между клиентом и сервером. Значение недопустимого типа» по причине ошибки преобразования XDTO.

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

Выясним причину ошибки передачи данных

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

Кроме того, попытка вызвать функцию ПолучитьИзВременногоХранилища() на клиенте для получения объекта типа ТаблицаЗначений также приведет к ошибке, так как клиентский контекст не умеет работать с подобными типами.

Способ 1. Использование временного хранилища и параметров формы (Рекомендуемый подход)

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

Разберем по шагам реализацию этого способа:

  1. На сервере формируем необходимые данные (структуру, таблицу значений и т. д.) и помещаем их во временное хранилище. Важно привязать данные к уникальному идентификатору формы, чтобы они не были удалены сборщиком мусора платформы.
  2. Возвращаем адрес хранилища на клиент.
  3. На клиенте формируем структуру параметров для формы и передаем в нее полученный адрес.
  4. Вызываем ОткрытьФорму().
  5. В модуле объекта нового документа в процедуре ОбработкаЗаполнения() или в модуле его формы в процедуре ПриСозданииНаСервере() извлекаем данные из хранилища и заполняем реквизиты документа.

Рассмотрим пример кода исходной обработки на клиенте и сервере:


&НаКлиенте
Процедура СоздатьИОткрытьДокумент()
    
    // Получаем адрес хранилища с данными, сформированными на сервере
    АдресХранилища = СформироватьДанныеНаСервере();
    
    // Передаем адрес во временных параметрах формы
    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("АдресДанныхЗаполнения", АдресХранилища);
    
    ОткрытьФорму("Документ.МойДокумент.ФормаОбъекта", ПараметрыФормы);
    
КонецПроцедуры

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

Теперь рассмотрим, как подхватить эти данные в модуле нового документа МойДокумент в процедуре ОбработкаЗаполнения:


// Модуль объекта "Документ.МойДокумент"
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
    
    Если ТипЗнч(ДанныеЗаполнения) = Тип("Структура") 
        И ДанныеЗаполнения.Свойство("АдресДанныхЗаполнения") Тогда
        
        // Получаем данные строго на сервере
        КонтекстДанных = ПолучитьИзВременногоХранилища(ДанныеЗаполнения.АдресДанныхЗаполнения);
        
        Если ТипЗнч(КонтекстДанных) = Тип("Структура") Тогда
            
            Если КонтекстДанных.Свойство("Дата") Тогда
                ЭтотОбъект.Дата = КонтекстДанных.Дата;
            КонецЕсли;
            
            Если КонтекстДанных.Свойство("Товары") Тогда
                ЭтотОбъект.Товары.Загрузить(КонтекстДанных.Товары);
            КонецЕсли;
            
        КонецЕсли;
        
        // Очищаем временное хранилище после использования
        УдалитьИзВременногоХранилища(ДанныеЗаполнения.АдресДанныхЗаполнения);
        
    КонецЕсли;
    
КонецПроцедуры

Способ 2. Конвертация таблицы значений в массив структур из примитивных типов

Если по каким-то причинам использование временного хранилища неудобно, а объемы передаваемых данных небольшие, можно обойтись без хранилища. Для этого необходимо преобразовать ТаблицаЗначений в Массив, состоящий из элементов типа Структура, где значениями выступают примитивные типы (числа, строки, булево) или ссылки на объекты базы данных (СправочникСсылка, ДокументСсылка).

Массивы и структуры, содержащие только примитивные типы и ссылки, являются сериализуемыми и легко передаются между сервером и клиентом.

Проанализируем пример кода конвертации таблицы значений:


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

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

Способ 3. Заполнение через подготовку объекта формы на сервере

Существует еще один альтернативный подход: мы можем заранее получить форму на клиенте, передать её контекст на сервер, заполнить объект формы напрямую через приведение к ДокументОбъект и затем открыть форму.

Рассмотрим, как выглядит этот процесс:


&НаКлиенте
Процедура ОткрытьИЗаполнитьФормуНапрямую()
    
    // Получаем форму нового документа
    ФормаДокумента = ПолучитьФорму("Документ.МойДокумент.ФормаОбъекта");
    
    // Заполняем данные формы на сервере
    ЗаполнитьФормуНаСервере(ФормаДокумента);
    
    // Открываем подготовленную форму
    ФормаДокумента.Открыть();
    
КонецПроцедуры

&НаСервере
Процедура ЗаполнитьФормуНаСервере(ФормаДокумента)
    
    // Преобразуем данные формы в объект документа
    ДокументОбъект = ДанныеФормыВЗначение(ФормаДокумента.Объект, Тип("ДокументОбъект.МойДокумент"));
    
    // Выполняем любые манипуляции на сервере
    ДокументОбъект.Дата = ТекущаяДата();
    
    // Например, заполняем табличную часть результатом запроса
    Запрос = Новый Запрос("ВЫБРАТЬ Номенклатура, Количество ИЗ Справочник.Номенклатура");
    ТаблицаРезультат = Запрос.Выполнить().Выгрузить();
    
    ДокументОбъект.Товары.Загрузить(ТаблицаРезультат);
    
    // Возвращаем измененные данные обратно в форму
    ЗначениеВДанныеФормы(ДокументОбъект, ФормаДокумента.Объект);
    
КонецПроцедуры

Резюме

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

← На главную