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