Что можно отправить с клиента на сервер в управляемой форме 1С и как это сделать

· 10 мин

Содержание

Передать объект на сервер в 1С можно только через параметры серверной процедуры или через реквизиты формы. Сам объект (СправочникОбъект, ДокументОбъект, ТаблицаЗначений) на клиенте не существует. На сервер уходит ссылка, примитивное значение, структура, массив или адрес во временном хранилище, а «тяжёлый» объект собирается уже там.

Типичная ситуация: программист пишет в обработчике кнопки &НаКлиенте строку ТЗ = Новый ТаблицаЗначений; и получает ошибку компиляции Тип не определен (ТаблицаЗначений). Или передаёт на сервер ссылку и пытается на клиенте прочитать Контрагент.ИНН. Обе проблемы — из-за непонимания, что и как ходит между клиентом и сервером. Разберём механику и рабочие приёмы для платформы 8.3 с управляемыми формами.

Как устроен обмен данными между клиентом и сервером

В управляемом приложении у кода два места выполнения. Клиент — тонкий клиент, веб-клиент или толстый клиент в управляемом режиме. Сервер — сервер 1С:Предприятия в клиент-серверном варианте или его эмуляция внутри клиента в файловом.

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

Каждый вызов серверной процедуры из клиентской — это:

  1. 📤 Сериализация параметров (и, если нужно, данных формы) на клиенте.
  2. 🌐 Передача по сети на сервер.
  3. ⚙️ Выполнение кода, обращения к базе.
  4. 📥 Сериализация результата и изменённых параметров, передача обратно.

Отсюда главное правило: передаются значения, а не живые объекты. На сервер уходит копия данных. Если на сервере вы изменили структуру, клиент увидит изменения только потому, что платформа вернула её обратно, а не потому, что это «тот же» объект.

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

Директивы компиляции: где выполняется код и что уходит на сервер

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

ДирективаГде выполняетсяДанные формы на сервереКогда применять
&НаКлиентеКлиент—Обработчики кнопок, работа с элементами формы, диалоги
&НаСервереСерверДа, передаются туда и обратноНужно читать или менять реквизиты формы, Объект, элементы
&НаСервереБезКонтекстаСерверНетПолучить данные по ссылке, выполнить запрос, проверить условие
&НаКлиентеНаСервереБезКонтекстаИ там, и тамНетОбщие расчёты на примитивных типах, которые нужны с обеих сторон

Для общих модулей роль директив играют флажки в свойствах модуля: «Клиент (управляемое приложение)», «Сервер», «Вызов сервера». Чтобы клиент мог вызвать функцию общего модуля, у модуля должны стоять флажки «Сервер» и «Вызов сервера». Без «Вызова сервера» серверная функция из клиентского кода просто не видна.

Чем &НаСервере дороже, чем &НаСервереБезКонтекста

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

&НаСервереБезКонтекста получает только параметры. Внутри нет доступа к Объект, Элементы, ЭтаФорма и реквизитам формы. Зато вызов лёгкий.

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

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

Здесь на сервер уходит одна ссылка, а возвращается одна строка. Форма целиком никуда не передаётся.

Зачем писать Знач у параметров

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

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

Какие типы можно передать на сервер, а какие нет

Общий критерий: тип должен быть доступен и на клиенте, и на сервере. Проверить это проще всего в синтакс-помощнике. На странице типа указано, в каких контекстах он доступен: тонкий клиент, веб-клиент, сервер.

Можно передатьНельзя передать
✅ Строка, Число, Дата, Булево, Неопределено, Null❌ СправочникОбъект, ДокументОбъект и другие «Объекты»
✅ Ссылки: СправочникСсылка, ДокументСсылка, ПеречислениеСсылка❌ ТаблицаЗначений, ДеревоЗначений
✅ Массив, Структура, Соответствие❌ Запрос, РезультатЗапроса, ВыборкаИзРезультатаЗапроса
✅ Фиксированные варианты: ФиксированныйМассив, ФиксированнаяСтруктура, ФиксированноеСоответствие❌ ОбъектМетаданных
✅ СписокЗначений, УникальныйИдентификатор, ДвоичныеДанные, ХранилищеЗначения, Тип, ОписаниеТипов❌ Наборы записей регистров, менеджеры (Справочники.Номенклатура)

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

Ссылка передаётся, но на клиенте у неё нельзя получить реквизиты через точку. Контрагент.ИНН в клиентском коде не скомпилируется. За значением реквизита придётся сходить на сервер.

Таблица значений: как доставить её на сервер и обратно

ТаблицаЗначений — серверный тип. На клиенте её нет, поэтому «передать таблицу значений на сервер» на практике означает одно из трёх.

Способ 1. Реквизит формы типа ТаблицаЗначений

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

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

    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    Номенклатура.Ссылка КАК Номенклатура,
    |    Номенклатура.Артикул КАК Артикул
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    НЕ Номенклатура.ЭтоГруппа";

    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        НоваяСтрока = ТЗ.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
    КонецЦикла;

    ЗначениеВРеквизитФормы(ТЗ, "ТаблицаРезультат");
КонецПроцедуры

Процедура &НаСервере, потому что ей нужен реквизит формы. Колонки реквизита должны совпадать с полями запроса по именам — тогда ЗаполнитьЗначенияСвойств перенесёт всё сам.

Способ 2. Массив структур

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

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

На сервере массив структур обратно в таблицу собирается циклом с ЗаполнитьЗначенияСвойств. Подходит для сотен строк. Для десятков тысяч — смотрите следующий способ.

Способ 3. Временное хранилище

Таблица кладётся во временное хранилище на сервере, а клиент получает только строку-адрес:

&НаСервере
Функция ПодготовитьДанные()
    ТЗ = ПолучитьБольшуюТаблицу(); // своя серверная функция
    Возврат ПоместитьВоВременноеХранилище(ТЗ, УникальныйИдентификатор);
КонецФункции

Дальше клиент передаёт этот адрес в другие серверные процедуры, и там таблица достаётся через ПолучитьИзВременногоХранилища(Адрес). Сама таблица по сети не путешествует.

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

Вызывать ПолучитьИзВременногоХранилища на клиенте для таблицы значений бесполезно: клиент не умеет работать с этим типом. На клиенте адрес — просто строка, которую передают дальше.

СпособКогдаОграничения
Реквизит формыТаблица показывается пользователюНужен контекст формы (&НаСервере), данные гоняются с формой
Массив структурНебольшая выборка для логики на клиентеНеудобно для больших объёмов, ручное преобразование
Временное хранилищеБольшая таблица между несколькими серверными вызовамиКлиент не видит содержимое, следите за временем жизни

Ссылки, объекты справочников и метаданные

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

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

Чтобы вызвать метод модуля объекта, получите объект на сервере:

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

Если нужен объект, которого нет в форме, передайте на сервер ссылку и вызовите Ссылка.ПолучитьОбъект() уже там.

Реквизиты по ссылке

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

Один вызов за несколькими реквизитами всегда лучше трёх вызовов по одному.

Метаданные

ОбъектМетаданных на клиент не передаётся, и глобального контекста Метаданные на клиенте нет. Рабочий приём — передавать полное имя строкой:

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

Полное имя ("Справочник.Номенклатура") получают на сервере через ОбъектМД.ПолноеИмя(). Если на руках только ссылка — передайте её, а на сервере вызовите Ссылка.Метаданные().

Ещё один вариант для типов — Тип и ОписаниеТипов: они доступны на клиенте, поэтому проверку вида ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты") можно делать без сервера.

Ошибки при передаче объектов и как их найти

Большинство проблем ловит компилятор, но часть проявляется только во время выполнения — при возврате результата на клиент.

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

Как отладить передачу

  • 🔍 Отладчик. Точки останова работают и в серверных процедурах. В клиент-серверном варианте для этого должна быть включена отладка на сервере 1С. В файловом режиме всё работает сразу.
  • 📊 Счётчик вызовов сервера. Запустите клиент с параметром командной строки /DisplayPerformance — в строке состояния появится число серверных вызовов и объём переданных данных. Видно, сколько вызовов делает одно нажатие кнопки.
  • ⏱ Замер производительности в конфигураторе показывает, сколько времени заняла каждая строка, включая серверные вызовы.

Как сократить число вызовов и трафик

Скорость формы зависит не столько от сервера, сколько от числа обращений к нему и объёма данных. Несколько приёмов, которые дают ощутимый эффект.

  1. Один вызов на одно действие пользователя. Если кнопка последовательно проверяет остаток, получает цену и пересчитывает сумму — соберите всё в одну серверную функцию, которая вернёт структуру.
  2. &НаСервереБезКонтекста по умолчанию. Директиву &НаСервере берите, только когда действительно нужен реквизит формы или элемент.
  3. Знач у входных параметров. Экономит обратный проход данных.
  4. Не храните в реквизитах формы лишнее. Скрытая табличная часть на тысячу строк передаётся при каждом &НаСервере.
  5. Фиксированные коллекции для констант. ФиксированнаяСтруктура в параметрах формы или в переменной модуля избавляет от повторных вызовов за одними и теми же настройками.
  6. Большие объёмы — через временное хранилище. Клиент держит адрес, сервер работает с данными.
  7. Длительные операции — в фоне. Если серверная процедура работает дольше нескольких секунд, запускайте её фоновым заданием, а результат забирайте через временное хранилище. В типовых конфигурациях на БСП для этого есть общий модуль ДлительныеОперации.

Хорошая проверка: откройте форму с /DisplayPerformance и нажмите основную кнопку. Если счётчик вызовов растёт больше чем на 1–2, есть что оптимизировать.

Проверка серверного вызова перед коммитом
Выполнено: 0 / 5

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

Можно ли передать на сервер саму форму?
Нет. Форма существует на клиенте. В процедуру &НаСервере передаются её данные, а не сама форма. Если серверный код должен заполнить форму, используйте директиву &НаСервере в модуле этой формы.
Почему код работает в файловой базе, а на сервере падает?
Частая причина — пути к файлам и права доступа: на сервере код выполняется от имени службы 1С и видит файловую систему сервера, а не компьютера пользователя. Для файлов используйте передачу через временное хранилище.
Передаются ли права пользователя на сервер?
Да. Серверный код выполняется с правами текущего пользователя. Если у него нет права чтения справочника, чтение по ссылке на сервере завершится ошибкой.
Можно ли передать на сервер файл с диска пользователя?
Не напрямую: сервер не видит диск клиента. Файл помещают во временное хранилище методом НачатьПомещениеФайла или аналогичным, а на сервер передают адрес и получают двоичные данные.
Сохраняются ли значения переменных модуля формы между вызовами сервера?
Серверные переменные модуля формы между вызовами не сохраняются. То, что нужно помнить, храните в реквизитах формы или во временном хранилище.