При разработке на платформе 1С:Предприятие 8.3 многие программисты сталкиваются с ситуацией, когда код, помеченный директивой &НаСервере, вызывает ошибку «Процедура или функция с указанным именем не определена» в Модуле управляемого приложения. Давайте разберемся, почему это происходит и как правильно организовать выполнение серверного кода.
Начнем с того, что Модуль управляемого приложения (в конфигураторе часто называемый "Модуль приложения") является специфическим элементом конфигурации, предназначенным для обработки событий, связанных с началом и окончанием сеанса работы пользователя. Важно понимать, что этот модуль всегда исполняется на клиентской стороне. При необходимости можно воспользоваться инструментом для изучения последовательности вызова различных событий платформы.
Это ключевая информация, которая объясняет причину возникновения ошибки. Поскольку Модуль управляемого приложения работает исключительно на клиенте, в его контексте не могут быть определены и выполнены серверные процедуры или функции, помеченные директивой &НаСервере. Когда вы пытаетесь вызвать такую процедуру изнутри этого модуля, система не находит ее в клиентском контексте, что и приводит к ошибке «Процедура или функция с указанным именем не определена».
Иногда название «Модуль приложения» может вводить в заблуждение, намекая на универсальное исполнение (и на клиенте, и на сервере) или даже преимущественно на сервере. Однако, для управляемого приложения, это строго клиентский модуль. Правильнее было бы называть его «Клиентский модуль приложения» для большей очевидности.
Для эффективной работы с платформой 1С:Предприятие критически важно понимать концепцию клиент-серверной архитектуры и контекстов выполнения кода. Даже в файловом режиме платформа эмулирует сервер в рамках одного процесса с запущенной программой. Это означает, что каждая строка кода выполняется либо на клиентской стороне (на компьютере пользователя), либо на серверной стороне (на сервере 1С:Предприятие). Дополнительно для изучения этой темы можно использовать материал, детально разбирающий концепцию разделения кода на клиентскую и серверную части.
Для управления тем, где будет выполняться код, мы используем директивы компиляции, которые обозначаются символом «&».
&НаКлиенте: Процедура или функция выполняется на стороне клиента. Здесь доступен интерфейс приложения, элементы формы (и их визуальный редактор), но нет прямого доступа к базе данных.&НаСервере: Процедура или функция выполняется на стороне сервера. В этом контексте есть доступ к базе данных и к контексту формы. При вызове такой процедуры с клиента весь контекст формы передается на сервер, что может увеличить трафик. Если директива не указана, по умолчанию используется &НаСервере.&НаСервереБезКонтекста: Процедура или функция выполняется на стороне сервера, но без доступа к контексту формы. Это помогает минимизировать сетевой трафик и нагрузку, так как серверная процедура не имеет информации о форме и ее данных. Мы используем эту директиву, когда не требуется программное изменение форм через её контекст.&НаКлиентеНаСервереБезКонтекста: Процедура или функция может выполняться как на клиенте, так и на сервере, но без доступа к контексту формы. Код в такой процедуре должен быть универсальным и корректно работать в обоих контекстах.&НаКлиентеНаСервере: Эта директива также существует и позволяет процедуре или функции выполняться как на клиенте, так и на сервере, обычно используется в модулях команд.Мы должны помнить, что из клиентских процедур можно вызывать серверные процедуры (с &НаСервере или &НаСервереБезКонтекста), но из серверных процедур нельзя вызвать клиентские. Важный принцип: прямой доступ к базе данных есть только на серверной части. Клиентская часть не может напрямую обращаться к базе данных. Поэтому для работы с данными в базе из клиентских процедур необходимо вызывать серверные процедуры или функции.
Итак, если нам необходимо выполнить серверные операции, которые инициируются из Модуля управляемого приложения (или любого другого клиентского модуля), мы должны вынести этот код в общий модуль.
Рассмотрим по шагам, как это сделать:
Название модуля часто содержит постфикс "ВызовСервера", чтобы сразу было понятно его назначение (например, "РаботаСФайламиВызовСервера").
&НаСервере или &НаСервереБезКонтекста и быть экспортируемой (т.е. иметь ключевое слово Экспорт).Посмотрим на пример:
Допустим, в Модуле управляемого приложения у вас был такой код, который вызывал ошибку:
// Модуль управляемого приложения
Процедура ПриНачалеРаботыСистемы()
МояСервернаяПроцедура(); // Ошибка!
КонецПроцедуры
&НаСервере
Процедура МояСервернаяПроцедура()
// Здесь код, который должен работать на сервере,
// например, обращение к данным базы
Константы.ПоследнийЗапуск.Установить(ТекущаяДата());
КонецПроцедуры
Правильное решение будет следующим:
// Общий модуль: МоиСерверныеФункции (флажки: Сервер, Вызов сервера)
&НаСервере
Процедура МояСервернаяПроцедураЭкспорт() Экспорт
// Здесь код, который должен работать на сервере,
// например, обращение к данным базы
Константы.ПоследнийЗапуск.Установить(ТекущаяДата());
КонецПроцедуры
// Модуль управляемого приложения
Процедура ПриНачалеРаботыСистемы()
МоиСерверныеФункции.МояСервернаяПроцедураЭкспорт(); // Теперь все работает!
КонецПроцедуры
Размещение большого количества кода в общих модулях, доступных для вызова с клиента, увеличивает время компиляции и запуска приложения. Поэтому старайтесь группировать функции по смыслу и не делать такие модули слишком объемными.
Для лучшего понимания разницы между модулями, рассмотрим Модуль команды. Это еще один тип модуля, который может вводить в заблуждение, но работает по другим правилам.
Модуль команды предназначен для описания действий, которые должна выполнить система при вызове команды. В отличие от Модуля управляемого приложения, Модуль команды поддерживает выполнение как на клиенте, так и на сервере. В нем можно использовать директивы &НаКлиенте, &НаСервере и даже &НаКлиентеНаСервере. Предопределенная процедура-обработчик ОбработатьКоманду() в модуле команды должна предваряться директивой &НаКлиенте, поскольку выполнение команды всегда начинается в клиентском приложении.
Пример кода в Модуле команды:
// Модуль команды "ВыполнитьДействие"
&НаКлиенте
Процедура ОбработатьКоманду()
Сообщить("Выполняется на клиенте!");
// Вызываем серверную часть команды
СерверноеДействие();
КонецПроцедуры
&НаСервере
Процедура СерверноеДействие()
Сообщить("Выполняется на сервере!");
// Здесь можно работать с базой данных
КонецПроцедуры
Этот пример показывает, как Модуль команды может включать в себя процедуры для обеих сторон, в то время как Модуль управляемого приложения строго клиентский.
В контексте клиентских процедур, давайте рассмотрим практический пример установки заголовка клиентского приложения. Это действие, очевидно, должно выполняться на клиенте.
В более старых версиях 1С:Предприятия использовался метод УстановитьЗаголовокКлиентскогоПриложения или УстановитьЗаголовокПриложения. Однако, начиная с версии 8.3.10+, эти процедуры считаются устаревшими и оставлены только для совместимости. Вместо них рекомендуется использовать метод КлиентскоеПриложение.УстановитьЗаголовок.
Пример использования в Модуле управляемого приложения (или любом другом клиентском модуле):
// Модуль управляемого приложения
Процедура ПриНачалеРаботыСистемы()
// Устанавливаем заголовок клиентского приложения
КлиентскоеПриложение.УстановитьЗаголовок("Мое Приложение - Рабочий Режим");
// Если нужна серверная логика, вызываем ее из общего модуля:
МоиСерверныеФункции.МояСервернаяПроцедураЭкспорт();
КонецПроцедуры
Этот пример демонстрирует типичную клиентскую операцию, которая выполняется непосредственно в клиентском контексте.
Мы часто слышим о важности чтения документации (RTFM - Read The F***ing Manual). И действительно, документация 1С содержит исчерпывающую информацию по каждому модулю и директиве. Однако, для новичка бывает сложно понять, что именно искать и как формулировать поисковый запрос, когда возникает абстрактная ошибка. Например, искать объяснение ошибки «Процедура или функция с указанным именем не определена» нужно не в описании самой ошибки, а в особенностях работы конкретного модуля, в котором она возникла.
Для новичков, столкнувшихся с подобными проблемами, мы рекомендуем следующий подход:
&НаСервере, попробуйте поисковые запросы типа: "Модуль управляемого приложения &НаСервере ошибка", "контексты выполнения 1С", "где выполняется Модуль приложения 1С". Также можно скопировать и вставить текст ошибки в поисковик.Для уверенного понимания, где какой код работает, необходимо иметь четкое представление о том, какие понятия вложены автором системы в те или иные элементы. Для модулей, которые "живут на одной стороне" (например, Модуль приложения или Модуль сеанса, которые всегда клиентские или всегда серверные соответственно), директивы &НаКлиенте или &НаСервере не требуются и их использование может приводить к ошибкам или игнорированию, так как контекст исполнения заранее определен.
Таким образом, понимание архитектуры платформы 1С:Предприятие, назначения различных модулей и правильное использование директив компиляции &НаКлиенте, &НаСервере и &НаСервереБезКонтекста является фундаментальным для написания корректного и эффективного кода. Всегда помните, что Модуль управляемого приложения является строго клиентским, и весь серверный код должен быть вынесен в общие модули с соответствующими флагами.