Почему код с директивой &НаСервереБезКонтекста не видит форму и как это обойти

· 9 мин

Содержание

Ошибка «на сервере без контекста» в 1С 8.3 означает одно: процедура с директивой &НаСервереБезКонтекста пытается обратиться к данным формы, которых на сервере у неё нет. Обычно это Переменная не определена (Объект), Переменная не определена (Элементы) или то же самое для ЭтаФорма. Исправляется двумя способами: передать нужные значения параметрами или сменить директиву на &НаСервере.

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

Как клиент и сервер 1С делят работу формы

Управляемая форма в 1С 8.3 живёт в двух местах одновременно. На клиенте — элементы, которые видит пользователь, и копия данных формы. На сервере — доступ к базе: запросы, объекты, регистры.

Каждый вызов серверной процедуры из клиентской — это сетевой запрос. В клиент-серверном варианте он идёт к рабочему процессу кластера, в тонком клиенте через интернет — к веб-серверу. Даже в файловой базе правила разделения кода действуют так же: компилятор проверяет директивы одинаково.

Что можно делать на каждой стороне:

  • 🖥️ Клиент: работать с элементами формы, открывать формы, задавать вопросы пользователю (ПоказатьВопрос, ПоказатьПредупреждение), работать с файлами на компьютере пользователя.
  • 🗄️ Сервер: выполнять Запрос, получать и записывать объекты (ПолучитьОбъект(), Записать()), читать регистры, формировать отчёты.
  • 🔁 Обе стороны: простые вычисления, работа со строками, датами, структурами и массивами.

Отсюда классика жанра: Новый Запрос в процедуре &НаКлиенте даёт ошибку Тип не определен (Запрос). Запросов на клиенте нет вообще.

Что такое «контекст» и чего лишается процедура без него

Контекст формы — это всё, что относится к конкретному открытому экземпляру формы:

  • 📄 Объект — основной реквизит (документ, элемент справочника);
  • 🧩 остальные реквизиты формы, включая табличные части и динамические списки;
  • 🎛️ Элементы — поля, кнопки, группы;
  • 🪟 ЭтаФорма и методы формы, например РеквизитФормыВЗначение().

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

Директива &НаСервереБезКонтекста говорит платформе: «форму не передавай». На сервер уходят только параметры процедуры, обратно — только возвращаемое значение. Вызов становится лёгким и быстрым. Цена — внутри такой процедуры нет ни Объект, ни Элементы, ни реквизитов формы. Для компилятора эти имена просто не существуют, отсюда сообщение «переменная не определена».

Слова «без контекста» в тексте ошибки сами по себе не встречаются. Пользователь видит «Переменная не определена» или «Процедура или функция с указанным именем не определена», а в окне сообщений Конфигуратора будет указан модуль формы и номер строки внутри процедуры с этой директивой.

Когда возникает ошибка: пять типовых ситуаций

  1. Обращение к реквизиту формы. В функции &НаСервереБезКонтекста написано Объект.Контрагент или Объект.Товары. Компилятор не находит Объект.
  2. Работа с элементами. Попытка скрыть поле: Элементы.Склад.Видимость = Ложь. Элементы формы на сервере без контекста недоступны.
  3. Вызов процедуры &НаСервере из процедуры без контекста. Процедура с формой не может быть вызвана оттуда, где формы нет: ей нечего передать.
  4. Вызов клиентской процедуры с сервера. Серверный код не видит процедуры &НаКлиенте, отсюда Процедура или функция с указанным именем не определена. То же с ОткрытьФорму и диалогами.
  5. Копирование кода между процедурами. Разработчик переносит фрагмент из процедуры &НаСервере в новую функцию без контекста, чтобы ускорить форму, и забывает, что фрагмент читал реквизиты формы.

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

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

Директивы компиляции модуля формы: что каждая видит

Директива ставится перед Процедура или Функция и определяет, где выполняется код и что ему доступно.

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

Правила вызова между ними:

  • ✅ &НаКлиенте может вызвать любую из трёх остальных.
  • ✅ &НаСервере может вызвать &НаСервереБезКонтекста.
  • ❌ &НаСервереБезКонтекста не может вызвать &НаСервере и &НаКлиенте.
  • ❌ Ни одна серверная процедура не вызывает клиентскую.

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

Внутри процедуры с двумя сторонами выполнения разделяют ветки инструкциями препроцессора: #Если Сервер Тогда … #КонецЕсли.

Как исправить ошибку: четыре рабочих приёма

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

Как найти место ошибки

  1. Откройте базу в Конфигураторе и выполните Конфигурация → Проверка конфигурации, отметив синтаксический контроль.
  2. В окне сообщений дважды щёлкните по строке ошибки — Конфигуратор откроет модуль формы на нужной строке.
  3. Поднимитесь вверх до заголовка процедуры и посмотрите директиву над ним.
  4. Выпишите все имена, к которым обращается процедура: реквизиты формы, элементы, вызовы других процедур.
  5. Для каждого имени решите: это значение можно передать параметром или процедуре действительно нужна форма целиком.

Если пользователь прислал только снимок экрана с ошибкой, в тексте сообщения обычно есть имя модуля вида Документ.РеализацияТоваровУслуг.Форма.ФормаДокумента.Форма и номер строки. По нему место находится за минуту.

Приём 1. Передать значения параметрами

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

Было (не компилируется):

&НаСервереБезКонтекста
Функция ЕдиницаТовара()
    Возврат Объект.Номенклатура.ЕдиницаИзмерения;
КонецФункции

Стало:

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

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

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

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

Приём 2. Вернуть результат и применить его на клиенте

Когда серверу нужно «решить», показывать ли поле, пусть он вернёт признак, а свойство элемента меняет клиент:

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

&НаСервереБезКонтекста
Функция ДоговорВалютный(Договор)
    Если Не ЗначениеЗаполнено(Договор) Тогда
        Возврат Ложь;
    КонецЕсли;
    Возврат Договор.ВалютаВзаиморасчетов <> Константы.ВалютаРегламентированногоУчета.Получить();
КонецФункции

Обращение через точку к реквизиту ссылки на сервере работает, но читает объект целиком. Для одной-двух проверок это допустимо, в цикле лучше запрос.

Приём 3. Сменить директиву на &НаСервере

Если процедура реально работает с формой — заполняет табличную часть, вызывает РеквизитФормыВЗначение() и ЗначениеВРеквизитФормы(), меняет много реквизитов сразу, — ей нужен контекст. Поменяйте директиву:

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

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

Смена директивы на &НаСервере — самый быстрый способ «убрать» ошибку, но не всегда лучший. Если процедура вызывается в обработчике изменения строки табличной части, каждый вызов будет гонять всю форму по сети, и на медленном канале ввод строк заметно затормозит.

Приём 4. Вынести логику в общий модуль

Если одна и та же функция нужна в нескольких формах, создайте общий модуль с флажками «Сервер» и «Вызов сервера» и перенесите её туда. Функция общего модуля по определению работает без контекста формы: всё нужное ей приходит параметрами. Заодно это убирает дублирование кода.

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

Как не допускать таких ошибок в разработке и сопровождении

Для разработчика:

  • 📝 Начинайте новую серверную функцию с &НаСервереБезКонтекста. Переходите на &НаСервере, только когда без формы не обойтись.
  • 🔍 Перед переносом кода между процедурами поищите в фрагменте слова Объект., Элементы., ЭтаФорма и имена реквизитов формы.
  • 🧮 Считайте серверные вызовы. В параметрах запуска 1С:Предприятия из Конфигуратора можно включить показ количества вызовов сервера — счётчик сразу покажет, где обработчик ходит на сервер лишний раз.
  • 📦 Объединяйте вызовы: одна функция, возвращающая структуру из пяти значений, лучше пяти функций.
  • ✍️ Помечайте параметры серверных функций Знач, если функция не должна менять их значения: так платформе не нужно возвращать параметр обратно на клиент.

Для администратора и тех, кто обновляет типовые базы:

  • 🧪 Обновляйте конфигурацию с доработками сначала на копии базы и прогоняйте проверку конфигурации там.
  • 🧷 Держите список расширений и доработанных форм. После обновления проверьте каждое расширение: если перехваченный метод в новом релизе сменил директиву, расширение нужно исправить разработчику.
  • 💾 Перед обновлением рабочей базы делайте резервную копию: ошибка компиляции после обновления может закрыть пользователям доступ к форме документа до исправления кода.

Частые промахи при исправлении:

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