Клиент → сервер в управляемых формах 1С 8.3: параметры, реквизиты, хранилище

· 9 мин

Содержание

Чтобы передать переменную с клиента на сервер в 1С, объявите серверную процедуру с параметром и вызовите её из клиентской: РассчитатьНаСервере(Сумма). Платформа сама упакует значение, отправит его на сервер и, если параметр объявлен без Знач, вернёт изменённое значение обратно на клиент. Второй путь — записать значение в реквизит формы и сделать контекстный вызов &НаСервере.

Типичная ситуация: программист пишет обработку, в клиентской процедуре считает переменную Ставка, а в серверной получает «Переменная не определена». Клиентские и серверные переменные живут в разных процессах. Общей памяти у них нет, только то, что вы явно передали. Всё ниже относится к платформе 8.3 и управляемым формам, для любой конфигурации.

Почему клиентская переменная не видна на сервере

Код управляемой формы выполняется в двух местах. Клиентская часть работает на компьютере пользователя (тонкий клиент, веб-клиент), серверная — в рабочем процессе сервера 1С или, в файловом варианте, в том же процессе, но в отдельном контексте. Где выполнится процедура, решает директива компиляции над ней.

ДирективаГде выполняетсяЧто видит
&НаКлиентена клиентереквизиты формы, клиентские переменные модуля
&НаСерверена серверереквизиты формы (их передают вместе с вызовом)
&НаСервереБезКонтекстана серверетолько свои параметры и общие модули
&НаКлиентеНаСервереБезКонтекстатам, откуда вызвалитолько параметры

Из этого следуют три правила, на которых держится всё остальное:

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

Каждый вызов сервера — это обмен по сети. В клиент-серверной базе через VPN лишние вызовы видны пользователю как задержка. Собирайте данные в один вызов, а не дёргайте сервер в цикле.

Передача значения через параметры серверной процедуры

Самый прямой способ. Клиентская процедура вызывает серверную функцию и передаёт ей значения как обычные параметры.

&НаКлиенте
Процедура Рассчитать(Команда)
	Сумма = 1500;
	Ставка = 20;
	НДС = РассчитатьНаСервере(Сумма, Ставка);
	Сообщить(СтрШаблон("НДС: %1", Формат(НДС, "ЧДЦ=2")));
КонецПроцедуры

&НаСервереБезКонтекста
Функция РассчитатьНаСервере(Знач Сумма, Знач Ставка)
	Возврат Сумма * Ставка / 100;
КонецФункции

Результат: 1500 × 20 / 100 = 300. Здесь выбран &НаСервереБезКонтекста, потому что функции не нужны данные формы. Такой вызов легче: платформа не тащит на сервер реквизиты.

Знач или без Знач

Ключевое слово Знач меняет поведение при серверном вызове:

ОбъявлениеЧто уходит на серверЧто возвращается на клиент
Процедура П(Знач Х)значение Хничего, изменения Х теряются
Процедура П(Х)значение Хизменённое значение Х

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

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

&НаСервереБезКонтекста
Процедура ПроверитьНаСервере(Знач Контрагент, Ошибки)
	Если Не ЗначениеЗаполнено(Контрагент) Тогда
		Ошибки.Добавить("Не выбран контрагент");
	ИначеЕсли Не ЗначениеЗаполнено(Контрагент.ИНН) Тогда
		Ошибки.Добавить("У контрагента не заполнен ИНН");
	КонецЕсли;
КонецПроцедуры

Массив Ошибки вернулся на клиент уже заполненным. Если входной параметр менять не нужно, ставьте Знач: так меньше трафика и меньше шансов случайно испортить значение на клиенте.

Много параметров — одна структура

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

&НаКлиенте
Процедура Сформировать(Команда)
	ПараметрыОтбора = Новый Структура;
	ПараметрыОтбора.Вставить("Организация", Объект.Организация);
	ПараметрыОтбора.Вставить("Период", Объект.Дата);
	ПараметрыОтбора.Вставить("ТолькоПроведенные", Истина);
	Итог = ПолучитьИтогНаСервере(ПараметрыОтбора);
КонецПроцедуры

В модуле формы не называйте переменную Параметры. Это свойство самой формы, переменная с таким именем его перекроет, и код поведёт себя странно.

Обратно сервер тоже может вернуть структуру: Возврат Новый Структура("Сумма, Количество", Сумма, Количество);. Это обычный способ получить с сервера несколько значений одним вызовом.

Контекстный вызов: значение через реквизит формы

Если значение уже лежит в реквизите формы, параметр не нужен. Процедура с директивой &НаСервере получает данные формы вместе с вызовом и видит их напрямую.

&НаКлиенте
Процедура ЗаполнитьКомментарий(Команда)
	ЗаполнитьКомментарийНаСервере();
КонецПроцедуры

&НаСервере
Процедура ЗаполнитьКомментарийНаСервере()
	Объект.Комментарий = "Создан " + Формат(ТекущаяДатаСеанса(), "ДФ=dd.MM.yyyy");
КонецПроцедуры

Всё, что серверная процедура изменила в реквизитах, платформа после вызова отправит на клиент, и форма перерисуется.

Когда что выбирать:

  • ✅ Контекстный вызов — когда нужно читать или менять реквизиты формы, элементы, табличные части объекта.
  • ✅ Без контекста — когда серверу хватает пары значений: проверка, расчёт, чтение реквизита из справочника.
  • ❌ Контекстный вызов ради одного числа — лишняя нагрузка: вместе с ним уходят данные формы.

Учтите ограничение: из &НаСервереБезКонтекста нельзя вызвать процедуру &НаСервере той же формы. Цепочка работает только в одну сторону.

Временная переменная формы

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

Переменная модуля формы с &НаКлиенте тоже живёт до закрытия формы, но на сервер она сама не попадёт: её придётся передать параметром.

Глобальные значения: параметры сеанса и переменные приложения

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

Параметры сеанса — глобальное значение на сервере

Параметр сеанса создаётся в конфигураторе в ветке Общие → Параметры сеанса. Значение задаётся в модуле сеанса:

Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)
	Если ТребуемыеПараметры = Неопределено Тогда
		ПараметрыСеанса.ТекущийСклад = Справочники.Склады.ПустаяСсылка();
	КонецЕсли;
КонецПроцедуры

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

// Общий модуль РаботаСоСкладомВызовСервера
Процедура УстановитьТекущийСклад(Знач Склад) Экспорт
	УстановитьПривилегированныйРежим(Истина);
	ПараметрыСеанса.ТекущийСклад = Склад;
КонецПроцедуры

Функция ТекущийСклад() Экспорт
	Возврат ПараметрыСеанса.ТекущийСклад;
КонецФункции

Теперь клиент передаёт склад на сервер одной строкой: РаботаСоСкладомВызовСервера.УстановитьТекущийСклад(Объект.Склад). После этого любой серверный код в этом сеансе видит значение без всяких параметров.

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

Переменные модуля управляемого приложения — глобальное значение на клиенте

В модуле управляемого приложения объявите экспортную переменную:

Перем ВыбранныйСклад Экспорт;

Она доступна из любого клиентского модуля по имени, пока открыт сеанс. Но на сервере её нет. Чтобы серверный код её увидел, передайте её параметром или запишите в параметр сеанса, как выше.

СпособГде доступенСрок жизни
Параметр сеансасервердо завершения сеанса
Экспортная переменная модуля приложенияклиентдо закрытия клиента
Реквизит формыклиент и контекстный вызовпока открыта форма
Временное хранилищеклиент и сервер по адресупока открыта форма-владелец

Если серверная функция возвращает одно и то же весь сеанс (настройки, константы), поместите её в общий модуль с повторным использованием возвращаемых значений «На время сеанса». Клиент будет брать результат из кэша, а не ходить на сервер каждый раз.

Ссылки и объекты: что можно отправить на сервер

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

А вот на клиенте реквизиты ссылки через точку недоступны. Запись Объект.Контрагент.ИНН в клиентской процедуре вызовет ошибку. Нужно прочитать реквизит на сервере:

&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
	ИНН = ИННКонтрагента(Объект.Контрагент);
КонецПроцедуры

&НаСервереБезКонтекста
Функция ИННКонтрагента(Знач Контрагент)
	Возврат Контрагент.ИНН;
КонецФункции

В типовых конфигурациях на БСП для этого есть готовая функция ОбщегоНазначенияВызовСервера.ЗначениеРеквизитаОбъекта(Ссылка, "ИНН"), свою писать не обязательно.

Объект передать нельзя

ДокументОбъект, СправочникОбъект, НаборЗаписей на клиенте не существуют. На форме вместо них лежат данные формы (Объект — это ДанныеФормыСтруктура). Чтобы вызвать метод модуля объекта, преобразуйте данные формы в объект на сервере и обратно:

&НаСервере
Процедура ПересчитатьНаСервере()
	Документ = РеквизитФормыВЗначение("Объект");
	Документ.ПересчитатьСуммы(); // экспортный метод модуля объекта
	ЗначениеВРеквизитФормы(Документ, "Объект");
КонецПроцедуры

Обе функции, РеквизитФормыВЗначение и ЗначениеВРеквизитФормы, работают только в контекстном серверном вызове. Для произвольных данных формы есть пара ДанныеФормыВЗначение и ЗначениеВДанныеФормы.

Значения при открытии формы

Передать переменную в другую форму можно параметрами открытия:

ОткрытьФорму("Обработка.ПодборТоваров.Форма.Форма",
	Новый Структура("Склад", Объект.Склад), ЭтотОбъект);

В открываемой форме параметр Склад нужно добавить в список параметров формы. Значение придёт на сервер в ПриСозданииНаСервере и читается как Параметры.Склад.

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

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

С таблицами сложнее. ТаблицаЗначений и ДеревоЗначений на тонком клиенте недоступны, параметром на клиент их не вернуть.

Три рабочих варианта:

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

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

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

Передавайте только нужные колонки. Табличная часть из 2 000 строк с десятком колонок — заметный объём, а серверу для проверки остатков обычно нужны две.

Временное хранилище для больших данных

Сервер кладёт таблицу в хранилище и возвращает строку-адрес. Клиент хранит только адрес в реквизите формы и передаёт его в следующие вызовы.

&НаКлиенте
Процедура Подготовить(Команда)
	АдресТаблицы = ПодготовитьНаСервере(УникальныйИдентификатор);
КонецПроцедуры

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

&НаСервереБезКонтекста
Функция КоличествоПозиций(Знач Адрес)
	Таблица = ПолучитьИзВременногоХранилища(Адрес);
	Возврат Таблица.Количество();
КонецФункции

АдресТаблицы — реквизит формы типа «Строка». Уникальный идентификатор формы во втором параметре привязывает данные к форме: они удалятся, когда форма закроется. Без него данные могут исчезнуть раньше, чем вы их прочитаете.

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

Ошибки при передаче данных на сервер

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

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

Ещё одна частая ловушка — вызов серверной процедуры в цикле по строкам табличной части. На 500 строках это 500 обращений к серверу. Соберите строки в массив и отправьте одним вызовом.

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

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