Как передать данные с сервера на клиент для заполнения строки табличной части в 1С?

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

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

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

Способ 1. Получение структуры через функцию без контекста и заполнение текущей строки на клиенте

Проанализируем ситуацию: когда в результате запроса к регистру возвращается только одна строка с остатками или реквизитами, нет никакой необходимости формировать полноценную таблицу. Для передачи данных с сервера на клиент отлично подходит коллекция Структура. Она легко сериализуется платформой, быстро передается по сети и позволяет применить стандартную процедуру ЗаполнитьЗначенияСвойств.

Для минимизации сетевого трафика вызовем серверную функцию с директивой &НаСервереБезКонтекста. В отличие от контекстного вызова &НаСервере, неконтекстная процедура не передает на сервер все данные формы, что существенно ускоряет отклик интерфейса.

Разберем по шагам реализацию данного варианта:

  1. На клиенте получаем текущие данные редактируемой строки через Элементы.ИмяТаблицы.ТекущиеДанные.
  2. Передаем ссылку на выбранный документ и вспомогательные параметры в серверную неконтекстную функцию.
  3. На сервере формируем оптимальный запрос к виртуальной таблице остатков регистра, помещая отбор внутрь параметров виртуальной таблицы, а не в блок ГДЕ.
  4. Записываем результат выборки в структуру с именами ключей, в точности совпадающими с именами колонок табличной части.
  5. Возвращаем структуру на клиент и применяем метод ЗаполнитьЗначенияСвойств.

Посмотрим на пример программного кода:


&НаКлиенте
Процедура ТабличнаяЧастьВАТСсылкаНаДокПриИзменении(Элемент)

    ТекущаяСтрока = Элементы.ТабличнаяЧастьВАТ.ТекущиеДанные;
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;

    // Передаем только необходимые параметры в неконтекстную функцию
    ДанныеДляЗаполнения = ПолучитьДанныеСтрокиНаСервере(ТекущаяСтрока.СсылкаНаДок, Объект.ВидОперации, Объект.СтавкаВАТ);
    
    // Заполняем реквизиты строки формы значениями из возвращенной структуры
    ЗаполнитьЗначенияСвойств(ТекущаяСтрока, ДанныеДляЗаполнения);

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

&НаСервереБезКонтекста
Функция ПолучитьДанныеСтрокиНаСервере(Знач ДокПТУ, Знач ВидОперации, Знач СтавкаВАТ)

    ДанныеСтроки = Новый Структура;
    ДанныеСтроки.Вставить("ОстатокВАТ", 0);
    ДанныеСтроки.Вставить("КоличествоВАТ", 0);
    ДанныеСтроки.Вставить("СуммаВАТ", 0);
    ДанныеСтроки.Вставить("Партнер", ПредопределенноеЗначение("Справочник.Партнеры.ПустаяСсылка"));
    ДанныеСтроки.Вставить("СуммаДок", 0);

    Если Не ЗначениеЗаполнено(ДокПТУ) Тогда
        Возврат ДанныеСтроки;
    КонецЕсли;

    Запрос = Новый Запрос;
    // Обязательно накладываем отбор в параметрах виртуальной таблицы
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |    ВАТОстатки.СуммаВАТОстаток КАК СуммаВАТОстаток,
        |    ВАТОстатки.ДокументПоступлениеТоваровУслуг.Партнер КАК Партнер,
        |    ВАТОстатки.ДокументПоступлениеТоваровУслуг.СуммаДокумента КАК СуммаДокумента
        |ИЗ
        |    РегистрНакопления.ВАТ.Остатки(&Период, ДокументПоступлениеТоваровУслуг = &ДокПТУ) КАК ВАТОстатки";

    Запрос.УстановитьПараметр("ДокПТУ", ДокПТУ);
    Запрос.УстановитьПараметр("Период", ТекущаяДатаСеанса());

    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        ДанныеСтроки.ОстатокВАТ = Выборка.СуммаВАТОстаток;
        ДанныеСтроки.КоличествоВАТ = Выборка.СуммаВАТОстаток;
        ДанныеСтроки.Партнер = Выборка.Партнер;
        ДанныеСтроки.СуммаДок = Выборка.СуммаДокумента;
        
        Если ВидОперации = ПредопределенноеЗначение("Перечисление.ВидыОперацийВАТ.Передача") Тогда
            ДанныеСтроки.СуммаВАТ = Выборка.СуммаВАТОстаток;
        Иначе
            ДанныеСтроки.СуммаВАТ = (Выборка.СуммаДокумента * СтавкаВАТ) / 100;
        КонецЕсли;
    КонецЕсли;

    Возврат ДанныеСтроки;

КонецФункции

Способ 2. Заполнение строки на сервере по идентификатору

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

Выясним причину, почему этот способ удобен: если при заполнении реквизитов требуется выполнять сложный пересчет итогов документа, обращаться ко многим реквизитам объекта формы или валидировать взаимосвязи между строками, удобнее оставаться в контексте формы &НаСервере.

Разберем реализацию с получением строки через идентификатор:


&НаКлиенте
Процедура ТабличнаяЧастьВАТСсылкаНаДокПриИзменении(Элемент)

    ТекущиеДанные = Элементы.ТабличнаяЧастьВАТ.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;

    // Получаем числовой идентификатор текущей строки
    ИдентификаторСтроки = ТекущиеДанные.ПолучитьИдентификатор();
    
    // Передаем идентификатор на сервер в контекстную процедуру
    ЗаполнитьТекущуюСтрокуНаСервере(ИдентификаторСтроки);

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

&НаСервере
Процедура ЗаполнитьТекущуюСтрокуНаСервере(ИдентификаторСтроки)

    // Находим строку в коллекции данных формы
    СтрокаТЧ = Объект.ТабличнаяЧастьВАТ.НайтиПоИдентификатору(ИдентификаторСтроки);
    Если СтрокаТЧ = Неопределено Тогда
        Возврат;
    КонецЕсли;

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |    ВАТОстатки.СуммаВАТОстаток КАК СуммаВАТОстаток,
        |    ВАТОстатки.ДокументПоступлениеТоваровУслуг.Партнер КАК Партнер,
        |    ВАТОстатки.ДокументПоступлениеТоваровУслуг.СуммаДокумента КАК СуммаДокумента
        |ИЗ
        |    РегистрНакопления.ВАТ.Остатки(&Период, ДокументПоступлениеТоваровУслуг = &ДокПТУ) КАК ВАТОстатки";

    Запрос.УстановитьПараметр("ДокПТУ", СтрокаТЧ.СсылкаНаДок);
    Запрос.УстановитьПараметр("Период", ТекущаяДатаСеанса());

    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        СтрокаТЧ.ОстатокВАТ = Выборка.СуммаВАТОстаток;
        СтрокаТЧ.КоличествоВАТ = Выборка.СуммаВАТОстаток;
        СтрокаТЧ.Партнер = Выборка.Партнер;
        СтрокаТЧ.СуммаДок = Выборка.СуммаДокумента;

        Если Объект.ВидОперации = Перечисления.ВидыОперацийВАТ.Передача Тогда
            СтрокаТЧ.СуммаВАТ = Выборка.СуммаВАТОстаток;
        Иначе
            СтрокаТЧ.СуммаВАТ = (Выборка.СуммаДокумента * Объект.СтавкаВАТ) / 100;
        КонецЕсли;
    КонецЕсли;

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

Способ 3. Передача на клиент нескольких строк или перезаполнение всей таблицы

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

  1. Полное перезаполнение на сервере: если табличная часть перезаполняется целиком по кнопке «Заполнить», проще всего выполнить операцию в контекстной серверной процедуре с помощью метода Объект.ТабличнаяЧастьВАТ.Загрузить(ТаблицаРезультата). В этом случае данные формы обновятся автоматически.
  2. Передача массива структур на клиент: если необходимо обработать несколько строк на клиенте без передачи всего контекста формы, на сервере создается Массив, элементами которого являются объекты Структура. Массив успешно передается на сторону тонкого или веб-клиента:

&НаСервереБезКонтекста
Функция ПолучитьНаборСтрокНаСервере(ПараметрОтбора)

    МассивРезультатов = Новый Массив;

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ВАТОстатки.СуммаВАТОстаток КАК СуммаВАТ,
        |    ВАТОстатки.Поставщик КАК Партнер
        |ИЗ
        |    РегистрНакопления.ВАТ.Остатки(&Период, ДокументПоступлениеТоваровУслуг = &ДокПТУ) КАК ВАТОстатки";

    Запрос.УстановитьПараметр("ДокПТУ", ПараметрОтбора);
    Запрос.УстановитьПараметр("Период", ТекущаяДатаСеанса());

    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        СтрДанных = Новый Структура("СуммаВАТ, Партнер");
        ЗаполнитьЗначенияСвойств(СтрДанных, Выборка);
        МассивРезультатов.Добавить(СтрДанных);
    КонецЦикла;

    Возврат МассивРезультатов;

КонецФункции

Важные правила при работе с запросами и клиент-серверной архитектурой:

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