Строка и вся табличная часть в серверном вызове: идентификатор, структура, таблица значений
Содержание
Чтобы передать строку табличной части на сервер 1С, передают не саму строку, а её идентификатор: Элементы.Товары.ТекущаяСтрока уходит в процедуру &НаСервере, а там строку находят через Объект.Товары.НайтиПоИдентификатору(Ид). Если нужен вызов &НаСервереБезКонтекста, данные строки копируют в Структура через ЗаполнитьЗначенияСвойств и передают её.
Выбор между способами простой. Идентификатор — когда сервер должен изменить строку прямо в форме. Структура или массив структур — когда серверу нужны только значения, а форма тяжёлая. Таблица значений появляется уже на сервере: на клиенте этого типа нет. Всё ниже относится к управляемым формам платформы 8.3 и одинаково работает в любой типовой конфигурации.
Как устроен обмен между клиентом и сервером в управляемой форме
Код формы выполняется в двух местах. Клиент рисует форму и реагирует на действия пользователя. Сервер работает с базой: запросы, объекты, регистры. Между ними передаются только сериализуемые значения.
Табличная часть на форме — это не ТабличнаяЧасть объекта, а ДанныеФормыКоллекция. Каждая строка — ДанныеФормыЭлементКоллекции. Эти данные живут в форме, и сервер видит их только при контекстном вызове.
Директивы, от которых зависит, что попадёт на сервер:
| Директива | Где выполняется | Доступны данные формы | Что передаётся по сети |
|---|---|---|---|
&НаКлиенте | клиент | да | ничего |
&НаСервере | сервер | да, весь Объект и реквизиты формы | данные формы туда и обратно |
&НаСервереБезКонтекста | сервер | нет | только параметры и результат |
&НаКлиентеНаСервереБезКонтекста | где вызвали | нет | только параметры |
Что можно передать параметром с клиента на сервер:
- ✅ примитивы:
Число,Строка,Дата,Булево,Неопределено; - ✅ ссылки:
СправочникСсылка.*,ДокументСсылка.*, перечисления; - ✅ коллекции:
Массив,Структура,Соответствиеи их фиксированные варианты; - ✅
УникальныйИдентификатор,ХранилищеЗначения,ОписаниеТипов; - ❌
ТаблицаЗначений,ДеревоЗначений,Запрос, объекты (ДокументОбъект) — на клиенте этих типов нет.
Каждый серверный вызов — это отдельный запрос по сети. В тонком клиенте через веб-сервер или интернет задержка одного вызова заметна глазом. Поэтому главное правило: один серверный вызов на одно действие пользователя, а не на каждую строку.
Передача текущей строки по идентификатору
Самый частый случай: пользователь стоит на строке и нажимает кнопку «Пересчитать» или меняет значение в колонке. Серверу нужно прочитать данные из базы и записать результат в эту же строку.
Одна строка
&НаКлиенте
Процедура ПересчитатьСтроку(Команда)
ИдентификаторСтроки = Элементы.Товары.ТекущаяСтрока;
Если ИдентификаторСтроки = Неопределено Тогда
Возврат;
КонецЕсли;
ПересчитатьСтрокуНаСервере(ИдентификаторСтроки);
КонецПроцедуры
&НаСервере
Процедура ПересчитатьСтрокуНаСервере(Знач ИдентификаторСтроки)
СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(ИдентификаторСтроки);
Если СтрокаТЧ = Неопределено Тогда
Возврат;
КонецЕсли;
СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
КонецПроцедуры
Тот же идентификатор можно получить и из текущих данных: Элементы.Товары.ТекущиеДанные.ПолучитьИдентификатор(). Результат одинаковый.
После выполнения процедуры форма сама покажет новое значение суммы: изменённые данные формы вернутся на клиент вместе с ответом сервера.
Несколько выделенных строк
Если пользователь выделил строки мышью с Ctrl или Shift, их идентификаторы лежат в массиве Элементы.Товары.ВыделенныеСтроки. Массив передаётся на сервер целиком, одним вызовом:
&НаКлиенте
Процедура ОбнулитьСкидку(Команда)
Выделенные = Элементы.Товары.ВыделенныеСтроки;
Если Выделенные.Количество() = 0 Тогда
Возврат;
КонецЕсли;
ОбнулитьСкидкуНаСервере(Выделенные);
КонецПроцедуры
&НаСервере
Процедура ОбнулитьСкидкуНаСервере(Знач Выделенные)
Для Каждого ИдентификаторСтроки Из Выделенные Цикл
СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(ИдентификаторСтроки);
Если СтрокаТЧ = Неопределено Тогда
Продолжить;
КонецЕсли;
СтрокаТЧ.ПроцентСкидки = 0;
СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
КонецЦикла;
КонецПроцедуры
Идентификатор строки — служебное число, которое живёт только пока открыта форма. Это не НомерСтроки и не индекс. Сохранять его в базе или в настройках бессмысленно: при следующем открытии формы он будет другим.
Структура и массив структур для вызова без контекста
Контекстный вызов удобен, но тянет за собой данные формы. В документе на 500 строк с десятком реквизитов это сотни килобайт на каждое нажатие. Если серверу нужны только два-три поля строки, их выгоднее передать в Структура и вызвать &НаСервереБезКонтекста.
Одна строка через структуру
Типичный пример — подбор цены при выборе номенклатуры:
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
ДанныеСтроки = Новый Структура("Номенклатура, Количество");
ЗаполнитьЗначенияСвойств(ДанныеСтроки, ТекущиеДанные);
Результат = ПолучитьЦенуИСумму(ДанныеСтроки, Объект.Дата);
ЗаполнитьЗначенияСвойств(ТекущиеДанные, Результат);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьЦенуИСумму(Знач ДанныеСтроки, Знач ДатаЦены)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
| &ДатаЦены, Номенклатура = &Номенклатура) КАК Цены";
Запрос.УстановитьПараметр("ДатаЦены", ДатаЦены);
Запрос.УстановитьПараметр("Номенклатура", ДанныеСтроки.Номенклатура);
Результат = Новый Структура("Цена, Сумма", 0, 0);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Результат.Цена = Выборка.Цена;
Результат.Сумма = Выборка.Цена * ДанныеСтроки.Количество;
КонецЕсли;
Возврат Результат;
КонецФункции
Имя регистра цен и его измерения в вашей конфигурации могут отличаться — смысл примера в схеме передачи, а не в запросе.
ЗаполнитьЗначенияСвойств копирует только совпадающие по имени свойства. Поэтому в структуру результата кладут ровно те поля, которые надо записать в строку: Цена и Сумма из функции попадут в одноимённые колонки, остальные колонки строки не изменятся.
Вся табличная часть массивом структур
Когда серверу нужны все строки, а форма тяжёлая, табличную часть упаковывают в Массив из Структура:
&НаКлиенте
Функция ТоварыВМассив()
МассивСтрок = Новый Массив;
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
ДанныеСтроки = Новый Структура("НомерСтроки, Номенклатура, Количество, Цена");
ЗаполнитьЗначенияСвойств(ДанныеСтроки, СтрокаТЧ);
МассивСтрок.Добавить(ДанныеСтроки);
КонецЦикла;
Возврат МассивСтрок;
КонецФункции
Массив передаётся на сервер одним вызовом. Поле НомерСтроки пригодится, чтобы потом вернуть результат в нужные строки.
Таблица значений на сервере: выгрузка, загрузка, запрос
ТаблицаЗначений — главный инструмент для обработки табличной части на сервере. С ней работают сортировка, свёртка, поиск по индексу и передача в запрос параметром. Создать её на клиенте нельзя, поэтому таблица появляется уже в серверной процедуре.
Из данных формы — одной строкой
В контекстном вызове табличную часть формы выгружают методом Выгрузить(), а после обработки загружают обратно через Загрузить():
&НаСервере
Процедура СвернутьТоварыНаСервере()
ТаблицаТоваров = Объект.Товары.Выгрузить();
ТаблицаТоваров.Свернуть("Номенклатура, Цена", "Количество, Сумма");
Объект.Товары.Загрузить(ТаблицаТоваров);
КонецПроцедуры
Пример схлопывает повторяющиеся строки с одинаковой номенклатурой и ценой и суммирует количество. Номера строк платформа пересчитает сама.
Из массива структур — с типизацией колонок
Если на сервер пришёл массив структур, таблицу собирают вручную. Колонки обязательно типизируют, иначе такую таблицу не получится передать в запрос:
&НаСервереБезКонтекста
Функция ТаблицаИзМассива(Знач МассивСтрок)
ТаблицаТоваров = Новый ТаблицаЗначений;
ТаблицаТоваров.Колонки.Добавить("НомерСтроки",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(5, 0)));
ТаблицаТоваров.Колонки.Добавить("Номенклатура",
Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТаблицаТоваров.Колонки.Добавить("Количество",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
ТаблицаТоваров.Колонки.Добавить("Цена",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
Для Каждого ДанныеСтроки Из МассивСтрок Цикл
ЗаполнитьЗначенияСвойств(ТаблицаТоваров.Добавить(), ДанныеСтроки);
КонецЦикла;
Возврат ТаблицаТоваров;
КонецФункции
Дальше таблица идёт в запрос параметром и кладётся во временную таблицу:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.НомерСтроки КАК НомерСтроки,
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТТовары
|ИЗ
| &Товары КАК Т";
Запрос.УстановитьПараметр("Товары", ТаблицаТоваров);
Запрос.Выполнить();
Временную таблицу ВТТовары потом соединяют с остатками, ценами или другими регистрами — одним запросом на все строки сразу, а не запросом в цикле.
Таблица, полученная через Объект.Товары.Выгрузить(), уже типизирована по метаданным табличной части. Её можно сразу передавать в запрос — колонки вручную описывать не нужно.
Какой способ выбрать: сравнение
| Способ | Директива | Что уходит на сервер | Когда применять | Ограничения |
|---|---|---|---|---|
| Идентификатор строки | &НаСервере | данные формы + число | изменить одну строку на месте | тяжёлая форма — медленный вызов |
ВыделенныеСтроки | &НаСервере | данные формы + массив чисел | групповая правка выделенного | то же |
Структура | &НаСервереБезКонтекста | 2–5 значений | подбор цены, проверка, расчёт по строке | надо вручную вернуть результат в строку |
| Массив структур | &НаСервереБезКонтекста | все строки, только нужные поля | обработка всей ТЧ в тяжёлой форме | на сервере нужно собрать таблицу |
Выгрузить() / Загрузить() | &НаСервере | данные формы | свёртка, сортировка, перезаполнение ТЧ | только в контекстном вызове |
| Временное хранилище | любая серверная | строка-адрес | фоновое задание, большие объёмы | данные живут ограниченное время |
Упрощённое правило выбора:
- Меняете строку на форме и форма небольшая — идентификатор и
&НаСервере. - Нужны только значения, а форма большая — структура и
&НаСервереБезКонтекста. - Нужна групповая обработка — одна таблица значений и один запрос на все строки.
- Обработка долгая и не должна вешать интерфейс — временное хранилище и фоновое задание.
Возврат результатов с сервера в форму
Как вернуть результат, зависит от того, каким способом строки ушли на сервер.
После контекстного вызова
Ничего делать не нужно. Всё, что сервер записал в Объект.Товары, вернётся на клиент вместе с данными формы. Форма отметится как изменённая, при закрытии пользователь увидит вопрос о сохранении.
После вызова без контекста
Сервер возвращает Структура (для одной строки) или Массив структур (для нескольких). Клиент раскладывает результат по строкам:
&НаКлиенте
Процедура ПрименитьРезультат(МассивРезультатов)
Для Каждого ДанныеСтроки Из МассивРезультатов Цикл
Найденные = Объект.Товары.НайтиСтроки(
Новый Структура("НомерСтроки", ДанныеСтроки.НомерСтроки));
Если Найденные.Количество() = 1 Тогда
ЗаполнитьЗначенияСвойств(Найденные[0], ДанныеСтроки);
КонецЕсли;
КонецЦикла;
Модифицированность = Истина;
КонецПроцедуры
После правки данных на клиенте флаг Модифицированность выставляют явно, чтобы пользователь не закрыл форму без записи.
Таблица значений и временное хранилище
Вернуть ТаблицаЗначений на клиент как есть нельзя. Есть два рабочих варианта:
- 📦 положить таблицу в реквизит формы типа
ТаблицаЗначенийчерезЗначениеВРеквизитФормы— на клиенте он будет виден какДанныеФормыКоллекция; - 🗂 поместить таблицу во временное хранилище:
Адрес = ПоместитьВоВременноеХранилище(ТаблицаТоваров, УникальныйИдентификатор)и вернуть на клиент только строкуАдрес.
Второй вариант — стандартная схема для фоновых заданий. Форма кладёт табличную часть в хранилище, фоновое задание получает адрес, читает данные через ПолучитьИзВременногоХранилища(Адрес), обрабатывает и кладёт результат обратно. Передача УникальныйИдентификатор формы привязывает данные к её жизни: закрыли форму — хранилище очистилось.
Производительность и частые ошибки
Проблемы с передачей табличной части обычно видны сразу: форма подтормаживает, данные не обновились или платформа ругается при компиляции модуля.
Что ещё ускоряет работу с табличными частями:
- 🚀 Не вызывайте сервер в цикле. 200 строк — это 200 сетевых запросов. Соберите строки в массив и передайте одним вызовом.
- 🧮 Не делайте запрос в цикле на сервере. Положите таблицу во временную таблицу и соедините с регистром одним запросом.
- 🔒 Пишите
Значу входных параметров. Параметр безЗначпосле серверного вызова возвращается на клиент. Для большого массива это лишний трафик в обратную сторону. - 📏 Передавайте только нужные колонки. Структура из трёх полей вместо всей строки с двадцатью колонками.
- 🖥 Считайте простое на клиенте.
Сумма = Количество * Ценане требует сервера: это обычная арифметика, её делают в процедуре&НаКлиенте.
Можно ли передать на сервер ссылку на документ и прочитать табличную часть оттуда?
ДокументСсылка.Товары читается на сервере из базы. Но вы получите только записанные строки — всё, что пользователь добавил или изменил в открытой форме без записи, на сервер так не попадёт.Зачем писать Знач у параметра серверной процедуры?
Знач параметр после вызова передаётся обратно на клиент. Если сервер его не меняет, это пустая трата трафика. Для входных данных ставьте Знач.Можно ли передать на сервер Соответствие вместо Структуры?
Соответствие сериализуется и передаётся. Его выбирают, когда ключи — ссылки или числа, например «номенклатура → количество». Для полей строки удобнее Структура: она работает с ЗаполнитьЗначенияСвойств.