Строка и вся табличная часть в серверном вызове: идентификатор, структура, таблица значений

· 9 мин

Содержание

Чтобы передать строку табличной части на сервер 1С, передают не саму строку, а её идентификатор: Элементы.Товары.ТекущаяСтрока уходит в процедуру &НаСервере, а там строку находят через Объект.Товары.НайтиПоИдентификатору(Ид). Если нужен вызов &НаСервереБезКонтекста, данные строки копируют в Структура через ЗаполнитьЗначенияСвойств и передают её.

Выбор между способами простой. Идентификатор — когда сервер должен изменить строку прямо в форме. Структура или массив структур — когда серверу нужны только значения, а форма тяжёлая. Таблица значений появляется уже на сервере: на клиенте этого типа нет. Всё ниже относится к управляемым формам платформы 8.3 и одинаково работает в любой типовой конфигурации.

Как устроен обмен между клиентом и сервером в управляемой форме

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

Табличная часть на форме — это не ТабличнаяЧасть объекта, а ДанныеФормыКоллекция. Каждая строка — ДанныеФормыЭлементКоллекции. Эти данные живут в форме, и сервер видит их только при контекстном вызове.

Директивы, от которых зависит, что попадёт на сервер:

ДирективаГде выполняетсяДоступны данные формыЧто передаётся по сети
&НаКлиентеклиентданичего
&НаСервересерверда, весь Объект и реквизиты формыданные формы туда и обратно
&НаСервереБезКонтекстасервернеттолько параметры и результат
&НаКлиентеНаСервереБезКонтекстагде вызвалинеттолько параметры

Что можно передать параметром с клиента на сервер:

  • ✅ примитивы: Число, Строка, Дата, Булево, Неопределено;
  • ✅ ссылки: СправочникСсылка.*, ДокументСсылка.*, перечисления;
  • ✅ коллекции: Массив, Структура, Соответствие и их фиксированные варианты;
  • ✅ УникальныйИдентификатор, ХранилищеЗначения, ОписаниеТипов;
  • ❌ ТаблицаЗначений, ДеревоЗначений, Запрос, объекты (ДокументОбъект) — на клиенте этих типов нет.

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

Передача текущей строки по идентификатору

Самый частый случай: пользователь стоит на строке и нажимает кнопку «Пересчитать» или меняет значение в колонке. Серверу нужно прочитать данные из базы и записать результат в эту же строку.

Одна строка

&НаКлиенте
Процедура ПересчитатьСтроку(Команда)

	ИдентификаторСтроки = Элементы.Товары.ТекущаяСтрока;
	Если ИдентификаторСтроки = Неопределено Тогда
		Возврат;
	КонецЕсли;

	ПересчитатьСтрокуНаСервере(ИдентификаторСтроки);

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

&НаСервере
Процедура ПересчитатьСтрокуНаСервере(Знач ИдентификаторСтроки)

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

	СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;

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

Тот же идентификатор можно получить и из текущих данных: Элементы.Товары.ТекущиеДанные.ПолучитьИдентификатор(). Результат одинаковый.

После выполнения процедуры форма сама покажет новое значение суммы: изменённые данные формы вернутся на клиент вместе с ответом сервера.

Несколько выделенных строк

Если пользователь выделил строки мышью с Ctrl или Shift, их идентификаторы лежат в массиве Элементы.Товары.ВыделенныеСтроки. Массив передаётся на сервер целиком, одним вызовом:

&НаКлиенте
Процедура ОбнулитьСкидку(Команда)

	Выделенные = Элементы.Товары.ВыделенныеСтроки;
	Если Выделенные.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;

	ОбнулитьСкидкуНаСервере(Выделенные);

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

&НаСервере
Процедура ОбнулитьСкидкуНаСервере(Знач Выделенные)

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

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

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

Структура и массив структур для вызова без контекста

Контекстный вызов удобен, но тянет за собой данные формы. В документе на 500 строк с десятком реквизитов это сотни килобайт на каждое нажатие. Если серверу нужны только два-три поля строки, их выгоднее передать в Структура и вызвать &НаСервереБезКонтекста.

Одна строка через структуру

Типичный пример — подбор цены при выборе номенклатуры:

&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)

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

	ДанныеСтроки = Новый Структура("Номенклатура, Количество");
	ЗаполнитьЗначенияСвойств(ДанныеСтроки, ТекущиеДанные);

	Результат = ПолучитьЦенуИСумму(ДанныеСтроки, Объект.Дата);
	ЗаполнитьЗначенияСвойств(ТекущиеДанные, Результат);

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

&НаСервереБезКонтекста
Функция ПолучитьЦенуИСумму(Знач ДанныеСтроки, Знач ДатаЦены)

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

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

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

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

Имя регистра цен и его измерения в вашей конфигурации могут отличаться — смысл примера в схеме передачи, а не в запросе.

ЗаполнитьЗначенияСвойств копирует только совпадающие по имени свойства. Поэтому в структуру результата кладут ровно те поля, которые надо записать в строку: Цена и Сумма из функции попадут в одноимённые колонки, остальные колонки строки не изменятся.

Вся табличная часть массивом структур

Когда серверу нужны все строки, а форма тяжёлая, табличную часть упаковывают в Массив из Структура:

&НаКлиенте
Функция ТоварыВМассив()

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

	Возврат МассивСтрок;

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

Массив передаётся на сервер одним вызовом. Поле НомерСтроки пригодится, чтобы потом вернуть результат в нужные строки.

Таблица значений на сервере: выгрузка, загрузка, запрос

ТаблицаЗначений — главный инструмент для обработки табличной части на сервере. С ней работают сортировка, свёртка, поиск по индексу и передача в запрос параметром. Создать её на клиенте нельзя, поэтому таблица появляется уже в серверной процедуре.

Из данных формы — одной строкой

В контекстном вызове табличную часть формы выгружают методом Выгрузить(), а после обработки загружают обратно через Загрузить():

&НаСервере
Процедура СвернутьТоварыНаСервере()

	ТаблицаТоваров = Объект.Товары.Выгрузить();
	ТаблицаТоваров.Свернуть("Номенклатура, Цена", "Количество, Сумма");
	Объект.Товары.Загрузить(ТаблицаТоваров);

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

Пример схлопывает повторяющиеся строки с одинаковой номенклатурой и ценой и суммирует количество. Номера строк платформа пересчитает сама.

Из массива структур — с типизацией колонок

Если на сервер пришёл массив структур, таблицу собирают вручную. Колонки обязательно типизируют, иначе такую таблицу не получится передать в запрос:

&НаСервереБезКонтекста
Функция ТаблицаИзМассива(Знач МассивСтрок)

	ТаблицаТоваров = Новый ТаблицаЗначений;
	ТаблицаТоваров.Колонки.Добавить("НомерСтроки",
		Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(5, 0)));
	ТаблицаТоваров.Колонки.Добавить("Номенклатура",
		Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
	ТаблицаТоваров.Колонки.Добавить("Количество",
		Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
	ТаблицаТоваров.Колонки.Добавить("Цена",
		Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));

	Для Каждого ДанныеСтроки Из МассивСтрок Цикл
		ЗаполнитьЗначенияСвойств(ТаблицаТоваров.Добавить(), ДанныеСтроки);
	КонецЦикла;

	Возврат ТаблицаТоваров;

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

Дальше таблица идёт в запрос параметром и кладётся во временную таблицу:

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

Временную таблицу ВТТовары потом соединяют с остатками, ценами или другими регистрами — одним запросом на все строки сразу, а не запросом в цикле.

Таблица, полученная через Объект.Товары.Выгрузить(), уже типизирована по метаданным табличной части. Её можно сразу передавать в запрос — колонки вручную описывать не нужно.

Какой способ выбрать: сравнение

СпособДирективаЧто уходит на серверКогда применятьОграничения
Идентификатор строки&НаСервереданные формы + числоизменить одну строку на местетяжёлая форма — медленный вызов
ВыделенныеСтроки&НаСервереданные формы + массив чиселгрупповая правка выделенногото же
Структура&НаСервереБезКонтекста2–5 значенийподбор цены, проверка, расчёт по строкенадо вручную вернуть результат в строку
Массив структур&НаСервереБезКонтекставсе строки, только нужные поляобработка всей ТЧ в тяжёлой формена сервере нужно собрать таблицу
Выгрузить() / Загрузить()&НаСервереданные формысвёртка, сортировка, перезаполнение ТЧтолько в контекстном вызове
Временное хранилищелюбая сервернаястрока-адресфоновое задание, большие объёмыданные живут ограниченное время

Упрощённое правило выбора:

  1. Меняете строку на форме и форма небольшая — идентификатор и &НаСервере.
  2. Нужны только значения, а форма большая — структура и &НаСервереБезКонтекста.
  3. Нужна групповая обработка — одна таблица значений и один запрос на все строки.
  4. Обработка долгая и не должна вешать интерфейс — временное хранилище и фоновое задание.

Возврат результатов с сервера в форму

Как вернуть результат, зависит от того, каким способом строки ушли на сервер.

После контекстного вызова

Ничего делать не нужно. Всё, что сервер записал в Объект.Товары, вернётся на клиент вместе с данными формы. Форма отметится как изменённая, при закрытии пользователь увидит вопрос о сохранении.

После вызова без контекста

Сервер возвращает Структура (для одной строки) или Массив структур (для нескольких). Клиент раскладывает результат по строкам:

&НаКлиенте
Процедура ПрименитьРезультат(МассивРезультатов)

	Для Каждого ДанныеСтроки Из МассивРезультатов Цикл
		Найденные = Объект.Товары.НайтиСтроки(
			Новый Структура("НомерСтроки", ДанныеСтроки.НомерСтроки));
		Если Найденные.Количество() = 1 Тогда
			ЗаполнитьЗначенияСвойств(Найденные[0], ДанныеСтроки);
		КонецЕсли;
	КонецЦикла;

	Модифицированность = Истина;

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

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

Таблица значений и временное хранилище

Вернуть ТаблицаЗначений на клиент как есть нельзя. Есть два рабочих варианта:

  • 📦 положить таблицу в реквизит формы типа ТаблицаЗначений через ЗначениеВРеквизитФормы — на клиенте он будет виден как ДанныеФормыКоллекция;
  • 🗂 поместить таблицу во временное хранилище: Адрес = ПоместитьВоВременноеХранилище(ТаблицаТоваров, УникальныйИдентификатор) и вернуть на клиент только строку Адрес.

Второй вариант — стандартная схема для фоновых заданий. Форма кладёт табличную часть в хранилище, фоновое задание получает адрес, читает данные через ПолучитьИзВременногоХранилища(Адрес), обрабатывает и кладёт результат обратно. Передача УникальныйИдентификатор формы привязывает данные к её жизни: закрыли форму — хранилище очистилось.

Производительность и частые ошибки

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

Диагностика: выберите симптом

Что ещё ускоряет работу с табличными частями:

  • 🚀 Не вызывайте сервер в цикле. 200 строк — это 200 сетевых запросов. Соберите строки в массив и передайте одним вызовом.
  • 🧮 Не делайте запрос в цикле на сервере. Положите таблицу во временную таблицу и соедините с регистром одним запросом.
  • 🔒 Пишите Знач у входных параметров. Параметр без Знач после серверного вызова возвращается на клиент. Для большого массива это лишний трафик в обратную сторону.
  • 📏 Передавайте только нужные колонки. Структура из трёх полей вместо всей строки с двадцатью колонками.
  • 🖥 Считайте простое на клиенте. Сумма = Количество * Цена не требует сервера: это обычная арифметика, её делают в процедуре &НаКлиенте.
Можно ли передать на сервер ссылку на документ и прочитать табличную часть оттуда?
Можно: ДокументСсылка.Товары читается на сервере из базы. Но вы получите только записанные строки — всё, что пользователь добавил или изменил в открытой форме без записи, на сервер так не попадёт.
Зачем писать Знач у параметра серверной процедуры?
Без Знач параметр после вызова передаётся обратно на клиент. Если сервер его не меняет, это пустая трата трафика. Для входных данных ставьте Знач.
Можно ли передать на сервер Соответствие вместо Структуры?
Да, Соответствие сериализуется и передаётся. Его выбирают, когда ключи — ссылки или числа, например «номенклатура → количество». Для полей строки удобнее Структура: она работает с ЗаполнитьЗначенияСвойств.
Как обработать табличную часть в фоне, не блокируя форму?
Поместите таблицу во временное хранилище, запустите фоновое задание с адресом в параметрах, а результат заберите по адресу после завершения задания. Серверный вызов из формы синхронный: пока он идёт, пользователь ждёт.
Подойдёт ли всё это для обычных форм 8.2?
Нет. В обычных формах клиент и сервер не разделены таким образом, и табличная часть доступна объекту формы напрямую. Описанные приёмы относятся к управляемым формам.