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