Почему код с директивой &НаСервереБезКонтекста не видит форму и как это обойти
Содержание
Ошибка «на сервере без контекста» в 1С 8.3 означает одно: процедура с директивой &НаСервереБезКонтекста пытается обратиться к данным формы, которых на сервере у неё нет. Обычно это Переменная не определена (Объект), Переменная не определена (Элементы) или то же самое для ЭтаФорма. Исправляется двумя способами: передать нужные значения параметрами или сменить директиву на &НаСервере.
Это не сбой базы и не проблема лицензии. Это ошибка компиляции модуля формы, её ловит синтаксический контроль в Конфигураторе или платформа при открытии формы. Данные пользователей не страдают, править нужно код.
Как клиент и сервер 1С делят работу формы
Управляемая форма в 1С 8.3 живёт в двух местах одновременно. На клиенте — элементы, которые видит пользователь, и копия данных формы. На сервере — доступ к базе: запросы, объекты, регистры.
Каждый вызов серверной процедуры из клиентской — это сетевой запрос. В клиент-серверном варианте он идёт к рабочему процессу кластера, в тонком клиенте через интернет — к веб-серверу. Даже в файловой базе правила разделения кода действуют так же: компилятор проверяет директивы одинаково.
Что можно делать на каждой стороне:
- 🖥️ Клиент: работать с элементами формы, открывать формы, задавать вопросы пользователю (
ПоказатьВопрос,ПоказатьПредупреждение), работать с файлами на компьютере пользователя. - 🗄️ Сервер: выполнять
Запрос, получать и записывать объекты (ПолучитьОбъект(),Записать()), читать регистры, формировать отчёты. - 🔁 Обе стороны: простые вычисления, работа со строками, датами, структурами и массивами.
Отсюда классика жанра: Новый Запрос в процедуре &НаКлиенте даёт ошибку Тип не определен (Запрос). Запросов на клиенте нет вообще.
Что такое «контекст» и чего лишается процедура без него
Контекст формы — это всё, что относится к конкретному открытому экземпляру формы:
- 📄
Объект— основной реквизит (документ, элемент справочника); - 🧩 остальные реквизиты формы, включая табличные части и динамические списки;
- 🎛️
Элементы— поля, кнопки, группы; - 🪟
ЭтаФормаи методы формы, напримерРеквизитФормыВЗначение().
При вызове процедуры &НаСервере платформа упаковывает данные формы, отправляет их на сервер, создаёт там копию формы, выполняет код и возвращает изменённые данные обратно. Для документа с табличной частью на 500 строк это заметный объём.
Директива &НаСервереБезКонтекста говорит платформе: «форму не передавай». На сервер уходят только параметры процедуры, обратно — только возвращаемое значение. Вызов становится лёгким и быстрым. Цена — внутри такой процедуры нет ни Объект, ни Элементы, ни реквизитов формы. Для компилятора эти имена просто не существуют, отсюда сообщение «переменная не определена».
Слова «без контекста» в тексте ошибки сами по себе не встречаются. Пользователь видит «Переменная не определена» или «Процедура или функция с указанным именем не определена», а в окне сообщений Конфигуратора будет указан модуль формы и номер строки внутри процедуры с этой директивой.
Когда возникает ошибка: пять типовых ситуаций
- Обращение к реквизиту формы. В функции
&НаСервереБезКонтекстанаписаноОбъект.КонтрагентилиОбъект.Товары. Компилятор не находитОбъект. - Работа с элементами. Попытка скрыть поле:
Элементы.Склад.Видимость = Ложь. Элементы формы на сервере без контекста недоступны. - Вызов процедуры
&НаСервереиз процедуры без контекста. Процедура с формой не может быть вызвана оттуда, где формы нет: ей нечего передать. - Вызов клиентской процедуры с сервера. Серверный код не видит процедуры
&НаКлиенте, отсюдаПроцедура или функция с указанным именем не определена. То же сОткрытьФормуи диалогами. - Копирование кода между процедурами. Разработчик переносит фрагмент из процедуры
&НаСерверев новую функцию без контекста, чтобы ускорить форму, и забывает, что фрагмент читал реквизиты формы.
Отдельный случай — доработки после обновления. Если в расширении или в доработанной типовой форме переопределяли процедуру, а в новом релизе разработчики конфигурации сменили у неё директиву, перехват может перестать компилироваться. Тогда ошибка появляется сразу после обновления, хотя ваш код не менялся.
Директивы компиляции модуля формы: что каждая видит
Директива ставится перед Процедура или Функция и определяет, где выполняется код и что ему доступно.
| Директива | Где выполняется | Данные формы | Типичное применение |
|---|---|---|---|
&НаКлиенте | клиент | есть (клиентская копия) | обработчики команд и событий элементов, диалоги |
&НаСервере | сервер | есть, передаются с каждым вызовом | заполнение табличных частей, РеквизитФормыВЗначение |
&НаСервереБезКонтекста | сервер | нет, только параметры | запросы, получение значений из базы по ссылке |
&НаКлиентеНаСервереБезКонтекста | и там и там | нет | общие расчёты без обращения к базе |
Правила вызова между ними:
- ✅
&НаКлиентеможет вызвать любую из трёх остальных. - ✅
&НаСервереможет вызвать&НаСервереБезКонтекста. - ❌
&НаСервереБезКонтекстане может вызвать&НаСервереи&НаКлиенте. - ❌ Ни одна серверная процедура не вызывает клиентскую.
В общих модулях директив нет, их заменяют флажки в свойствах модуля: «Клиент (управляемое приложение)», «Сервер», «Вызов сервера». Функцию серверного общего модуля можно вызвать с клиента, только если у модуля включён «Вызов сервера». Без этого флажка клиентский код её не увидит, и ошибка будет похожей — «не определена».
Внутри процедуры с двумя сторонами выполнения разделяют ветки инструкциями препроцессора: #Если Сервер Тогда … #КонецЕсли.
Как исправить ошибку: четыре рабочих приёма
Выбор зависит от того, что процедуре нужно от формы. Сначала откройте модуль формы в Конфигураторе, найдите строку из сообщения об ошибке и посмотрите, к чему она обращается.
Как найти место ошибки
- Откройте базу в Конфигураторе и выполните
Конфигурация → Проверка конфигурации, отметив синтаксический контроль. - В окне сообщений дважды щёлкните по строке ошибки — Конфигуратор откроет модуль формы на нужной строке.
- Поднимитесь вверх до заголовка процедуры и посмотрите директиву над ним.
- Выпишите все имена, к которым обращается процедура: реквизиты формы, элементы, вызовы других процедур.
- Для каждого имени решите: это значение можно передать параметром или процедуре действительно нужна форма целиком.
Если пользователь прислал только снимок экрана с ошибкой, в тексте сообщения обычно есть имя модуля вида Документ.РеализацияТоваровУслуг.Форма.ФормаДокумента.Форма и номер строки. По нему место находится за минуту.
Приём 1. Передать значения параметрами
Самый частый и правильный вариант. Процедуре нужны два-три значения из формы — передайте их, а не всю форму.
Было (не компилируется):
&НаСервереБезКонтекста
Функция ЕдиницаТовара()
Возврат Объект.Номенклатура.ЕдиницаИзмерения;
КонецФункции
Стало:
&НаКлиенте
Процедура НоменклатураПриИзменении(Элемент)
Объект.ЕдиницаИзмерения = ЕдиницаТовара(Объект.Номенклатура);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЕдиницаТовара(Номенклатура)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.ЕдиницаИзмерения КАК ЕдиницаИзмерения
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.ЕдиницаИзмерения;
КонецЕсли;
Возврат Неопределено;
КонецФункции
Имена реквизитов здесь условные — подставьте свои из конфигурации. Принцип один: функция получает ссылку, возвращает значение, а запись в реквизит формы делает клиентская процедура.
Если значений много, соберите их в Структура на клиенте и передайте одним параметром. Структура, массив, ссылки, даты и строки передаются между клиентом и сервером без проблем.
Приём 2. Вернуть результат и применить его на клиенте
Когда серверу нужно «решить», показывать ли поле, пусть он вернёт признак, а свойство элемента меняет клиент:
&НаКлиенте
Процедура ДоговорПриИзменении(Элемент)
Элементы.Валюта.Видимость = ДоговорВалютный(Объект.Договор);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ДоговорВалютный(Договор)
Если Не ЗначениеЗаполнено(Договор) Тогда
Возврат Ложь;
КонецЕсли;
Возврат Договор.ВалютаВзаиморасчетов <> Константы.ВалютаРегламентированногоУчета.Получить();
КонецФункции
Обращение через точку к реквизиту ссылки на сервере работает, но читает объект целиком. Для одной-двух проверок это допустимо, в цикле лучше запрос.
Приём 3. Сменить директиву на &НаСервере
Если процедура реально работает с формой — заполняет табличную часть, вызывает РеквизитФормыВЗначение() и ЗначениеВРеквизитФормы(), меняет много реквизитов сразу, — ей нужен контекст. Поменяйте директиву:
&НаСервере
Процедура ЗаполнитьТоварыНаСервере()
Документ = РеквизитФормыВЗначение("Объект");
// заполнение табличной части Документ.Товары
ЗначениеВРеквизитФормы(Документ, "Объект");
КонецПроцедуры
После смены директивы проверьте, откуда процедура вызывается. Если её вызывала другая функция &НаСервереБезКонтекста, ту тоже придётся перевести на &НаСервере или переделать цепочку вызовов.
Смена директивы на &НаСервере — самый быстрый способ «убрать» ошибку, но не всегда лучший. Если процедура вызывается в обработчике изменения строки табличной части, каждый вызов будет гонять всю форму по сети, и на медленном канале ввод строк заметно затормозит.
Приём 4. Вынести логику в общий модуль
Если одна и та же функция нужна в нескольких формах, создайте общий модуль с флажками «Сервер» и «Вызов сервера» и перенесите её туда. Функция общего модуля по определению работает без контекста формы: всё нужное ей приходит параметрами. Заодно это убирает дублирование кода.
После любой правки запустите Конфигурация → Проверка конфигурации с включённым синтаксическим контролем: он покажет все оставшиеся обращения к недоступным именам сразу, а не по одному при открытии формы.
Как не допускать таких ошибок в разработке и сопровождении
Для разработчика:
- 📝 Начинайте новую серверную функцию с
&НаСервереБезКонтекста. Переходите на&НаСервере, только когда без формы не обойтись. - 🔍 Перед переносом кода между процедурами поищите в фрагменте слова
Объект.,Элементы.,ЭтаФормаи имена реквизитов формы. - 🧮 Считайте серверные вызовы. В параметрах запуска 1С:Предприятия из Конфигуратора можно включить показ количества вызовов сервера — счётчик сразу покажет, где обработчик ходит на сервер лишний раз.
- 📦 Объединяйте вызовы: одна функция, возвращающая структуру из пяти значений, лучше пяти функций.
- ✍️ Помечайте параметры серверных функций
Знач, если функция не должна менять их значения: так платформе не нужно возвращать параметр обратно на клиент.
Для администратора и тех, кто обновляет типовые базы:
- 🧪 Обновляйте конфигурацию с доработками сначала на копии базы и прогоняйте проверку конфигурации там.
- 🧷 Держите список расширений и доработанных форм. После обновления проверьте каждое расширение: если перехваченный метод в новом релизе сменил директиву, расширение нужно исправить разработчику.
- 💾 Перед обновлением рабочей базы делайте резервную копию: ошибка компиляции после обновления может закрыть пользователям доступ к форме документа до исправления кода.
Частые промахи при исправлении:
- ❌ Перевести всё на
&НаСервере«чтобы работало». Ошибка уйдёт, а форма станет медленной. - ❌ Передать в функцию без контекста весь
Объектпараметром. Данные формы так передавать бессмысленно: это та же передача контекста, только вручную. Передавайте ссылки и конкретные значения. - ❌ Читать реквизиты ссылки через точку в цикле по строкам табличной части. Каждое обращение — отдельное чтение из базы; один запрос по массиву ссылок работает в разы быстрее.