Как правильно передать табличные данные с клиента на сервер в 1С 8.3 и заполнить табличную часть объекта?

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

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

Почему прямая загрузка табличной части объекта методом Загрузить() может вызвать ошибку "Поле объекта не обнаружено"?

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

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

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

Решение 1: Передача данных с использованием МассивСтруктур

Один из наиболее распространенных и удобных способов передачи табличных данных с клиента на сервер — это использование МассивСтруктур. Этот подход позволяет четко определить структуру каждой передаваемой строки и легко сопоставить ее с реквизитами табличной части на сервере.

  1. Создание и заполнение МассивСтруктур на стороне клиента:

    На стороне клиента мы формируем массив, каждый элемент которого будет представлять собой Структура. Имена полей в этой структуре должны точно соответствовать именам колонок табличной части, которую мы хотим заполнить на сервере. Давайте посмотрим на пример:

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

    Важный момент: Мы передаем МассивСтруктур, содержащий ссылки на объекты (например, Номенклатура) и числовые значения. Эти типы данных хорошо сериализуются и десериализуются при передаче между клиентом и сервером.

  2. Обработка МассивСтруктур на стороне сервера и заполнение табличной части объекта:

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

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

Преимущества данного подхода:

Этот метод является предпочтительным для большинства случаев, когда объем данных не превышает нескольких тысяч строк.

Решение 2: Передача данных с использованием ТаблицаЗначений

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

  1. Создание и заполнение ТаблицаЗначений на стороне клиента:

    Как и в случае с МассивСтруктур, мы сначала формируем ТаблицаЗначений на клиенте. Крайне важно, чтобы имена колонок в этой таблице значений точно соответствовали именам реквизитов табличной части объекта на сервере.

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

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

  2. Обработка ТаблицаЗначений на стороне сервера и заполнение табличной части объекта:

    На сервере мы получаем ТаблицаЗначений и аналогично первому способу, итерируем по ее строкам, создавая новые строки в табличной части объекта и заполняя их реквизиты.

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

Преимущества данного подхода:

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

Что делать с очень большими объемами данных: XML и JSON

Описанные выше методы с МассивСтруктур и ТаблицаЗначений отлично подходят для большинства повседневных задач. Однако, когда речь заходит о передаче десятков тысяч и более строк табличных данных, могут возникнуть вопросы производительности и потребления памяти. В таких исключительных случаях мы можем рассмотреть использование сериализации данных в форматы XML или JSON.

При использовании этих форматов процесс передачи выглядит следующим образом:

  1. Сериализация на клиенте: Мы преобразуем МассивСтруктур или ТаблицаЗначений в строковое представление формата XML или JSON.

    Пример (концептуально):

    
    &НаКлиенте
    Функция ПолучитьДанныеВJSON()
        МассивДанных = Новый Массив; // Ваш МассивСтруктур или ТаблицаЗначений
        // ... заполнение МассивДанных ...
                
        ЗаписьJSON = Новый ЗаписьJSON;
        ЗаписьJSON.УстановитьСтроку();
        ЗаписатьJSON(ЗаписьJSON, МассивДанных);
        СтрокаJSON = ЗаписьJSON.Закрыть();
        Возврат СтрокаJSON;
    КонецФункции
    
    &НаКлиенте
    Процедура ПередатьОченьБольшиеДанные()
        СтрокаСДанными = ПолучитьДанныеВJSON();
        ПередатьБольшуюСтрокуНаСервер(СтрокаСДанными);
    КонецПроцедуры
    
  2. Передача строки: Полученная строка (XML или JSON) передается с клиента на сервер как обычный строковый параметр.

  3. Десериализация и обработка на сервере: На сервере мы десериализуем строку обратно в МассивСтруктур или ТаблицаЗначений, а затем используем описанные выше методы итеративного заполнения табличной части.

    Пример (концептуально):

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

Важно отметить, что такой подход усложняет код и добавляет накладные расходы на сериализацию/десериализацию. Поэтому мы рекомендуем его применять только в тех случаях, когда другие методы демонстрируют явные проблемы с производительностью или памятью.

Общие рекомендации и лучшие практики

При работе с клиент-серверным взаимодействием и заполнением табличных частей, мы рекомендуем придерживаться следующих принципов:

  1. Используйте серверные процедуры для модификации объектов: Всегда выполняйте операции по изменению данных объектов (таких как запись документа, изменение табличной части) в серверных процедурах или функциях. Никогда не пытайтесь изменять объект напрямую на клиенте.

  2. Минимизируйте количество клиент-серверных вызовов: Каждый вызов сервера несет определенные накладные расходы. Стремитесь передать все необходимые данные за один вызов, а не делать множество отдельных запросов.

  3. Четкие имена полей: Убедитесь, что имена полей в структурах или колонках таблиц значений на клиенте точно совпадают с именами реквизитов табличной части на сервере. Это упрощает код и делает его менее подверженным ошибкам, особенно при использовании ЗаполнитьЗначенияСвойств().

  4. Очистка перед заполнением: Если вы полностью перезаписываете табличную часть, не забывайте предварительно очистить ее методом Очистить(), чтобы избежать дублирования или устаревших данных.

  5. Тестирование производительности: Для критически важных участков кода, работающих с большими объемами данных, всегда проводите тестирование производительности, чтобы выбрать наиболее оптимальный подход.

Применяя эти рекомендации, вы сможете создавать надежные и эффективные механизмы обмена данными между клиентом и сервером в 1С:Предприятии 8.3.

← На главную