Как в управляемой форме 1С отдать серверу текущую строку табличной части

· 9 мин

Содержание

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

Сам объект ТекущиеДанные на сервер передавать не нужно: это свойство живёт только на клиенте. Всё, что описано ниже, относится к управляемым формам платформы 8.3. В обычных формах толстого клиента разделения на клиент и сервер в модуле формы нет, и строку берут напрямую из ЭлементыФормы.Товары.ТекущаяСтрока.

Где живёт строка табличной части: клиент, сервер и данные формы

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

На экране эту коллекцию показывает элемент ТаблицаФормы. У него три свойства, через которые вы узнаёте, на какой строке стоит пользователь:

СвойствоЧто возвращаетГде доступно
ТекущиеДанныеСаму строку (ДанныеФормыЭлементКоллекции) или НеопределеноТолько клиент
ТекущаяСтрокаИдентификатор строки (число) или НеопределеноКлиент и сервер
ВыделенныеСтрокиМассив идентификаторов выделенных строкКлиент и сервер

Отсюда главное правило. 🔑 Через границу клиент-сервер передают идентификатор или значения полей, а не элемент коллекции.

Почему не элемент целиком:

  • 🧩 при вызове &НаСервере данные формы и так уходят на сервер вместе с контекстом — строка там уже есть, её нужно только найти;
  • 🔁 переданный параметром элемент на сервере стал бы копией: изменения в нём не вернутся в таблицу на экране;
  • 📦 в &НаСервереБезКонтекста данных формы нет вообще, туда идут только значения простых типов, ссылки и универсальные коллекции.

Идентификатор строки — не номер строки. Номер (НомерСтроки) меняется при сортировке и удалении. Идентификатор привязан к самой строке, пока коллекция не перезаполнена целиком.

Если форма обычная, а не управляемая

В конфигурациях на обычных формах (толстый клиент, старые доработки) код модуля формы выполняется там же, где форма, — на клиенте толстого клиента. Директив &НаСервере в модуле обычной формы нет.

Текущую строку там получают так: ЭлементыФормы.Товары.ТекущаяСтрока возвращает саму строку табличной части, а ЭлементыФормы.Товары.ТекущиеДанные — данные строки. С ними работают напрямую, без поиска по идентификатору.

Когда код из обычной формы обращается к серверному общему модулю (с флагом «Вызов сервера»), правило то же, что и в управляемой форме:

  • 📌 передавайте значения полей или ссылку на документ;
  • 📌 результат возвращайте функцией и записывайте в строку на стороне формы.

Если базу переводят на управляемые формы, именно места с прямой работой со строкой переписывают первыми: ТекущаяСтрока в новой форме — уже число, а не строка табличной части.

Передача текущей строки по идентификатору в процедуру &НаСервере

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

Клиентская часть

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

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

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

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

Серверная часть

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

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

	СтрокаТЧ.Цена  = ПолучитьЦену(СтрокаТЧ.Номенклатура, Объект.Дата);
	СтрокаТЧ.Сумма = СтрокаТЧ.Цена * СтрокаТЧ.Количество;

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

После возврата с сервера данные формы синхронизируются, и пользователь сразу видит новую цену и сумму в той же строке. Функцию ПолучитьЦену здесь пишете вы сами — это обычный запрос к регистру цен.

💡 Можно не передавать идентификатор параметром, а прочитать Элементы.Товары.ТекущаяСтрока прямо на сервере. Работает, но явный параметр читается лучше: по сигнатуре видно, с какой строкой работает процедура.

Значения строки в структуре для &НаСервереБезКонтекста

Если серверу нужны только два-три поля строки, тащить туда всю форму незачем. Соберите значения в Структура на клиенте и вызовите внеконтекстную функцию.

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

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

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

	Результат = ДанныеДляСтроки(ДанныеСтроки, Объект.Дата);

	ТекДанные.Цена  = Результат.Цена;
	ТекДанные.Сумма = Результат.Цена * ТекДанные.Количество;

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

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

	Результат = Новый Структура("Цена", 0);
	// здесь запрос к регистру цен по ДанныеСтроки.Номенклатура на ДатаДокумента
	Возврат Результат;

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

Обратите внимание на схему: сервер ничего не пишет в строку, он только возвращает значения. Записывает их в строку клиент — через ТекДанные.

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

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

ЗаполнитьЗначенияСвойств копирует только те свойства, ключи которых уже есть в структуре. Пустая Новый Структура без ключей останется пустой.

Какой способ выбрать

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

Команды формы и параметр команды: откуда брать строку

Обработчик команды формы получает один параметр — Команда. Это описание самой команды (имя, заголовок), а не данные. Строку в нём искать бесполезно, её берут из элемента формы, как в примерах выше.

Другое дело — общая команда или команда объекта с заданным свойством ТипПараметраКоманды. Её обработчик в модуле команды выглядит так:

&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
	ОбработатьНаСервере(ПараметрКоманды);
КонецПроцедуры

Сюда платформа сама подставляет текущую строку списка или формы-источника. Для формы списка это ссылка на объект. Если в свойстве РежимИспользованияПараметра выбран режим Множественный, в ПараметрКоманды придёт массив ссылок выделенных строк.

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

⚠️ В динамическом списке ТекущаяСтрока — это не число, а ключ строки: обычно ссылка на элемент. ТекущиеДанные списка тоже есть, но в нём только колонки, выведенные в список. Если серверу нужен реквизит, которого нет в колонках, передайте ссылку и прочитайте реквизит на сервере.

Несколько выделенных строк: массив идентификаторов

Чтобы пользователь мог выделить несколько строк , у элемента таблицы установите свойство РежимВыделения в значение Множественный. Дальше всё как с одной строкой, только вместо идентификатора — массив.

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

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

	УстановитьСкидкуНаСервере(Новый ФиксированныйМассив(Выделенные), 5);

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

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

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

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

Пример с числами: цена 1 200, количество 3, скидка 5 %. Сумма = 1 200 × 3 × 95 / 100 = 3 420.

Зачем ФиксированныйМассив: ВыделенныеСтроки — живое свойство элемента, его содержимое меняется, когда пользователь щёлкает по таблице. Копия фиксирует набор строк на момент нажатия кнопки.

Удаление выделенных строк

При удалении идите не по массиву выделенных, а по найденным строкам, и удаляйте через коллекцию:

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

Цикл безопасен: перебирается массив идентификаторов, а не сама таблица. Идентификаторы оставшихся строк после удаления не меняются.

Изменить строку через объект документа и вернуть её в форму

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

&НаСервере
Процедура ЗаполнитьСтрокуОбъектом(Идентификатор)

	СтрокаФормы = Объект.Товары.НайтиПоИдентификатору(Идентификатор);
	Индекс = Объект.Товары.Индекс(СтрокаФормы);

	ДокОбъект = РеквизитФормыВЗначение("Объект");
	СтрокаОбъекта = ДокОбъект.Товары[Индекс];
	ДокОбъект.ЗаполнитьСтроку(СтрокаОбъекта); // ваш экспортный метод модуля объекта

	ЗначениеВРеквизитФормы(ДокОбъект, "Объект");

	// идентификаторы после перезаполнения новые — восстанавливаем курсор
	Элементы.Товары.ТекущаяСтрока = Объект.Товары[Индекс].ПолучитьИдентификатор();

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

Три приёма, без которых этот код ломается:

  1. Строку объекта ищут по индексу, а не по идентификатору: у ТабличнаяЧасть объекта нет идентификаторов формы.
  2. После ЗначениеВРеквизитФормы коллекция формы заполняется заново, и старый идентификатор больше не находит строку.
  3. Курсор возвращают вручную, иначе пользователь окажется на первой строке.

Если в форме есть несохранённые изменения, они уходят в объект вместе с остальными данными — это нормально, но не вызывайте здесь запись документа без явного действия пользователя.

Ошибки при передаче строки на сервер и что с ними делать

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

Отдельная ловушка — обработчик ПриАктивизацииСтроки. Он срабатывает при каждом движении курсора, в том числе стрелками. Серверный вызов прямо в нём даёт заметные подвисания при листании таблицы.

Как передавать строки на сервер и не тормозить форму

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

Что реально ускоряет форму:

  • ⚡ Один вызов вместо цикла. Не вызывайте сервер для каждой строки в клиентском цикле. Соберите массив идентификаторов и обработайте всё за один заход.
  • 📉 БезКонтекста, где можно. &НаСервере передаёт данные формы туда и обратно. При таблице на 500–1000 строк разница с внеконтекстным вызовом видна без замеров.
  • ⏱ Отложенная реакция на курсор. В ПриАктивизацииСтроки не вызывайте сервер напрямую: подключите обработчик ожидания на 0,1–0,2 секунды через ПодключитьОбработчикОжидания("ОбновитьПодвал", 0.2, Истина). При быстром листании сработает только последний.
  • 🧮 Расчёты на клиенте. Сумму из цены и количества считают на клиенте, сервер для этого не нужен.
  • 🗂 Кэш неизменных данных. Если по номенклатуре нужен реквизит, который не меняется в рамках формы, держите его в реквизите формы или в соответствии на клиенте.

Как проверить, сколько вызовов идёт на сервер

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

Проверка кода перед сдачей
Выполнено: 0 / 5

Частые вопросы

Можно ли передать на сервер всю таблицу значений из формы?
Да. Реквизит формы типа ТаблицаЗначений на сервере превращают в обычную таблицу через РеквизитФормыВЗначение. Во внеконтекстную процедуру таблицу передают, предварительно выгрузив нужные колонки в массив структур.
Чем `ТекущаяСтрока` отличается от `НомерСтроки`?
ТекущаяСтрока — внутренний идентификатор строки в данных формы. НомерСтроки — видимый номер в табличной части документа. Номер меняется при сортировке и удалении, идентификатор — нет.
Как получить идентификатор строки, если у меня есть сама строка?
Вызовите у строки данных формы метод ПолучитьИдентификатор(). Он есть у ДанныеФормыЭлементКоллекции и у элементов дерева формы.
Работает ли этот подход с деревом формы?
Да. У ДанныеФормыДерево тоже есть НайтиПоИдентификатору, а ТекущаяСтрока элемента-дерева тоже возвращает идентификатор. Разница одна: строки дерева вложены, и родителя получают через ПолучитьРодителя().
Нужно ли передавать текущую строку, если сервер всё равно получает форму?
Сервер получает данные, но не знает, какую строку вы имели в виду, если не прочитать Элементы.Товары.ТекущаяСтрока. Явный параметр удобнее: одну и ту же серверную процедуру можно вызвать и для текущей строки, и для любой другой, например из цикла по выделенным.