Клиент → сервер в управляемых формах 1С 8.3: параметры, реквизиты, хранилище
Содержание
Чтобы передать переменную с клиента на сервер в 1С, объявите серверную процедуру с параметром и вызовите её из клиентской: РассчитатьНаСервере(Сумма). Платформа сама упакует значение, отправит его на сервер и, если параметр объявлен без Знач, вернёт изменённое значение обратно на клиент. Второй путь — записать значение в реквизит формы и сделать контекстный вызов &НаСервере.
Типичная ситуация: программист пишет обработку, в клиентской процедуре считает переменную Ставка, а в серверной получает «Переменная не определена». Клиентские и серверные переменные живут в разных процессах. Общей памяти у них нет, только то, что вы явно передали. Всё ниже относится к платформе 8.3 и управляемым формам, для любой конфигурации.
Почему клиентская переменная не видна на сервере
Код управляемой формы выполняется в двух местах. Клиентская часть работает на компьютере пользователя (тонкий клиент, веб-клиент), серверная — в рабочем процессе сервера 1С или, в файловом варианте, в том же процессе, но в отдельном контексте. Где выполнится процедура, решает директива компиляции над ней.
| Директива | Где выполняется | Что видит |
|---|---|---|
&НаКлиенте | на клиенте | реквизиты формы, клиентские переменные модуля |
&НаСервере | на сервере | реквизиты формы (их передают вместе с вызовом) |
&НаСервереБезКонтекста | на сервере | только свои параметры и общие модули |
&НаКлиентеНаСервереБезКонтекста | там, откуда вызвали | только параметры |
Из этого следуют три правила, на которых держится всё остальное:
- 🔹 Переменная, объявленная в клиентской процедуре, существует только до её конца и только на клиенте.
- 🔹 На сервер попадают параметры вызова и, при контекстном вызове, данные формы. Больше ничего.
- 🔹 Переменные модуля формы с директивой
&НаСерверене хранят значение между вызовами: каждый серверный вызов начинается с чистого листа.
Каждый вызов сервера — это обмен по сети. В клиент-серверной базе через VPN лишние вызовы видны пользователю как задержка. Собирайте данные в один вызов, а не дёргайте сервер в цикле.
Передача значения через параметры серверной процедуры
Самый прямой способ. Клиентская процедура вызывает серверную функцию и передаёт ей значения как обычные параметры.
&НаКлиенте
Процедура Рассчитать(Команда)
Сумма = 1500;
Ставка = 20;
НДС = РассчитатьНаСервере(Сумма, Ставка);
Сообщить(СтрШаблон("НДС: %1", Формат(НДС, "ЧДЦ=2")));
КонецПроцедуры
&НаСервереБезКонтекста
Функция РассчитатьНаСервере(Знач Сумма, Знач Ставка)
Возврат Сумма * Ставка / 100;
КонецФункции
Результат: 1500 × 20 / 100 = 300. Здесь выбран &НаСервереБезКонтекста, потому что функции не нужны данные формы. Такой вызов легче: платформа не тащит на сервер реквизиты.
Знач или без Знач
Ключевое слово Знач меняет поведение при серверном вызове:
| Объявление | Что уходит на сервер | Что возвращается на клиент |
|---|---|---|
Процедура П(Знач Х) | значение Х | ничего, изменения Х теряются |
Процедура П(Х) | значение Х | изменённое значение Х |
Без Знач платформа гоняет параметр в обе стороны. Это удобно, когда серверная процедура должна вернуть несколько результатов:
&НаКлиенте
Процедура Проверить(Команда)
Ошибки = Новый Массив;
ПроверитьНаСервере(Объект.Контрагент, Ошибки);
Для Каждого Текст Из Ошибки Цикл
Сообщить(Текст);
КонецЦикла;
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ПроверитьНаСервере(Знач Контрагент, Ошибки)
Если Не ЗначениеЗаполнено(Контрагент) Тогда
Ошибки.Добавить("Не выбран контрагент");
ИначеЕсли Не ЗначениеЗаполнено(Контрагент.ИНН) Тогда
Ошибки.Добавить("У контрагента не заполнен ИНН");
КонецЕсли;
КонецПроцедуры
Массив Ошибки вернулся на клиент уже заполненным. Если входной параметр менять не нужно, ставьте Знач: так меньше трафика и меньше шансов случайно испортить значение на клиенте.
Много параметров — одна структура
Когда серверу нужно пять-семь значений, передавайте одну Структура. Сигнатура не разрастается, а добавить новый параметр можно без правки всех вызовов.
&НаКлиенте
Процедура Сформировать(Команда)
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("Организация", Объект.Организация);
ПараметрыОтбора.Вставить("Период", Объект.Дата);
ПараметрыОтбора.Вставить("ТолькоПроведенные", Истина);
Итог = ПолучитьИтогНаСервере(ПараметрыОтбора);
КонецПроцедуры
В модуле формы не называйте переменную Параметры. Это свойство самой формы, переменная с таким именем его перекроет, и код поведёт себя странно.
Обратно сервер тоже может вернуть структуру: Возврат Новый Структура("Сумма, Количество", Сумма, Количество);. Это обычный способ получить с сервера несколько значений одним вызовом.
Контекстный вызов: значение через реквизит формы
Если значение уже лежит в реквизите формы, параметр не нужен. Процедура с директивой &НаСервере получает данные формы вместе с вызовом и видит их напрямую.
&НаКлиенте
Процедура ЗаполнитьКомментарий(Команда)
ЗаполнитьКомментарийНаСервере();
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьКомментарийНаСервере()
Объект.Комментарий = "Создан " + Формат(ТекущаяДатаСеанса(), "ДФ=dd.MM.yyyy");
КонецПроцедуры
Всё, что серверная процедура изменила в реквизитах, платформа после вызова отправит на клиент, и форма перерисуется.
Когда что выбирать:
- ✅ Контекстный вызов — когда нужно читать или менять реквизиты формы, элементы, табличные части объекта.
- ✅ Без контекста — когда серверу хватает пары значений: проверка, расчёт, чтение реквизита из справочника.
- ❌ Контекстный вызов ради одного числа — лишняя нагрузка: вместе с ним уходят данные формы.
Учтите ограничение: из &НаСервереБезКонтекста нельзя вызвать процедуру &НаСервере той же формы. Цепочка работает только в одну сторону.
Временная переменная формы
Если значение нужно сохранять между вызовами, но показывать пользователю не надо, создайте реквизит формы без элемента на форме. Реквизиты формы живут, пока форма открыта, и доступны и на клиенте, и при контекстном серверном вызове.
Переменная модуля формы с &НаКлиенте тоже живёт до закрытия формы, но на сервер она сама не попадёт: её придётся передать параметром.
Глобальные значения: параметры сеанса и переменные приложения
Иногда значение нужно не одной форме, а всей сессии пользователя: выбранный склад, текущее подразделение, флаг режима работы.
Параметры сеанса — глобальное значение на сервере
Параметр сеанса создаётся в конфигураторе в ветке Общие → Параметры сеанса. Значение задаётся в модуле сеанса:
Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)
Если ТребуемыеПараметры = Неопределено Тогда
ПараметрыСеанса.ТекущийСклад = Справочники.Склады.ПустаяСсылка();
КонецЕсли;
КонецПроцедуры
ПараметрыСеанса доступны только на сервере. С клиента к ним обращаются через общий модуль с флажками «Сервер» и «Вызов сервера»:
// Общий модуль РаботаСоСкладомВызовСервера
Процедура УстановитьТекущийСклад(Знач Склад) Экспорт
УстановитьПривилегированныйРежим(Истина);
ПараметрыСеанса.ТекущийСклад = Склад;
КонецПроцедуры
Функция ТекущийСклад() Экспорт
Возврат ПараметрыСеанса.ТекущийСклад;
КонецФункции
Теперь клиент передаёт склад на сервер одной строкой: РаботаСоСкладомВызовСервера.УстановитьТекущийСклад(Объект.Склад). После этого любой серверный код в этом сеансе видит значение без всяких параметров.
Привилегированный режим включайте только внутри модуля и только для записи параметра. Не передавайте через такой метод данные, которые пользователь не должен менять.
Переменные модуля управляемого приложения — глобальное значение на клиенте
В модуле управляемого приложения объявите экспортную переменную:
Перем ВыбранныйСклад Экспорт;
Она доступна из любого клиентского модуля по имени, пока открыт сеанс. Но на сервере её нет. Чтобы серверный код её увидел, передайте её параметром или запишите в параметр сеанса, как выше.
| Способ | Где доступен | Срок жизни |
|---|---|---|
| Параметр сеанса | сервер | до завершения сеанса |
| Экспортная переменная модуля приложения | клиент | до закрытия клиента |
| Реквизит формы | клиент и контекстный вызов | пока открыта форма |
| Временное хранилище | клиент и сервер по адресу | пока открыта форма-владелец |
Если серверная функция возвращает одно и то же весь сеанс (настройки, константы), поместите её в общий модуль с повторным использованием возвращаемых значений «На время сеанса». Клиент будет брать результат из кэша, а не ходить на сервер каждый раз.
Ссылки и объекты: что можно отправить на сервер
Ссылку передавать можно и нужно. СправочникСсылка, ДокументСсылка, перечисления — лёгкие значения: по сети идёт только идентификатор. На сервере по ссылке читаются любые реквизиты.
А вот на клиенте реквизиты ссылки через точку недоступны. Запись Объект.Контрагент.ИНН в клиентской процедуре вызовет ошибку. Нужно прочитать реквизит на сервере:
&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
ИНН = ИННКонтрагента(Объект.Контрагент);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ИННКонтрагента(Знач Контрагент)
Возврат Контрагент.ИНН;
КонецФункции
В типовых конфигурациях на БСП для этого есть готовая функция ОбщегоНазначенияВызовСервера.ЗначениеРеквизитаОбъекта(Ссылка, "ИНН"), свою писать не обязательно.
Объект передать нельзя
ДокументОбъект, СправочникОбъект, НаборЗаписей на клиенте не существуют. На форме вместо них лежат данные формы (Объект — это ДанныеФормыСтруктура). Чтобы вызвать метод модуля объекта, преобразуйте данные формы в объект на сервере и обратно:
&НаСервере
Процедура ПересчитатьНаСервере()
Документ = РеквизитФормыВЗначение("Объект");
Документ.ПересчитатьСуммы(); // экспортный метод модуля объекта
ЗначениеВРеквизитФормы(Документ, "Объект");
КонецПроцедуры
Обе функции, РеквизитФормыВЗначение и ЗначениеВРеквизитФормы, работают только в контекстном серверном вызове. Для произвольных данных формы есть пара ДанныеФормыВЗначение и ЗначениеВДанныеФормы.
Значения при открытии формы
Передать переменную в другую форму можно параметрами открытия:
ОткрытьФорму("Обработка.ПодборТоваров.Форма.Форма",
Новый Структура("Склад", Объект.Склад), ЭтотОбъект);
В открываемой форме параметр Склад нужно добавить в список параметров формы. Значение придёт на сервер в ПриСозданииНаСервере и читается как Параметры.Склад.
Табличная часть, массивы и таблицы значений между клиентом и сервером
Простые коллекции — Массив, Структура, Соответствие и их фиксированные версии — передаются параметрами как есть, если внутри лежат значения допустимых типов.
С таблицами сложнее. ТаблицаЗначений и ДеревоЗначений на тонком клиенте недоступны, параметром на клиент их не вернуть.
Три рабочих варианта:
| Задача | Способ |
|---|---|
| Обработать табличную часть документа на форме | контекстный вызов: Объект.Товары.Выгрузить() на сервере, обработка, Объект.Товары.Загрузить(Таблица) |
| Отправить строки табличной части в функцию без контекста | собрать на клиенте массив структур |
| Подготовить большую таблицу и использовать её в нескольких вызовах | временное хранилище |
Табличная часть как массив структур
&НаКлиенте
Функция ТоварыВМассив()
Результат = Новый Массив;
Для Каждого Строка Из Объект.Товары Цикл
Результат.Добавить(Новый Структура("Номенклатура, Количество, Сумма",
Строка.Номенклатура, Строка.Количество, Строка.Сумма));
КонецЦикла;
Возврат Результат;
КонецФункции
Передавайте только нужные колонки. Табличная часть из 2 000 строк с десятком колонок — заметный объём, а серверу для проверки остатков обычно нужны две.
Временное хранилище для больших данных
Сервер кладёт таблицу в хранилище и возвращает строку-адрес. Клиент хранит только адрес в реквизите формы и передаёт его в следующие вызовы.
&НаКлиенте
Процедура Подготовить(Команда)
АдресТаблицы = ПодготовитьНаСервере(УникальныйИдентификатор);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПодготовитьНаСервере(Знач ИдентификаторФормы)
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления");
Таблица = Запрос.Выполнить().Выгрузить();
Возврат ПоместитьВоВременноеХранилище(Таблица, ИдентификаторФормы);
КонецФункции
&НаСервереБезКонтекста
Функция КоличествоПозиций(Знач Адрес)
Таблица = ПолучитьИзВременногоХранилища(Адрес);
Возврат Таблица.Количество();
КонецФункции
АдресТаблицы — реквизит формы типа «Строка». Уникальный идентификатор формы во втором параметре привязывает данные к форме: они удалятся, когда форма закроется. Без него данные могут исчезнуть раньше, чем вы их прочитаете.
Тот же приём работает для файлов: двоичные данные, загруженные с клиента, кладут во временное хранилище и на сервер передают только адрес.
Ошибки при передаче данных на сервер
Большинство проблем сводится к трём вещам: значение не передано, передан недопустимый тип, данные формы не вернулись.
Ещё одна частая ловушка — вызов серверной процедуры в цикле по строкам табличной части. На 500 строках это 500 обращений к серверу. Соберите строки в массив и отправьте одним вызовом.
Частые вопросы
Можно ли передать на сервер саму форму или ЭтотОбъект?
&НаСервере и так даёт серверу доступ к её реквизитам.Чем общий модуль «Вызов сервера» отличается от серверной процедуры формы?
Знач работают так же, как у &НаСервереБезКонтекста. Общий модуль удобен, когда функцию вызывают из разных форм.Передаётся ли значение Неопределено и пустая ссылка?
ЗначениеЗаполнено().