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