Как правильно передать Таблицу Значений с клиента на сервер в 1С?

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

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

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

Понимаем суть проблемы: Клиент-серверная архитектура

Прежде чем приступать к написанию кода, давайте разберемся в теории. Управляемое приложение 1С разделяет логику на клиентскую (то, что происходит в браузере или тонком клиенте) и серверную (где выполняются запросы к базе данных и сложные вычисления). При проектировании вызовов важно учитывать особенности передачи параметров процедур и использования "Знач".

Объект ТаблицаЗначений является "тяжелым" объектом, который существует полноценно только на сервере (или в толстом клиенте). На тонком клиенте этого типа данных просто не существует. Платформа защищает нас от передачи огромных объемов данных по сети, запрещая прямую передачу таких объектов в параметрах серверных процедур.

Давайте рассмотрим ситуацию, когда у нас есть некоторая таблица, сформированная на клиенте (например, загруженная из внешнего файла или сформированная программно), и нам нужно обработать её на сервере. Рассмотрим основные методы решения.

Способ 1: Использование Временного Хранилища

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

Разберем этот процесс по шагам.

Шаг 1. Подготовка данных на клиенте.

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

Однако, если мы формируем данные чисто программно на клиенте, нам придется использовать промежуточные структуры. Но чаще всего задача стоит так: у нас есть реквизит формы (Табличная часть или Динамический список, в который иногда требуется передать ТЗ), и нам нужно отправить это на сервер.

Рассмотрим код на клиенте. Обратите внимание на использование УникальныйИдентификатор — это идентификатор нашей формы, который привязывает данные к жизни формы. Если форма закроется, данные очистятся.


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

Теперь давайте посмотрим, как мы будем принимать эти данные на сервере. Здесь нам понадобится метод ПолучитьИзВременногоХранилища.


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

Важное уточнение: Если вы хотите создать ТаблицаЗначений именно на клиенте, вы этого сделать не сможете. На клиенте вы можете оперировать массивами структур. Если задача — передать сложный массив данных с клиента на сервер, где превратить его в ТЗ, переходим ко второму способу.

Способ 2: Массив структур (Сериализация)

Этот способ отлично подходит, если данных не слишком много (сотни строк, но не десятки тысяч), и вы формируете их программно на клиенте. Платформа 1С позволяет передавать между клиентом и сервером массивы и структуры, так как они являются сериализуемыми объектами. Если же объем данных велик, стоит предварительно изучить сжатие строк и данных внутренними методами 1С.

Давайте построим алгоритм, где мы на клиенте собираем данные в массив структур, а на сервере "распаковываем" их в таблицу значений.

Этап 1: Сбор данных на клиенте

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


&НаКлиенте
Процедура ОтправитьДанныеНаСервер(Команда)
    
    МассивСтрок = Новый Массив;
    
    // Предположим, мы заполняем данные программно или читаем из элементов формы
    Для Сч = 1 По 5 Цикл
        
        // Создаем структуру для одной строки
        СтрокаДанных = Новый Структура;
        СтрокаДанных.Вставить("Номенклатура", "Товар " + Сч);
        СтрокаДанных.Вставить("Количество", 10 * Сч);
        СтрокаДанных.Вставить("Цена", 100);
        
        // Добавляем строку в массив
        МассивСтрок.Добавить(СтрокаДанных);
        
    КонецЦикла;
    
    // Передаем МАССИВ на сервер. Это разрешено.
    ПринятьИОбработатьМассив(МассивСтрок);

КонецПроцедуры

Этап 2: Преобразование в Таблицу Значений на сервере

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


&НаСервере
Процедура ПринятьИОбработатьМассив(МассивДанных)
    
    // Создаем новую таблицу значений
    Таблица = Новый ТаблицаЗначений;
    
    // Если массив пуст, выходим
    Если МассивДанных.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // 1. Создаем колонки на основе первой строки массива (структуры)
    ПерваяСтрока = МассивДанных[0];
    Для Каждого ЭлементСтруктуры Из ПерваяСтрока Цикл
        // Мы не знаем типы, поэтому оставляем их произвольными или определяем по значению,
        // но для простоты создадим просто колонки по именам.
        Таблица.Колонки.Добавить(ЭлементСтруктуры.Ключ);
    КонецЦикла;
    
    // 2. Заполняем таблицу
    Для Каждого ЭлементМассива Из МассивДанных Цикл
        
        НоваяСтрока = Таблица.Добавить();
        
        // Заполняем строку значениями из структуры
        // Используем ЗаполнитьЗначенияСвойств, так как имена ключей и колонок совпадают
        ЗаполнитьЗначенияСвойств(НоваяСтрока, ЭлементМассива);
        
    КонецЦикла;
    
    // Теперь у нас есть полноценная ТЗ, с которой можно работать запросами и т.д.
    // Например, передать в запрос:
    // Запрос.УстановитьПараметр("ТЗ", Таблица);
    
КонецПроцедуры

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

Способ 3: Реквизиты формы (Контекстный вызов)

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

При вызове серверной процедуры &НаСервере (без приставки БезКонтекста), вся форма целиком "улетает" на сервер. Это значит, что внутри серверной процедуры вы можете напрямую обращаться к Объект.Товары или ЭтаФорма.МояТаблица.


&НаКлиенте
Процедура ОбработатьКонтекстно(Команда)
    // Просто вызываем сервер, ничего не передавая
    ОбработатьТаблицуКонтекст();
КонецПроцедуры

&НаСервере
Процедура ОбработатьТаблицуКонтекст()
    // Здесь мы имеем прямой доступ к данным формы
    // Но это тип "ДанныеФормыКоллекция", а не "ТаблицаЗначений"
    
    // Если нам нужна именно ТЗ (например, для выгрузки в запрос),
    // нам нужно преобразовать её:
    ТЗ = Объект.Товары.Выгрузить();
    
    // Теперь ТЗ - это настоящая ТаблицаЗначений
    // Делаем вычисления...
    
    // В новых версиях платформы можно использовать эффективное 
    // добавление данных в существующую временную таблицу 
    // через механизмы запросов прямо на сервере.
    
    // Если нужно вернуть данные обратно на форму:
    Объект.Товары.Загрузить(ТЗ);
КонецПроцедуры

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

Рекомендации по выбору метода

Давайте подведем итог и выберем лучшую стратегию для вашей задачи:

  1. Используйте Временное Хранилище, если вы работаете с файлами, внешними обработками или очень большими объемами данных, которые формируются частями.
  2. Используйте Массив Структур, если вам нужно передать небольшой список параметров или строк, собранных программно на клиенте, и вы хотите использовать вызов &НаСервереБезКонтекста (чтобы сэкономить трафик и ускорить работу).
  3. Используйте Контекстный вызов, если вы просто обрабатываете текущую табличную часть документа и производительность не является критическим узким местом (или данных мало).

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

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

← На главную